#include "bar/config.h" #include "bar/modules.h" #include "bar/util.h" #include #include #include typedef struct { redraw_fn_t redraw; void *redraw_data; const char *compositor_name; char *class; char *title; char workspace[16]; bool has_window; } window_state_t; static const char *remap_class(const char *cls) { if (!cls) return "unknown"; if (!strcmp(cls, "footclient") || !strcmp(cls, "foot")) return "terminal"; if (strchr(cls, ' ') != nullptr) return cls; const char *last_dot = strrchr(cls, '.'); return (last_dot != nullptr) ? (last_dot + 1) : cls; } static void clean_discord(const char *in, char *out, size_t max) { const char *p = in; if (*p == '(') { while (*p && *p != ')') p++; if (*p) p++; while (*p == ' ') p++; } constexpr char sep[] = "Discord | "; if (!strncmp(p, sep, sizeof sep - 1)) p += sizeof sep - 1; strncpy(out, p, max - 1); out[max - 1] = '\0'; } static void rebuild_ws_label(char *buffer, size_t buffer_size, const workspace_info_t *workspaces, size_t count) { if (!buffer || buffer_size == 0) return; for (size_t i = 0; i < count; i++) { if (workspaces[i].flags & WS_FOCUSED) { if (workspaces[i].name && workspaces[i].name[0] != '\0') snprintf(buffer, buffer_size, "Workspace %s", workspaces[i].name); else snprintf(buffer, buffer_size, "Workspace %d", workspaces[i].index); return; } } snprintf(buffer, buffer_size, "Workspace ?"); } static void on_focus(const focus_info_t *info, void *data) { module_t *mod = data; window_state_t *w = mod->internal; w->has_window = info->has_window; if (!w->has_window) { assign_str(&w->class, ""); assign_str(&w->title, ""); w->redraw(w->redraw_data); return; } char tmp_class[128]; const char *cls = remap_class(info->class); strncpy(tmp_class, cls, sizeof tmp_class - 1); tmp_class[sizeof tmp_class - 1] = '\0'; for (int i = 0; tmp_class[i]; i++) { tmp_class[i] = tolower((unsigned char)tmp_class[i]); } char clean_class[128]; sanitise_and_truncate(clean_class, tmp_class, sizeof clean_class); assign_str(&w->class, clean_class); char tmp_title[128]; bool discord = !strcmp(w->class, "discord") || !strcmp(w->class, "vesktop"); if (discord) { clean_discord(info->title ? info->title : "", tmp_title, sizeof tmp_title); } else { strncpy(tmp_title, info->title ? info->title : "", sizeof tmp_title - 1); tmp_title[sizeof tmp_title - 1] = '\0'; } char clean_title[128]; sanitise_and_truncate(clean_title, tmp_title, g_config.max_title_length); assign_str(&w->title, clean_title); w->redraw(w->redraw_data); } static void on_workspaces(const workspace_info_t *wss, size_t count, void *data) { module_t *mod = data; window_state_t *w = mod->internal; size_t n = count < MAX_WORKSPACES ? count : MAX_WORKSPACES; rebuild_ws_label(w->workspace, sizeof w->workspace, wss, n); if (!w->has_window) w->redraw(w->redraw_data); } static size_t window_render(module_t *mod, segment_t *out) { window_state_t *w = mod->internal; *out = (segment_t){.is_multi_line = true, .top_fg = CLR_MUTED, .bottom_fg = CLR_FG, .bg = CLR_BG}; if (w->has_window) { out->top = w->class; out->bottom = w->title; } else { out->top = w->compositor_name; out->bottom = w->workspace; } return 1; } static void window_destroy(module_t *mod) { window_state_t *w = mod->internal; if (w->class) free(w->class); if (w->title) free(w->title); free(w); free(mod); } module_t *window_module_new(protocol_t *proto, redraw_fn_t fn, void *data) { if (!proto) { warn("no protocol provided for window module"); return nullptr; } module_t *mod = malloc(sizeof *mod); window_state_t *w = calloc(1, sizeof *w); if (!mod || !w) { if (mod) free(mod); if (w) free(w); return nullptr; } w->redraw = fn; w->redraw_data = data; w->compositor_name = proto ? proto->name : "wayland"; strncpy(w->workspace, "Workspace ?", sizeof w->workspace - 1); *mod = (module_t){ .render = window_render, .destroy = window_destroy, .internal = w}; if (proto->on_focus_changed) proto->on_focus_changed(proto, on_focus, mod); if (proto->on_workspaces_changed) proto->on_workspaces_changed(proto, on_workspaces, mod); return mod; }