dwl-patches/patches/appicons/appicons.patch
2025-10-24 16:20:39 +02:00

287 lines
9.7 KiB
Diff

From 04d84cccbfb0fa324f960942359c3edd92f89071 Mon Sep 17 00:00:00 2001
From: Rumen <rumenmitov@protonmail.com>
Date: Fri, 24 Oct 2025 16:00:58 +0200
Subject: [PATCH] App Icons in the dwl bar!
Adds support for app icons that can replace the tag indicator and tag name.
This feature is configurable through an additional option in rules.
Icons should work out of the box.
Emojis require a special font like Noto Color Emoji.
When one or more app icons are present in a tag, the tag name will be enclosed
by the outer separators (`outer_separator_beg` and `outer_separator_end`).
Additionally, the icons within the tag will be separated by inner_separator.
Each tag can display a maximum of `truncate_icons_after` icons, after which the
`truncate_symbol` will be shown.
Inspired by XMonad's DynamicIcons.
---
config.def.h | 14 +++--
dwl.c | 141 +++++++++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 148 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..33a76d9 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):
+ * <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)
{
@@ -1588,7 +1709,19 @@ 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->appicon && strlen(c->appicon) > 0) {
+ applyappicon(m->tag_icons, icons_per_tag, c);
+ }
+
if (c->mon != m)
continue;
occ |= c->tags;
@@ -1598,10 +1731,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