summaryrefslogtreecommitdiffstats
path: root/dwl-patches/patches/menurule
diff options
context:
space:
mode:
authorLeonard Kugis <leonard@kug.is>2025-05-23 11:41:09 +0000
committerLeonard Kugis <leonard@kug.is>2025-05-23 11:41:09 +0000
commitc70505d7c7b7b48600f273357694b56ccf5d2a15 (patch)
tree21c27ac6ffced8d6d904e35bdb39baa5d685d829 /dwl-patches/patches/menurule
downloaddotfiles-master.tar.gz
dotfiles-master.tar.bz2
dotfiles-master.zip
Initial commitHEADmaster
Diffstat (limited to 'dwl-patches/patches/menurule')
-rw-r--r--dwl-patches/patches/menurule/README.md70
-rw-r--r--dwl-patches/patches/menurule/menurule.patch167
-rw-r--r--dwl-patches/patches/menurule/menurule.pngbin0 -> 179351 bytes
3 files changed, 237 insertions, 0 deletions
diff --git a/dwl-patches/patches/menurule/README.md b/dwl-patches/patches/menurule/README.md
new file mode 100644
index 0000000..8d8b757
--- /dev/null
+++ b/dwl-patches/patches/menurule/README.md
@@ -0,0 +1,70 @@
+### Description
+
+This patch adds a dmenu interface to [setrule][setrule], which allows to add or
+change client rules at runtime. It must be applied on top of [setrule][setrule]
+and [menu][menu] patches.
+
+You can invoke the menu by pressing `Alt+R`. The menu lists all the rules, plus
+a shortcut to define a new one that would apply to the currently focused client
+(marked with `(NEW)`). Rules that already apply to the focused client are marked
+with `<`.
+
+![menurule in action](menurule.png)
+
+To edit a rule, you need to select it, press `Tab`, change what you need and
+finally press `Enter`. You can remove a rule by prepending it with `-`. To add a
+new rule, just put new values into `[appid|title]`.
+
+To add support for new rules, you need to edit `fprintf` and `sscanf` calls in
+`menurulefeed` and `menuruleaction` functions respectively.
+
+For example, this is what I did to add support for [swallow][swallow] patch
+rules.
+
+```diff
+diff --git a/dwl.c b/dwl.c
+index 34397fc..f1b31ea 100644
+--- a/dwl.c
++++ b/dwl.c
+@@ -2441,10 +2441,14 @@ menurulefeed(FILE *f)
+ fprintf(f, "%-*s "
+ " tags:%-4"PRIi32
+ " isfloating:%-2d"
++ " isterm:%-2d"
++ " noswallow:%-2d"
+ " monitor:%-2d"
+ "%s\n", wid, buf,
+ r->tags,
+ r->isfloating,
++ r->isterm,
++ r->noswallow,
+ r->monitor,
+ (r == &t) ? " (NEW)" : match ? " <" : "");
+ }
+@@ -2465,10 +2469,14 @@ menuruleaction(char *line)
+ sscanf(line, "[%255[^|]|%255[^]]]"
+ " tags:%"SCNu32
+ " isfloating:%d"
++ " isterm:%d"
++ " noswallow:%d"
+ " monitor:%d"
+ "%n", appid, title,
+ &r.tags,
+ &r.isfloating,
++ &r.isterm,
++ &r.noswallow,
+ &r.monitor,
+ &end);
+```
+
+[setrule]: /dwl/dwl-patches/src/branch/main/patches/setrule
+[menu]: /dwl/dwl-patches/src/branch/main/patches/menu
+[swallow]: /dwl/dwl-patches/src/branch/main/patches/swallow
+
+### Download
+
+- [v0.7](/dwl/dwl-patches/raw/branch/main/patches/menurule/menurule.patch)
+
+### Authors
+
+- [Nikita Ivanov](https://codeberg.org/nikitaivanov) ([GitHub](https://github.com/NikitaIvanovV))
diff --git a/dwl-patches/patches/menurule/menurule.patch b/dwl-patches/patches/menurule/menurule.patch
new file mode 100644
index 0000000..b74a600
--- /dev/null
+++ b/dwl-patches/patches/menurule/menurule.patch
@@ -0,0 +1,167 @@
+From 7b578d9f4647d84f79a2e8a46a1c65cbacf8d90b Mon Sep 17 00:00:00 2001
+From: Nikita Ivanov <nikita.vyach.ivanov@gmail.com>
+Date: Wed, 19 Mar 2025 02:28:46 +0100
+Subject: [PATCH] Add menurule to tweak rules at runtime
+
+---
+ config.def.h | 2 +
+ dwl.c | 116 +++++++++++++++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 118 insertions(+)
+
+diff --git a/config.def.h b/config.def.h
+index e03a754..77b10ff 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -24,6 +24,7 @@ static const Menu menus[] = {
+ /* command feed function action function */
+ { "wmenu -i -l 10 -p Windows", menuwinfeed, menuwinaction },
+ { "wmenu -i -p Layouts", menulayoutfeed, menulayoutaction },
++ { "wmenu -i -l 10 -p Rules", menurulefeed, menuruleaction },
+ };
+
+ /* Max amount of dynamically added rules */
+@@ -151,6 +152,7 @@ static const Key keys[] = {
+ { MODKEY, XKB_KEY_space, setlayout, {0} },
+ { MODKEY, XKB_KEY_o, menu, {.v = &menus[0]} },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_O, menu, {.v = &menus[1]} },
++ { MODKEY, XKB_KEY_r, menu, {.v = &menus[2]} },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} },
+ { MODKEY, XKB_KEY_e, togglefullscreen, {0} },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_R, setruleisfloating,{0} },
+diff --git a/dwl.c b/dwl.c
+index be007d8..df4901f 100644
+--- a/dwl.c
++++ b/dwl.c
+@@ -316,6 +316,8 @@ static void menuwinfeed(FILE *f);
+ static void menuwinaction(char *line);
+ static void menulayoutfeed(FILE *f);
+ static void menulayoutaction(char *line);
++static void menurulefeed(FILE *f);
++static void menuruleaction(char *line);
+ static void monocle(Monitor *m);
+ static void motionabsolute(struct wl_listener *listener, void *data);
+ static void motionnotify(uint32_t time, struct wlr_input_device *device, double sx,
+@@ -1974,6 +1976,120 @@ found:
+ setlayout(&(const Arg){ .v = l });
+ }
+
++void
++menurulefeed(FILE *f)
++{
++ Rule t, *p, *r;
++ const char *appid, *title;
++ static char buf[515];
++ Client *c = focustop(selmon);
++ int n, wid = 0, match;
++
++ t = (Rule){ 0 };
++ t.monitor = -1;
++ if (c) {
++ t.id = client_get_appid(c);
++ t.title = client_get_title(c);
++ appid = t.id ? t.id : broken;
++ title = t.title ? t.title : broken;
++ }
++
++ for (p = drules; p <= drules + druleslen; p++) {
++ r = (p == drules + druleslen) ? &t : p;
++ n = 0;
++ n += strlen(r->id ? r->id : "NULL");
++ n += strlen(r->title ? r->title : "NULL");
++ n += 3;
++ wid = MAX(wid, n);
++ }
++ wid = MIN(wid, 40);
++
++ for (p = drules; p <= drules + druleslen; p++) {
++ match = 0;
++ /* Check if rule applies to the focused client */
++ if (c && p < drules + druleslen) {
++ match = (!p->title || strstr(title, p->title))
++ && (!p->id || strstr(appid, p->id));
++ if (match && p->id)
++ t.id = NULL;
++ if (match && p->title)
++ t.title = NULL;
++ }
++ r = (p == drules + druleslen) ? &t : p;
++ if (r == &t && t.id)
++ t.title = NULL;
++ /* Do not suggest to add a new empty rule */
++ if (r == &t && !(t.id || t.title))
++ continue;
++ if (r->id && r->title &&
++ strcmp(r->id, "removedrule") == 0 && strcmp(r->title, "removedrule") == 0)
++ continue;
++ snprintf(buf, LENGTH(buf) - 1, "[%s|%s]",
++ r->id ? r->id : "NULL", r->title ? r->title : "NULL");
++ fprintf(f, "%-*s "
++ " tags:%-4"PRIi32
++ " isfloating:%-2d"
++ " monitor:%-2d"
++ "%s\n", wid, buf,
++ r->tags,
++ r->isfloating,
++ r->monitor,
++ (r == &t) ? " (NEW)" : match ? " <" : "");
++ }
++}
++
++void
++menuruleaction(char *line)
++{
++ Rule r, *f;
++ static char appid[256], title[256];
++ int del = 0, end;
++
++ if (line[0] == '-') {
++ del = 1;
++ line++;
++ }
++ end = 0;
++ sscanf(line, "[%255[^|]|%255[^]]]"
++ " tags:%"SCNu32
++ " isfloating:%d"
++ " monitor:%d"
++ "%n", appid, title,
++ &r.tags,
++ &r.isfloating,
++ &r.monitor,
++ &end);
++
++ /* Full line was not parsed, exit */
++ if (!end)
++ return;
++
++ r.id = (strcmp(appid, "NULL") != 0) ? appid : NULL;
++ r.title = (strcmp(title, "NULL") != 0) ? title : NULL;
++
++ /* Find which rule we are trying to edit */
++ for (f = drules; f < drules + druleslen; f++)
++ if (((!r.title && !f->title) || (r.title && f->title && !strcmp(r.title, f->title)))
++ && (((!r.id && !f->id) || (r.id && f->id && !strcmp(r.id, f->id)))))
++ goto found;
++
++ if (druleslen >= LENGTH(rules) + RULES_MAX)
++ return; /* No free slots left */
++
++ f = drules + druleslen++;
++ f->id = r.id ? strdup(r.id) : NULL;
++ f->title = r.title ? strdup(r.title) : NULL;
++
++found:
++ if (del) {
++ f->id = f->title = "removedrule";
++ return;
++ }
++ r.id = f->id;
++ r.title = f->title;
++ *f = r;
++}
++
+ void
+ monocle(Monitor *m)
+ {
+--
+2.49.0
+
diff --git a/dwl-patches/patches/menurule/menurule.png b/dwl-patches/patches/menurule/menurule.png
new file mode 100644
index 0000000..20faac2
--- /dev/null
+++ b/dwl-patches/patches/menurule/menurule.png
Binary files differ