From 97be2a869d915a9d679b3bb2907fa1785a85c383 Mon Sep 17 00:00:00 2001 From: Nikita Ivanov Date: Fri, 14 Feb 2025 00:01:07 +0100 Subject: [PATCH] Add setrule patch --- patches/setrule/README.md | 31 ++++++++ patches/setrule/setrule.patch | 142 ++++++++++++++++++++++++++++++++++ 2 files changed, 173 insertions(+) create mode 100644 patches/setrule/README.md create mode 100644 patches/setrule/setrule.patch diff --git a/patches/setrule/README.md b/patches/setrule/README.md new file mode 100644 index 0000000..d2dfa64 --- /dev/null +++ b/patches/setrule/README.md @@ -0,0 +1,31 @@ +### Description + +This patch adds an ability to add or change client rules at runtime. + +The patch only adds one keybinding (`Alt+Shift+R`) to toggle `isfloating` +option, but this can be easily extended if you want to tweak other options as +well. You just need to define a new function similar to `setruleisfloating` and +add a new keybinding to `config.h`. + +For example, this is a function I created for my build to toggle `noswallow` +option from the [swallow][swallow] patch: + +```c +void +setrulenoswallow(const Arg *arg) { + Rule *r = getrule(focustop(selmon)); + if (!r) + return; + r->noswallow = !r->noswallow; +} +``` + +[swallow]: /dwl/dwl-patches/src/branch/main/patches/swallow + +### Download + +- [0.7](/dwl/dwl-patches/raw/branch/main/patches/setrule/setrule.patch) + +### Authors + +- [Nikita Ivanov](https://codeberg.org/nikitaivanov) ([GitHub](https://github.com/NikitaIvanovV)) diff --git a/patches/setrule/setrule.patch b/patches/setrule/setrule.patch new file mode 100644 index 0000000..22fe4e8 --- /dev/null +++ b/patches/setrule/setrule.patch @@ -0,0 +1,142 @@ +From 3fcf8eed49af57b1185c9a41cf660d2b3604da96 Mon Sep 17 00:00:00 2001 +From: Nikita Ivanov +Date: Sun, 9 Feb 2025 23:12:09 +0100 +Subject: [PATCH] setrule: add/change rules at runtime + +--- + config.def.h | 9 ++++++++- + dwl.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++- + 2 files changed, 63 insertions(+), 2 deletions(-) + +diff --git a/config.def.h b/config.def.h +index 22d2171..666a8c3 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -20,14 +20,20 @@ static const float fullscreen_bg[] = {0.1f, 0.1f, 0.1f, 1.0f}; /* You ca + /* logging */ + static int log_level = WLR_ERROR; + ++/* Max amount of rules */ ++#define RULES_MAX 100 ++ + /* NOTE: ALWAYS keep a rule declared even if you don't use rules (e.g leave at least one example) */ +-static const Rule rules[] = { ++static Rule rules[RULES_MAX] = { + /* app_id title tags mask isfloating monitor */ + /* 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" */ + }; + ++static const Rule rule_default = ++ { NULL, NULL, 0, 0, -1 }; ++ + /* layout(s) */ + static const Layout layouts[] = { + /* symbol arrange function */ +@@ -142,6 +148,7 @@ static const Key keys[] = { + { MODKEY, XKB_KEY_space, setlayout, {0} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} }, + { MODKEY, XKB_KEY_e, togglefullscreen, {0} }, ++ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_R, setruleisfloating,{0} }, + { MODKEY, XKB_KEY_0, view, {.ui = ~0} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_parenright, tag, {.ui = ~0} }, + { MODKEY, XKB_KEY_comma, focusmon, {.i = WLR_DIRECTION_LEFT} }, +diff --git a/dwl.c b/dwl.c +index def2562..bdbb8b6 100644 +--- a/dwl.c ++++ b/dwl.c +@@ -290,6 +290,7 @@ static void focusmon(const Arg *arg); + static void focusstack(const Arg *arg); + static Client *focustop(Monitor *m); + static void fullscreennotify(struct wl_listener *listener, void *data); ++static Rule *getrule(Client *c); + static void gpureset(struct wl_listener *listener, void *data); + static void handlesig(int signo); + static void incnmaster(const Arg *arg); +@@ -331,6 +332,7 @@ static void setlayout(const Arg *arg); + static void setmfact(const Arg *arg); + static void setmon(Client *c, Monitor *m, uint32_t newtags); + static void setpsel(struct wl_listener *listener, void *data); ++static void setruleisfloating(const Arg *arg); + static void setsel(struct wl_listener *listener, void *data); + static void setup(void); + static void spawn(const Arg *arg); +@@ -466,7 +468,7 @@ applyrules(Client *c) + if (!(title = client_get_title(c))) + title = broken; + +- for (r = rules; r < END(rules); r++) { ++ for (r = rules; r->id || r->title; r++) { + if ((!r->title || strstr(title, r->title)) + && (!r->id || strstr(appid, r->id))) { + c->isfloating = r->isfloating; +@@ -1472,6 +1474,50 @@ fullscreennotify(struct wl_listener *listener, void *data) + setfullscreen(c, client_wants_fullscreen(c)); + } + ++Rule * ++getrule(Client *c) ++{ ++ Rule *r, *e = NULL; ++ const char *appid, *title; ++ ++ if (!c) ++ return NULL; ++ ++ if (!(appid = client_get_appid(c))) ++ appid = broken; ++ if (!(title = client_get_title(c))) ++ title = broken; ++ ++ /* ++ * Find first matching rule from the end. It seems intuitive to me ++ * that the the first matching rule from the end is going to be ++ * overriden. I also do not include the last element (hence -2) ++ * in the search because we always want to keep at least one empty ++ * slot for applyrules(). ++ */ ++ for (r = END(rules) - 2; r >= rules; r--) { ++ if (!r->title && !r->id) { ++ e = r; ++ } else { ++ if ((!r->title || strstr(title, r->title)) ++ && (!r->id || strstr(appid, r->id))) { ++ break; ++ } ++ } ++ } ++ if (r < rules) ++ r = e; ++ if (!r) /* No free slots left */ ++ return NULL; ++ if (r == e) { /* Fill the empty slot */ ++ *r = rule_default; ++ /* r->title = strdup(title); */ ++ r->id = strdup(appid); ++ } ++ ++ return r; ++} ++ + void + gpureset(struct wl_listener *listener, void *data) + { +@@ -2417,6 +2463,14 @@ setpsel(struct wl_listener *listener, void *data) + wlr_seat_set_primary_selection(seat, event->source, event->serial); + } + ++void ++setruleisfloating(const Arg *arg) { ++ Rule *r = getrule(focustop(selmon)); ++ if (!r) ++ return; ++ r->isfloating = !r->isfloating; ++} ++ + void + setsel(struct wl_listener *listener, void *data) + { +-- +2.48.1 +