fix dwm-appicons - sites - public wiki contents of suckless.org
 (HTM) git clone git://git.suckless.org/sites
 (DIR) Log
 (DIR) Files
 (DIR) Refs
       ---
 (DIR) commit 53a3bc9dec3cca309fb4a77d068e4281dcb15a61
 (DIR) parent 8242cafbdac3078762d860c0b75d439bf3331cff
 (HTM) Author: Rumen <rumenmitov@protonmail.com>
       Date:   Sun,  1 Jun 2025 14:33:49 +0200
       
       fix dwm-appicons
       
       fixed segfault in dwm-appicons due to a double free
       
       Diffstat:
         A dwm.suckless.org/patches/appicons/… |     280 +++++++++++++++++++++++++++++++
         M dwm.suckless.org/patches/appicons/… |       3 ++-
       
       2 files changed, 282 insertions(+), 1 deletion(-)
       ---
 (DIR) diff --git a/dwm.suckless.org/patches/appicons/dwm-appicons-20250601-c05f117.diff b/dwm.suckless.org/patches/appicons/dwm-appicons-20250601-c05f117.diff
       @@ -0,0 +1,280 @@
       +From c0fcc16b38f41a0ae7638c5ed5718f5aa9747913 Mon Sep 17 00:00:00 2001
       +From: Rumen <rumenmitov@protonmail.com>
       +Date: Sun, 1 Jun 2025 12:23:04 +0200
       +Subject: [PATCH] fix: segfault when rendering icons
       +
       +fixed a segfault due to a double free when copying appicons between
       +strings
       +---
       + config.def.h |  14 ++++--
       + dwm.c        | 140 +++++++++++++++++++++++++++++++++++++++++++++++++--
       + 2 files changed, 147 insertions(+), 7 deletions(-)
       +
       +diff --git a/config.def.h b/config.def.h
       +index 9efa774..3045af6 100644
       +--- a/config.def.h
       ++++ b/config.def.h
       +@@ -21,14 +21,22 @@ static const char *colors[][3]      = {
       + /* tagging */
       + static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
       + 
       ++/* 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[]         = "...";
       ++
       + static const Rule rules[] = {
       +         /* xprop(1):
       +          *        WM_CLASS(STRING) = instance, class
       +          *        WM_NAME(STRING) = title
       +          */
       +-        /* class      instance    title       tags mask     isfloating   monitor */
       +-        { "Gimp",     NULL,       NULL,       0,            1,           -1 },
       +-        { "Firefox",  NULL,       NULL,       1 << 8,       0,           -1 },
       ++        /* class      instance    title       tags mask     isfloating   monitor   appicon*/
       ++        { "Gimp",     NULL,       NULL,       0,            1,           -1,         NULL },
       ++        { "Firefox",  NULL,       NULL,       1 << 8,       0,           -1,         "󰈹"  },
       + };
       + 
       + /* layout(s) */
       +diff --git a/dwm.c b/dwm.c
       +index 1443802..4fe7a6d 100644
       +--- a/dwm.c
       ++++ b/dwm.c
       +@@ -85,6 +85,7 @@ typedef struct Monitor Monitor;
       + typedef struct Client Client;
       + struct Client {
       +         char name[256];
       ++    char *appicon;
       +         float mina, maxa;
       +         int x, y, w, h;
       +         int oldx, oldy, oldw, oldh;
       +@@ -121,6 +122,7 @@ struct Monitor {
       +         unsigned int seltags;
       +         unsigned int sellt;
       +         unsigned int tagset[2];
       ++    char **tag_icons;
       +         int showbar;
       +         int topbar;
       +         Client *clients;
       +@@ -138,6 +140,7 @@ typedef struct {
       +         unsigned int tags;
       +         int isfloating;
       +         int monitor;
       ++    const char *appicon;
       + } Rule;
       + 
       + /* function declarations */
       +@@ -160,6 +163,9 @@ static void destroynotify(XEvent *e);
       + static void detach(Client *c);
       + static void detachstack(Client *c);
       + static Monitor *dirtomon(int 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 enternotify(XEvent *e);
       +@@ -283,7 +289,13 @@ applyrules(Client *c)
       +         Monitor *m;
       +         XClassHint ch = { NULL, NULL };
       + 
       ++    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 */
       ++    c->appicon = NULL;
       +         c->isfloating = 0;
       +         c->tags = 0;
       +         XGetClassHint(dpy, c->win, &ch);
       +@@ -296,6 +308,8 @@ applyrules(Client *c)
       +                 && (!r->class || strstr(class, r->class))
       +                 && (!r->instance || strstr(instance, r->instance)))
       +                 {
       ++            /* r->appicon is static, so lifetime is sufficient */
       ++            c->appicon = (char*) r->appicon; 
       +                         c->isfloating = r->isfloating;
       +                         c->tags |= r->tags;
       +                         for (m = mons; m && m->num != r->monitor; m = m->next);
       +@@ -433,7 +447,7 @@ buttonpress(XEvent *e)
       +         if (ev->window == selmon->barwin) {
       +                 i = x = 0;
       +                 do
       +-                        x += TEXTW(tags[i]);
       ++                        x += TEXTW(m->tag_icons[i]);
       +                 while (ev->x >= x && ++i < LENGTH(tags));
       +                 if (i < LENGTH(tags)) {
       +                         click = ClkTagBar;
       +@@ -508,6 +522,14 @@ cleanupmon(Monitor *mon)
       +         }
       +         XUnmapWindow(dpy, mon->barwin);
       +         XDestroyWindow(dpy, mon->barwin);
       ++
       ++    for (int i = 0; i < LENGTH(tags); i++) {
       ++        if (mon->tag_icons[i]) free(mon->tag_icons[i]);
       ++        mon->tag_icons[i] = NULL;
       ++    }
       ++
       ++    if (mon->tag_icons) free(mon->tag_icons);
       ++
       +         free(mon);
       + }
       + 
       +@@ -643,6 +665,13 @@ createmon(void)
       +         m->lt[0] = &layouts[0];
       +         m->lt[1] = &layouts[1 % LENGTH(layouts)];
       +         strncpy(m->ltsymbol, layouts[0].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;
       ++    }
       ++
       +         return m;
       + }
       + 
       +@@ -694,6 +723,96 @@ dirtomon(int dir)
       +         return m;
       + }
       + 
       ++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):
       ++     *  <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[], 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)
       + {
       +@@ -713,22 +832,35 @@ drawbar(Monitor *m)
       +                 drw_text(drw, m->ww - tw, 0, tw, bh, 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]));
       ++    }
       ++
       +         for (c = m->clients; c; c = c->next) {
       ++        if (c->appicon && strlen(c->appicon) > 0) {
       ++            applyappicon(m->tag_icons, icons_per_tag, c);
       ++        }
       ++
       +                 occ |= c->tags;
       +                 if (c->isurgent)
       +                         urg |= c->tags;
       +         }
       +         x = 0;
       +         for (i = 0; i < LENGTH(tags); i++) {
       +-                w = TEXTW(tags[i]);
       ++                w = TEXTW(m->tag_icons[i]);
       +                 drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]);
       +-                drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i);
       +-                if (occ & 1 << i)
       ++                drw_text(drw, x, 0, w, bh, lrpad / 2, m->tag_icons[i], urg & 1 << i);
       ++                if (occ & 1 << i && icons_per_tag[i] == 0)
       +                         drw_rect(drw, x + boxs, boxs, boxw, boxw,
       +                                 m == selmon && selmon->sel && selmon->sel->tags & 1 << i,
       +                                 urg & 1 << i);
       +                 x += w;
       +         }
       ++
       +         w = TEXTW(m->ltsymbol);
       +         drw_setscheme(drw, scheme[SchemeNorm]);
       +         x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0);
       +-- 
       +2.49.0
       +
 (DIR) diff --git a/dwm.suckless.org/patches/appicons/index.md b/dwm.suckless.org/patches/appicons/index.md
       @@ -22,7 +22,8 @@ Each tag can display a maximum of `truncate_icons_after` icons, after which the
        
        Download
        --------
       -* [dwm-appicons-6.5.diff](dwm-appicons-6.5.diff) (2025-01-04)
       +* [dwm-appicons-6.5.diff](dwm-appicons-6.5.diff) (2025-01-04), *deprecated*
       +* [dwm-appicons-20250601-c05f117.diff](dwm-appicons-20250601-c05f117.diff) (2025-06-01)
        
        Author
        ------