dwm-appicons-20250601-c05f117.diff - sites - public wiki contents of suckless.org
 (HTM) git clone git://git.suckless.org/sites
 (DIR) Log
 (DIR) Files
 (DIR) Refs
       ---
       dwm-appicons-20250601-c05f117.diff (8751B)
       ---
            1 From c0fcc16b38f41a0ae7638c5ed5718f5aa9747913 Mon Sep 17 00:00:00 2001
            2 From: Rumen <rumenmitov@protonmail.com>
            3 Date: Sun, 1 Jun 2025 12:23:04 +0200
            4 Subject: [PATCH] fix: segfault when rendering icons
            5 
            6 fixed a segfault due to a double free when copying appicons between
            7 strings
            8 ---
            9  config.def.h |  14 ++++--
           10  dwm.c        | 140 +++++++++++++++++++++++++++++++++++++++++++++++++--
           11  2 files changed, 147 insertions(+), 7 deletions(-)
           12 
           13 diff --git a/config.def.h b/config.def.h
           14 index 9efa774..3045af6 100644
           15 --- a/config.def.h
           16 +++ b/config.def.h
           17 @@ -21,14 +21,22 @@ static const char *colors[][3]      = {
           18  /* tagging */
           19  static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
           20  
           21 +/* appicons */
           22 +/* NOTE: set to 0 to set to default (whitespace) */
           23 +static char outer_separator_beg      = '[';
           24 +static char outer_separator_end      = ']';
           25 +static char inner_separator          = ' ';
           26 +static unsigned truncate_icons_after = 2; /* will default to 1, that is the min */
           27 +static char truncate_symbol[]         = "...";
           28 +
           29  static const Rule rules[] = {
           30          /* xprop(1):
           31           *        WM_CLASS(STRING) = instance, class
           32           *        WM_NAME(STRING) = title
           33           */
           34 -        /* class      instance    title       tags mask     isfloating   monitor */
           35 -        { "Gimp",     NULL,       NULL,       0,            1,           -1 },
           36 -        { "Firefox",  NULL,       NULL,       1 << 8,       0,           -1 },
           37 +        /* class      instance    title       tags mask     isfloating   monitor   appicon*/
           38 +        { "Gimp",     NULL,       NULL,       0,            1,           -1,         NULL },
           39 +        { "Firefox",  NULL,       NULL,       1 << 8,       0,           -1,         "󰈹"  },
           40  };
           41  
           42  /* layout(s) */
           43 diff --git a/dwm.c b/dwm.c
           44 index 1443802..4fe7a6d 100644
           45 --- a/dwm.c
           46 +++ b/dwm.c
           47 @@ -85,6 +85,7 @@ typedef struct Monitor Monitor;
           48  typedef struct Client Client;
           49  struct Client {
           50          char name[256];
           51 +    char *appicon;
           52          float mina, maxa;
           53          int x, y, w, h;
           54          int oldx, oldy, oldw, oldh;
           55 @@ -121,6 +122,7 @@ struct Monitor {
           56          unsigned int seltags;
           57          unsigned int sellt;
           58          unsigned int tagset[2];
           59 +    char **tag_icons;
           60          int showbar;
           61          int topbar;
           62          Client *clients;
           63 @@ -138,6 +140,7 @@ typedef struct {
           64          unsigned int tags;
           65          int isfloating;
           66          int monitor;
           67 +    const char *appicon;
           68  } Rule;
           69  
           70  /* function declarations */
           71 @@ -160,6 +163,9 @@ static void destroynotify(XEvent *e);
           72  static void detach(Client *c);
           73  static void detachstack(Client *c);
           74  static Monitor *dirtomon(int dir);
           75 +static void remove_outer_separators(char **str);
           76 +static void appiconsappend(char **str, const char *appicon, size_t new_size);
           77 +static void applyappicon(char *tag_icons[], int *icons_per_tag, const Client *c);
           78  static void drawbar(Monitor *m);
           79  static void drawbars(void);
           80  static void enternotify(XEvent *e);
           81 @@ -283,7 +289,13 @@ applyrules(Client *c)
           82          Monitor *m;
           83          XClassHint ch = { NULL, NULL };
           84  
           85 +    outer_separator_beg = outer_separator_beg ? outer_separator_beg : ' ';
           86 +    outer_separator_end = outer_separator_end ? outer_separator_end : ' ';
           87 +    inner_separator = inner_separator ? inner_separator : ' ';
           88 +    truncate_icons_after = truncate_icons_after > 0 ? truncate_icons_after : 1;
           89 +
           90          /* rule matching */
           91 +    c->appicon = NULL;
           92          c->isfloating = 0;
           93          c->tags = 0;
           94          XGetClassHint(dpy, c->win, &ch);
           95 @@ -296,6 +308,8 @@ applyrules(Client *c)
           96                  && (!r->class || strstr(class, r->class))
           97                  && (!r->instance || strstr(instance, r->instance)))
           98                  {
           99 +            /* r->appicon is static, so lifetime is sufficient */
          100 +            c->appicon = (char*) r->appicon; 
          101                          c->isfloating = r->isfloating;
          102                          c->tags |= r->tags;
          103                          for (m = mons; m && m->num != r->monitor; m = m->next);
          104 @@ -433,7 +447,7 @@ buttonpress(XEvent *e)
          105          if (ev->window == selmon->barwin) {
          106                  i = x = 0;
          107                  do
          108 -                        x += TEXTW(tags[i]);
          109 +                        x += TEXTW(m->tag_icons[i]);
          110                  while (ev->x >= x && ++i < LENGTH(tags));
          111                  if (i < LENGTH(tags)) {
          112                          click = ClkTagBar;
          113 @@ -508,6 +522,14 @@ cleanupmon(Monitor *mon)
          114          }
          115          XUnmapWindow(dpy, mon->barwin);
          116          XDestroyWindow(dpy, mon->barwin);
          117 +
          118 +    for (int i = 0; i < LENGTH(tags); i++) {
          119 +        if (mon->tag_icons[i]) free(mon->tag_icons[i]);
          120 +        mon->tag_icons[i] = NULL;
          121 +    }
          122 +
          123 +    if (mon->tag_icons) free(mon->tag_icons);
          124 +
          125          free(mon);
          126  }
          127  
          128 @@ -643,6 +665,13 @@ createmon(void)
          129          m->lt[0] = &layouts[0];
          130          m->lt[1] = &layouts[1 % LENGTH(layouts)];
          131          strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol);
          132 +
          133 +    m->tag_icons = (char**) malloc(LENGTH(tags) * sizeof(char*));
          134 +    if (m->tag_icons == NULL) perror("dwm: malloc()");
          135 +    for (int i = 0; i < LENGTH(tags); i++) {
          136 +        m->tag_icons[i] = NULL;
          137 +    }
          138 +
          139          return m;
          140  }
          141  
          142 @@ -694,6 +723,96 @@ dirtomon(int dir)
          143          return m;
          144  }
          145  
          146 +void
          147 +remove_outer_separators(char **str)
          148 +{
          149 +    size_t clean_tag_name_len = strlen(*str) - 2;
          150 +
          151 +    char *temp_tag_name = (char*) 
          152 +        malloc(clean_tag_name_len + 1);
          153 +
          154 +    if (temp_tag_name == NULL) perror("dwm: malloc()");
          155 +
          156 +    memset(temp_tag_name, 0, clean_tag_name_len + 1);
          157 +
          158 +    char *clean_tag_name_beg = *str + 1;
          159 +    strncpy(temp_tag_name, 
          160 +            clean_tag_name_beg, 
          161 +            clean_tag_name_len);
          162 +
          163 +    if (*str) free(*str);
          164 +    *str = temp_tag_name;
          165 +}
          166 +
          167 +void
          168 +appiconsappend(char **str, const char *appicon, size_t new_size)
          169 +{
          170 +    char *temp_tag_name = (char*) malloc(new_size);
          171 +    if (temp_tag_name == NULL) perror("dwm: malloc()");
          172 +
          173 +    /* NOTE: Example format of temp_tag_name (with two appicons):
          174 +     *  <outer_sep_beg><appicon><inner_sep><appicon><outer_sep_end>
          175 +     */
          176 +    temp_tag_name = memset(temp_tag_name, 0, new_size);
          177 +
          178 +    temp_tag_name[0] = outer_separator_beg;
          179 +    temp_tag_name[new_size - 2] = outer_separator_end;
          180 +
          181 +    strncpy(temp_tag_name + 1, *str, strlen(*str));
          182 +    temp_tag_name[strlen(temp_tag_name)] = inner_separator;
          183 +
          184 +    strncpy(temp_tag_name + strlen(temp_tag_name),
          185 +            appicon, strlen(appicon));
          186 +
          187 +    if (*str) free(*str);
          188 +    *str = temp_tag_name;
          189 +}
          190 +
          191 +void
          192 +applyappicon(char *tag_icons[], int *icons_per_tag, const Client *c)
          193 +{
          194 +    for (unsigned t = 1, i = 0;
          195 +            i < LENGTH(tags);
          196 +            t <<= 1, i++) 
          197 +    {
          198 +        if (c->tags & t) {
          199 +          if (icons_per_tag[i] == 0) {
          200 +                if (tag_icons[i]) free(tag_icons[i]);
          201 +                tag_icons[i] = strndup(c->appicon, strlen(c->appicon));
          202 +          } else {
          203 +                char *icon = NULL;
          204 +                if (icons_per_tag[i] < truncate_icons_after)
          205 +                    icon = c->appicon;
          206 +                else if (icons_per_tag[i] == truncate_icons_after)
          207 +                    icon =  truncate_symbol;
          208 +                else {
          209 +                    icons_per_tag[i]++;
          210 +                    continue;
          211 +                }
          212 +                    
          213 +                /* remove outer separators from previous iterations
          214 +                 * otherwise they get applied recursively */
          215 +                if (icons_per_tag[i] > 1) {
          216 +                    remove_outer_separators(&tag_icons[i]);
          217 +                }
          218 +
          219 +                size_t outer_separators_size = 2;
          220 +                size_t inner_separator_size = 1;
          221 +
          222 +                size_t new_size = strlen(tag_icons[i])
          223 +                    + outer_separators_size 
          224 +                    + inner_separator_size
          225 +                    + strlen(icon)
          226 +                    + 1;
          227 +
          228 +                appiconsappend(&tag_icons[i], icon, new_size);
          229 +            }
          230 +
          231 +            icons_per_tag[i]++;
          232 +        }
          233 +    }
          234 +}
          235 +
          236  void
          237  drawbar(Monitor *m)
          238  {
          239 @@ -713,22 +832,35 @@ drawbar(Monitor *m)
          240                  drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0);
          241          }
          242  
          243 +    int icons_per_tag[LENGTH(tags)];
          244 +    memset(icons_per_tag, 0, LENGTH(tags) * sizeof(int));
          245 +
          246 +    for (int i = 0; i < LENGTH(tags); i++) {
          247 +        /* set each tag to default value */
          248 +        m->tag_icons[i] = strndup(tags[i], strlen(tags[i]));
          249 +    }
          250 +
          251          for (c = m->clients; c; c = c->next) {
          252 +        if (c->appicon && strlen(c->appicon) > 0) {
          253 +            applyappicon(m->tag_icons, icons_per_tag, c);
          254 +        }
          255 +
          256                  occ |= c->tags;
          257                  if (c->isurgent)
          258                          urg |= c->tags;
          259          }
          260          x = 0;
          261          for (i = 0; i < LENGTH(tags); i++) {
          262 -                w = TEXTW(tags[i]);
          263 +                w = TEXTW(m->tag_icons[i]);
          264                  drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]);
          265 -                drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i);
          266 -                if (occ & 1 << i)
          267 +                drw_text(drw, x, 0, w, bh, lrpad / 2, m->tag_icons[i], urg & 1 << i);
          268 +                if (occ & 1 << i && icons_per_tag[i] == 0)
          269                          drw_rect(drw, x + boxs, boxs, boxw, boxw,
          270                                  m == selmon && selmon->sel && selmon->sel->tags & 1 << i,
          271                                  urg & 1 << i);
          272                  x += w;
          273          }
          274 +
          275          w = TEXTW(m->ltsymbol);
          276          drw_setscheme(drw, scheme[SchemeNorm]);
          277          x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0);
          278 -- 
          279 2.49.0
          280