mirror of
https://codeberg.org/dwl/dwl-patches.git
synced 2025-10-26 17:54:17 +00:00
287 lines
9.7 KiB
Diff
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
|
|
|