diff --git a/appicons.patch b/appicons.patch new file mode 100644 index 0000000..95a0c85 --- /dev/null +++ b/appicons.patch @@ -0,0 +1,275 @@ +From f0c747be0deca64801fe01eebfa78ce5b3d803fe Mon Sep 17 00:00:00 2001 +From: Rumen +Date: Fri, 24 Oct 2025 17:14:02 +0200 +Subject: [PATCH] fix: appicons displaying on all monitors + +--- + config.def.h | 14 +++-- + dwl.c | 142 +++++++++++++++++++++++++++++++++++++++++++++++++-- + 2 files changed, 149 insertions(+), 7 deletions(-) + +diff --git a/config.def.h b/config.def.h +index 1b7472d..a48b78d 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -26,12 +26,20 @@ static char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; + /* logging */ + static int log_level = WLR_ERROR; + ++/* appicons */ ++/* NOTE: set to 0 to set to default (whitespace) */ ++static char outer_separator_beg = '['; ++static char outer_separator_end = ']'; ++static char inner_separator = ' '; ++static unsigned truncate_icons_after = 2; /* will default to 1, that is the min */ ++static char truncate_symbol[] = "..."; ++ + /* NOTE: ALWAYS keep a rule declared even if you don't use rules (e.g leave at least one example) */ + static const Rule rules[] = { +- /* app_id title tags mask isfloating monitor */ ++ /* app_id title tags mask isfloating monitor appicon*/ + /* examples: */ +- { "Gimp_EXAMPLE", NULL, 0, 1, -1 }, /* Start on currently visible tags floating, not tiled */ +- { "firefox_EXAMPLE", NULL, 1 << 8, 0, -1 }, /* Start on ONLY tag "9" */ ++ { "Gimp_EXAMPLE", NULL, 0, 1, -1, NULL }, /* Start on currently visible tags floating, not tiled */ ++ { "firefox_EXAMPLE", NULL, 1 << 8, 0, -1, "" }, /* Start on ONLY tag "9" */ + }; + + /* layout(s) */ +diff --git a/dwl.c b/dwl.c +index bf340d8..ba6388a 100644 +--- a/dwl.c ++++ b/dwl.c +@@ -143,6 +143,7 @@ typedef struct { + struct wl_listener set_hints; + #endif + unsigned int bw; ++ char *appicon; + uint32_t tags; + int isfloating, isurgent, isfullscreen; + uint32_t resize; /* configure serial of a pending resize */ +@@ -221,6 +222,7 @@ struct Monitor { + unsigned int seltags; + unsigned int sellt; + uint32_t tagset[2]; ++ char **tag_icons; + float mfact; + int gamma_lut_changed; + int nmaster; +@@ -252,6 +254,7 @@ typedef struct { + uint32_t tags; + int isfloating; + int monitor; ++ const char *appicon; + } Rule; + + typedef struct { +@@ -313,6 +316,9 @@ static void destroypointerconstraint(struct wl_listener *listener, void *data); + static void destroysessionlock(struct wl_listener *listener, void *data); + static void destroykeyboardgroup(struct wl_listener *listener, void *data); + static Monitor *dirtomon(enum wlr_direction dir); ++static void remove_outer_separators(char **str); ++static void appiconsappend(char **str, const char *appicon, size_t new_size); ++static void applyappicon(char *tag_icons[], int *icons_per_tag, const Client *c); + static void drawbar(Monitor *m); + static void drawbars(void); + static void focusclient(Client *c, int lift); +@@ -520,6 +526,11 @@ applybounds(Client *c, struct wlr_box *bbox) + void + applyrules(Client *c) + { ++ outer_separator_beg = outer_separator_beg ? outer_separator_beg : ' '; ++ outer_separator_end = outer_separator_end ? outer_separator_end : ' '; ++ inner_separator = inner_separator ? inner_separator : ' '; ++ truncate_icons_after = truncate_icons_after > 0 ? truncate_icons_after : 1; ++ + /* rule matching */ + const char *appid, *title; + uint32_t newtags = 0; +@@ -533,6 +544,8 @@ applyrules(Client *c) + for (r = rules; r < END(rules); r++) { + if ((!r->title || strstr(title, r->title)) + && (!r->id || strstr(appid, r->id))) { ++ /* r->appicon is static, so lifetime is sufficient */ ++ c->appicon = (char*) r->appicon; + c->isfloating = r->isfloating; + newtags |= r->tags; + i = 0; +@@ -775,7 +788,7 @@ buttonpress(struct wl_listener *listener, void *data) + (buffer = wlr_scene_buffer_from_node(node)) && buffer == selmon->scene_buffer) { + cx = (cursor->x - selmon->m.x) * selmon->wlr_output->scale; + do +- x += TEXTW(selmon, tags[i]); ++ x += TEXTW(selmon, selmon->tag_icons[i]); + while (cx >= x && ++i < LENGTH(tags)); + if (i < LENGTH(tags)) { + click = ClkTagBar; +@@ -905,6 +918,16 @@ cleanupmon(struct wl_listener *listener, void *data) + wlr_output_layout_remove(output_layout, m->wlr_output); + wlr_scene_output_destroy(m->scene_output); + ++ for (int i = 0; i < LENGTH(tags); i++) { ++ if (m->tag_icons[i]) free(m->tag_icons[i]); ++ m->tag_icons[i] = NULL; ++ } ++ ++ if (m->tag_icons) { ++ free(m->tag_icons); ++ m->tag_icons = NULL; ++ } ++ + closemon(m); + wlr_scene_node_destroy(&m->fullscreen_bg->node); + wlr_scene_node_destroy(&m->scene_buffer->node); +@@ -1227,6 +1250,13 @@ createmon(struct wl_listener *listener, void *data) + m->lt[0] = r->lt; + m->lt[1] = &layouts[LENGTH(layouts) > 1 && r->lt != &layouts[1]]; + strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof(m->ltsymbol)); ++ ++ m->tag_icons = (char**) malloc(LENGTH(tags) * sizeof(char*)); ++ if (m->tag_icons == NULL) perror("dwm: malloc()"); ++ for (int i = 0; i < LENGTH(tags); i++) { ++ m->tag_icons[i] = NULL; ++ } ++ + wlr_output_state_set_scale(&state, r->scale); + wlr_output_state_set_transform(&state, r->rr); + break; +@@ -1566,6 +1596,97 @@ dirtomon(enum wlr_direction dir) + return selmon; + } + ++void ++remove_outer_separators(char **str) ++{ ++ size_t clean_tag_name_len = strlen(*str) - 2; ++ ++ char *temp_tag_name = (char*) ++ malloc(clean_tag_name_len + 1); ++ ++ if (temp_tag_name == NULL) perror("dwm: malloc()"); ++ ++ memset(temp_tag_name, 0, clean_tag_name_len + 1); ++ ++ char *clean_tag_name_beg = *str + 1; ++ strncpy(temp_tag_name, ++ clean_tag_name_beg, ++ clean_tag_name_len); ++ ++ if (*str) free(*str); ++ *str = temp_tag_name; ++} ++ ++void ++appiconsappend(char **str, const char *appicon, size_t new_size) ++{ ++ char *temp_tag_name = (char*) malloc(new_size); ++ if (temp_tag_name == NULL) perror("dwm: malloc()"); ++ ++ /* NOTE: Example format of temp_tag_name (with two appicons): ++ * ++ */ ++ temp_tag_name = memset(temp_tag_name, 0, new_size); ++ ++ temp_tag_name[0] = outer_separator_beg; ++ temp_tag_name[new_size - 2] = outer_separator_end; ++ ++ strncpy(temp_tag_name + 1, *str, strlen(*str)); ++ temp_tag_name[strlen(temp_tag_name)] = inner_separator; ++ ++ strncpy(temp_tag_name + strlen(temp_tag_name), ++ appicon, strlen(appicon)); ++ ++ if (*str) free(*str); ++ *str = temp_tag_name; ++} ++ ++void ++applyappicon(char *tag_icons[], int *icons_per_tag, const Client *c) ++{ ++ for (unsigned t = 1, i = 0; ++ i < LENGTH(tags); ++ t <<= 1, i++) ++ { ++ if (c->tags & t) { ++ if (icons_per_tag[i] == 0) { ++ if (tag_icons[i]) free(tag_icons[i]); ++ tag_icons[i] = strndup(c->appicon, strlen(c->appicon)); ++ } else { ++ char *icon = NULL; ++ if (icons_per_tag[i] < truncate_icons_after) ++ icon = c->appicon; ++ else if (icons_per_tag[i] == truncate_icons_after) ++ icon = truncate_symbol; ++ else { ++ icons_per_tag[i]++; ++ continue; ++ } ++ ++ /* remove outer separators from previous iterations ++ * otherwise they get applied recursively */ ++ if (icons_per_tag[i] > 1) { ++ remove_outer_separators(&tag_icons[i]); ++ } ++ ++ size_t outer_separators_size = 2; ++ size_t inner_separator_size = 1; ++ ++ size_t new_size = strlen(tag_icons[i]) ++ + outer_separators_size ++ + inner_separator_size ++ + strlen(icon) ++ + 1; ++ ++ appiconsappend(&tag_icons[i], icon, new_size); ++ } ++ ++ icons_per_tag[i]++; ++ } ++ } ++} ++ ++ + void + drawbar(Monitor *m) + { +@@ -1588,9 +1709,22 @@ drawbar(Monitor *m) + drwl_text(m->drw, m->b.width - tw, 0, tw, m->b.height, 0, stext, 0); + } + ++ int icons_per_tag[LENGTH(tags)]; ++ memset(icons_per_tag, 0, LENGTH(tags) * sizeof(int)); ++ ++ for (int i = 0; i < LENGTH(tags); i++) { ++ /* set each tag to default value */ ++ m->tag_icons[i] = strndup(tags[i], strlen(tags[i])); ++ } ++ + wl_list_for_each(c, &clients, link) { + if (c->mon != m) + continue; ++ ++ if (c->appicon && strlen(c->appicon) > 0) { ++ applyappicon(m->tag_icons, icons_per_tag, c); ++ } ++ + occ |= c->tags; + if (c->isurgent) + urg |= c->tags; +@@ -1598,10 +1732,10 @@ drawbar(Monitor *m) + x = 0; + c = focustop(m); + for (i = 0; i < LENGTH(tags); i++) { +- w = TEXTW(m, tags[i]); ++ w = TEXTW(m, m->tag_icons[i]); + drwl_setscheme(m->drw, colors[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]); +- drwl_text(m->drw, x, 0, w, m->b.height, m->lrpad / 2, tags[i], urg & 1 << i); +- if (occ & 1 << i) ++ drwl_text(m->drw, x, 0, w, m->b.height, m->lrpad / 2, m->tag_icons[i], urg & 1 << i); ++ if (occ & 1 << i && icons_per_tag[i] ==0) + drwl_rect(m->drw, x + boxs, boxs, boxw, boxw, + m == selmon && c && c->tags & 1 << i, + urg & 1 << i); +-- +2.51.1 +