From f5d1206f7f467cafd5a0217a46c31928316ba2fe Mon Sep 17 00:00:00 2001
From: Rumen <rumenmitov@protonmail.com>
Date: Mon, 1 Dec 2025 20:29:49 +0100
Subject: [PATCH] fix(bar-appicons): fixed various compiler warnings

---
 config.def.h |  14 +++--
 dwl.c        | 143 +++++++++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 150 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..e2baf66 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[], unsigned int *icons_per_tag, const Client *c);
 static void drawbar(Monitor *m);
 static void drawbars(void);
 static void focusclient(Client *c, int lift);
@@ -527,12 +533,19 @@ applyrules(Client *c)
 	const Rule *r;
 	Monitor *mon = selmon, *m;

+  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;
+
 	appid = client_get_appid(c);
 	title = client_get_title(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 (long unsigned int tag_idx = 0; tag_idx < LENGTH(tags); tag_idx++) {
+      if (m->tag_icons[tag_idx]) free(m->tag_icons[tag_idx]);
+      m->tag_icons[tag_idx] = 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 (long unsigned int tag_idx = 0; tag_idx < LENGTH(tags); tag_idx++) {
+        m->tag_icons[tag_idx] = NULL;
+      }
+
 			wlr_output_state_set_scale(&state, r->scale);
 			wlr_output_state_set_transform(&state, r->rr);
 			break;
@@ -1566,6 +1596,98 @@ dirtomon(enum wlr_direction dir)
 	return selmon;
 }

+void
+remove_outer_separators(char **str)
+{
+    const char *clean_tag_name_beg = *str + 1;
+    const 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);
+
+    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):
+     *  <outer_sep_beg><appicon><inner_sep><appicon><outer_sep_end>
+     */
+    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[], unsigned int *icons_per_tag, const Client *c)
+{
+  const size_t outer_separators_size = 2;
+  const size_t inner_separator_size = 1;
+  size_t new_size = 0;
+
+    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]);
+                }
+
+                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)
 {
@@ -1575,6 +1697,7 @@ drawbar(Monitor *m)
 	uint32_t i, occ = 0, urg = 0;
 	Client *c;
 	Buffer *buf;
+  unsigned int icons_per_tag[LENGTH(tags)];

 	if (!m->scene_buffer->node.enabled)
 		return;
@@ -1588,9 +1711,21 @@ drawbar(Monitor *m)
 		drwl_text(m->drw, m->b.width - tw, 0, tw, m->b.height, 0, stext, 0);
 	}

+  memset(icons_per_tag, 0, LENGTH(tags) * sizeof(int));
+
+  for (long unsigned int tag_idx = 0; tag_idx < LENGTH(tags); tag_idx++) {
+    /* set each tag to default value */
+    m->tag_icons[tag_idx] = strndup(tags[tag_idx], strlen(tags[tag_idx]));
+  }
+
 	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 +1733,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.52.0

