Compare commits

...

3 Commits

Author SHA1 Message Date
Nikita Ivanov
77b45273bf
Add menurule patch 2025-03-21 23:48:12 +01:00
Nikita Ivanov
e0999c504b
Update setrule patch 2025-03-21 22:29:58 +01:00
Nikita Ivanov
b0de2fb715
Update menu patch 2025-03-21 22:12:45 +01:00
7 changed files with 411 additions and 153 deletions

View File

@ -1,16 +1,18 @@
### Description
This patch adds `menu` command, which allows dwl to interface with dmenu-like programs.
This patch adds `menu` command, which allows dwl to interface with dmenu-like
programs.
By default, two menus are available:
- focusing a window by its title by pressing `Alt+o`
- focusing a window by its title/appid by pressing `Alt+o`
- selecting a layout from a list by pressing `Alt+Shift+o`
Edit `menus` array in `config.h` to add/change menus and use a different dmenu program.
Edit `menus` array in `config.h` to add/change menus and use a different dmenu
program (`wmenu` is the default).
### Download
- [git branch](https://codeberg.org/nikitaivanov/dwl/src/branch/menu)
- [2024-07-13](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/menu/menu.patch)
- [2025-03-21 v0.7](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/menu/menu.patch)
- [2024-07-13 v0.7](https://codeberg.org/dwl/dwl-patches/raw/commit/65ea99519bbf7a52f48932aea7385f81f8b30867/patches/menu/menu.patch)
### Authors
- [Nikita Ivanov](https://codeberg.org/nikitaivanov) ([GitHub](https://github.com/NikitaIvanovV))

View File

@ -1,11 +1,15 @@
commit 2d7e19dc948aec61746fd858394a9c80d34a3216
Author: Nikita Ivanov <nikita.vyach.ivanov@gmail.com>
Date: Sat Jul 13 01:05:20 2024 +0200
From 1ce61fea52891ed719898c05d616ec20d34f2c73 Mon Sep 17 00:00:00 2001
From: Nikita Ivanov <nikita.vyach.ivanov@gmail.com>
Date: Fri, 21 Mar 2025 21:48:42 +0100
Subject: [PATCH] Add menu command
Add menu command
---
config.def.h | 8 +++
dwl.c | 154 +++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 162 insertions(+)
diff --git a/config.def.h b/config.def.h
index 22d2171..ecd2c67 100644
index 22d2171..a5914ca 100644
--- a/config.def.h
+++ b/config.def.h
@@ -20,6 +20,12 @@ static const float fullscreen_bg[] = {0.1f, 0.1f, 0.1f, 1.0f}; /* You ca
@ -14,7 +18,7 @@ index 22d2171..ecd2c67 100644
+static const Menu menus[] = {
+ /* command feed function action function */
+ { "wmenu -i -l 5 -p Windows", menuwinfeed, menuwinaction },
+ { "wmenu -i -l 10 -p Windows", menuwinfeed, menuwinaction },
+ { "wmenu -i -p Layouts", menulayoutfeed, menulayoutaction },
+};
+
@ -31,10 +35,18 @@ index 22d2171..ecd2c67 100644
{ MODKEY, XKB_KEY_e, togglefullscreen, {0} },
{ MODKEY, XKB_KEY_0, view, {.ui = ~0} },
diff --git a/dwl.c b/dwl.c
index dc0437e..90bb09a 100644
index def2562..04543a6 100644
--- a/dwl.c
+++ b/dwl.c
@@ -241,6 +241,12 @@ typedef struct {
@@ -1,6 +1,7 @@
/*
* See LICENSE file for copyright and license details.
*/
+#include <fcntl.h>
#include <getopt.h>
#include <libinput.h>
#include <linux/input-event-codes.h>
@@ -242,6 +243,12 @@ typedef struct {
struct wl_listener destroy;
} SessionLock;
@ -47,12 +59,12 @@ index dc0437e..90bb09a 100644
/* function declarations */
static void applybounds(Client *c, struct wlr_box *bbox);
static void applyrules(Client *c);
@@ -298,6 +304,12 @@ static void killclient(const Arg *arg);
@@ -302,6 +309,12 @@ static void killclient(const Arg *arg);
static void locksession(struct wl_listener *listener, void *data);
static void mapnotify(struct wl_listener *listener, void *data);
static void maximizenotify(struct wl_listener *listener, void *data);
+static void menu(const Arg *arg);
+static int menuloop(void *data);
+static int menuread(int fd, uint32_t mask, void *data);
+static void menuwinfeed(FILE *f);
+static void menuwinaction(char *line);
+static void menulayoutfeed(FILE *f);
@ -60,27 +72,19 @@ index dc0437e..90bb09a 100644
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,
@@ -408,6 +420,11 @@ static struct wlr_box sgeom;
@@ -413,6 +426,11 @@ static struct wlr_box sgeom;
static struct wl_list mons;
static Monitor *selmon;
+static struct wl_event_source *menu_source;
+static pid_t menu_pid;
+static int menu_fd;
+static const Menu *menu_current;
+static int menu_fd;
+static pid_t menu_pid;
+static struct wl_event_source *menu_source;
+
#ifdef XWAYLAND
static void activatex11(struct wl_listener *listener, void *data);
static void associatex11(struct wl_listener *listener, void *data);
@@ -675,6 +692,7 @@ cleanup(void)
wlr_xwayland_destroy(xwayland);
xwayland = NULL;
#endif
+ wl_event_source_remove(menu_source);
wl_display_destroy_clients(dpy);
if (child_pid > 0) {
kill(-child_pid, SIGTERM);
@@ -1717,6 +1735,136 @@ maximizenotify(struct wl_listener *listener, void *data)
@@ -1768,6 +1786,142 @@ maximizenotify(struct wl_listener *listener, void *data)
wlr_xdg_surface_schedule_configure(c->surface.xdg);
}
@ -88,66 +92,71 @@ index dc0437e..90bb09a 100644
+menu(const Arg *arg)
+{
+ FILE *f;
+ pid_t pid;
+ int fd_right[2], fd_left[2];
+
+ if (!selmon || menu_pid != 0)
+ return;
+
+ menu_current = arg->v;
+ if (menu_current != NULL) {
+ wl_event_source_remove(menu_source);
+ close(menu_fd);
+ kill(menu_pid, SIGTERM);
+ menu_current = NULL;
+ if (!arg->v)
+ return;
+ }
+
+ if (pipe(fd_right) == -1 || pipe(fd_left) == -1)
+ return;
+ if ((pid = fork()) == -1)
+ if ((menu_pid = fork()) == -1)
+ return;
+ if (pid == 0) {
+ if (menu_pid == 0) {
+ close(fd_right[1]);
+ close(fd_left[0]);
+ dup2(fd_right[0], STDIN_FILENO);
+ close(fd_right[0]);
+ dup2(fd_left[1], STDOUT_FILENO);
+ close(fd_left[1]);
+
+ execl("/bin/sh", "/bin/sh", "-c", menu_current->cmd, NULL);
+ execl("/bin/sh", "/bin/sh", "-c", ((Menu *)(arg->v))->cmd, NULL);
+ die("dwl: execl %s failed:", "/bin/sh");
+ }
+
+ close(fd_right[0]);
+ close(fd_left[1]);
+
+ menu_fd = fd_left[0];
+ if (fcntl(menu_fd, F_SETFL, fcntl(menu_fd, F_GETFL) | O_NONBLOCK) == -1)
+ return;
+ if (!(f = fdopen(fd_right[1], "w")))
+ return;
+ menu_current = arg->v;
+ menu_current->feed(f);
+ fclose(f);
+
+ menu_pid = pid;
+ menu_fd = fd_left[0];
+ wl_event_source_timer_update(menu_source, 10);
+ menu_source = wl_event_loop_add_fd(event_loop,
+ menu_fd, WL_EVENT_READABLE, menuread, NULL);
+}
+
+int
+menuloop(void *data)
+menuread(int fd, uint32_t mask, void *data)
+{
+ FILE *f;
+ pid_t pid;
+ char line[256], *s;
+ char *s;
+ int n;
+ static char line[512];
+ static int i = 0;
+
+ /* If process is still running, wait for another 50 ms */
+ if ((pid = waitpid(menu_pid, NULL, WNOHANG)) == 0) {
+ wl_event_source_timer_update(menu_source, 10);
+ if (mask & (WL_EVENT_HANGUP | WL_EVENT_ERROR)) {
+ menu(&(const Arg){ .v = NULL });
+ return 0;
+ }
+
+ menu_pid = 0;
+
+ if (!(f = fdopen(menu_fd, "r")))
+ if ((n = read(menu_fd, line + i, LENGTH(line) - 1 - i)) == -1) {
+ if (errno != EAGAIN)
+ menu(&(const Arg){ .v = NULL });
+ return 0;
+ if (fgets(line, sizeof(line), f)) {
+ if ((s = strchr(line, '\n')))
+ *s = '\0';
+ menu_current->action(line);
+ }
+ fclose(f);
+ line[i + n] = '\0';
+ if (!(s = strchr(line + i, '\n'))) {
+ i += n;
+ return 0;
+ }
+ i = 0;
+ *s = '\0';
+ menu_current->action(line);
+ return 0;
+}
+
@ -155,12 +164,15 @@ index dc0437e..90bb09a 100644
+menuwinfeed(FILE *f)
+{
+ Client *c;
+ const char *title;
+ const char *title, *appid;
+
+ wl_list_for_each(c, &fstack, flink) {
+ if (!(title = client_get_title(c)))
+ continue;
+ fprintf(f, "%s\n", title);
+ fprintf(f, "%s", title);
+ if ((appid = client_get_appid(c)))
+ fprintf(f, " | %s", appid);
+ fputc('\n', f);
+ }
+}
+
@ -168,63 +180,53 @@ index dc0437e..90bb09a 100644
+menuwinaction(char *line)
+{
+ Client *c;
+ Monitor *prevm = selmon;
+ const char *title;
+
+ if (!selmon)
+ return;
+ const char *appid, *title;
+ static char buf[512];
+
+ wl_list_for_each(c, &fstack, flink) {
+ if (!(title = client_get_title(c)))
+ continue;
+ if (strcmp(line, title) == 0)
+ appid = client_get_appid(c);
+ snprintf(buf, LENGTH(buf) - 1, "%s%s%s",
+ title, appid ? " | " : "", appid ? appid : "");
+ if (strcmp(line, buf) == 0)
+ goto found;
+ }
+ return;
+
+found:
+ focusclient(c, 1);
+ wlr_cursor_move(cursor, NULL, selmon->m.x - prevm->m.x , 0);
+ selmon->seltags ^= 1; /* toggle sel tagset */
+ selmon->tagset[selmon->seltags] = c->tags;
+ arrange(selmon);
+ printstatus();
+ if (!c->mon)
+ return;
+ wl_list_remove(&c->flink);
+ wl_list_insert(&fstack, &c->flink);
+ selmon = c->mon;
+ view(&(const Arg){ .ui = c->tags });
+}
+
+void
+menulayoutfeed(FILE *f)
+{
+ unsigned int i;
+ for (i = 0; i < LENGTH(layouts); i++)
+ fprintf(f, "%s\n", layouts[i].symbol);
+ const Layout *l;
+ for (l = layouts; l < END(layouts); l++)
+ fprintf(f, "%s\n", l->symbol);
+}
+
+void
+menulayoutaction(char *line)
+{
+ unsigned int i;
+ Arg a;
+ for (i = 0; i < LENGTH(layouts); i++)
+ if (strcmp(line, layouts[i].symbol) == 0)
+ const Layout *l;
+ for (l = layouts; l < END(layouts); l++)
+ if (strcmp(line, l->symbol) == 0)
+ goto found;
+ return;
+
+found:
+ a.v = &layouts[i];
+ setlayout(&a);
+ setlayout(&(const Arg){ .v = l });
+}
+
void
monocle(Monitor *m)
{
@@ -2576,6 +2724,10 @@ setup(void)
* e.g when running in the x11 backend or the wayland backend and the
* compositor has Xwayland support */
unsetenv("DISPLAY");
+
+ menu_source = wl_event_loop_add_timer(
+ wl_display_get_event_loop(dpy), menuloop, NULL);
+
#ifdef XWAYLAND
/*
* Initialise the XWayland X server.
--
2.49.0

View File

@ -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))

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 KiB

View File

@ -2,6 +2,13 @@
This patch adds an ability to add or change client rules at runtime.
Sometimes it happens that some client rule I've set is not relevant in the
current circumstance and adds major disturbance and annoyance to my work
(e.g., `isfloating` is set or unset and I want the opposite). Changing the rule
is not an option because it will require recompilation and restarting dwl, which
is even worse. Having an option of always being able to change a rule solves
this issue.
The patch only adds one keybinding (`Alt+Shift+R`) to toggle `isfloating`
option. Upon pressing it, dwl will try to find a matching rule for the focused
client and change its `isfloating` setting. If there's no such a rule, a new
@ -23,18 +30,16 @@ setrulenoswallow(const Arg *arg)
}
```
**NOTE**: This patch makes it impossible to have rules with `NULL` title *and*
appid (such a rule is used internally to mark the end of `rules` array).
You can also try [menurule][menurule] patch if you want to be able to edit rules
with dmenu.
**NOTE:** If you happen to apply patches that add new options to the rules, make
sure to update `rule_default` variable in `config.h` as well. This variable is
used for newly created rules.
[swallow]: /dwl/dwl-patches/src/branch/main/patches/swallow
[swallow]: /dwl/dwl-patches/src/branch/main/patches/swallow
[menurule]: /dwl/dwl-patches/src/branch/main/patches/menurule
### Download
- [0.7](/dwl/dwl-patches/raw/branch/main/patches/setrule/setrule.patch)
- [v0.7](/dwl/dwl-patches/raw/branch/main/patches/setrule/setrule.patch)
- [2025-02-14 v0.7](https://codeberg.org/dwl/dwl-patches/raw/commit/268bee3cee239e5bd52cceed88a52bfc21143cc3/patches/setrule/setrule.patch)
### Authors

View File

@ -1,40 +1,28 @@
From 8ac024bd9aebfe73a44069aae5ddd4499e9e1713 Mon Sep 17 00:00:00 2001
From 3c78308f0d74ac6ef112804333f82c098e33bb40 Mon Sep 17 00:00:00 2001
From: Nikita Ivanov <nikita.vyach.ivanov@gmail.com>
Date: Sun, 9 Feb 2025 23:12:09 +0100
Date: Fri, 21 Mar 2025 22:20:54 +0100
Subject: [PATCH] setrule: add/change rules at runtime
---
config.def.h | 9 ++++++++-
dwl.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 64 insertions(+), 2 deletions(-)
config.def.h | 4 ++++
dwl.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 70 insertions(+), 1 deletion(-)
diff --git a/config.def.h b/config.def.h
index 22d2171..666a8c3 100644
index 22d2171..5b05e52 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
@@ -20,6 +20,9 @@ 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 */
+/* Max amount of dynamically added 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] = {
static const Rule rules[] = {
/* 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[] = {
@@ -142,6 +145,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} },
@ -43,7 +31,7 @@ index 22d2171..666a8c3 100644
{ 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..82929ca 100644
index def2562..8beac1f 100644
--- a/dwl.c
+++ b/dwl.c
@@ -290,6 +290,7 @@ static void focusmon(const Arg *arg);
@ -62,23 +50,34 @@ index def2562..82929ca 100644
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)
@@ -413,6 +415,9 @@ static struct wlr_box sgeom;
static struct wl_list mons;
static Monitor *selmon;
+static Rule *drules;
+static size_t druleslen;
+
#ifdef XWAYLAND
static void activatex11(struct wl_listener *listener, void *data);
static void associatex11(struct wl_listener *listener, void *data);
@@ -466,7 +471,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++) {
+ for (r = drules; r < drules + druleslen; 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)
@@ -1472,6 +1477,53 @@ fullscreennotify(struct wl_listener *listener, void *data)
setfullscreen(c, client_wants_fullscreen(c));
}
+Rule *
+getrule(Client *c)
+{
+ Rule *r, *e = NULL;
+ Rule *r;
+ const Rule *e;
+ const char *appid, *title;
+
+ if (!c)
@ -89,40 +88,42 @@ index def2562..82929ca 100644
+ 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;
+ }
+ for (r = drules + druleslen - 1; r >= drules; r--)
+ if ((!r->title || strstr(title, r->title))
+ && (!r->id || strstr(appid, r->id)))
+ goto found;
+
+ if (druleslen >= LENGTH(rules) + RULES_MAX)
+ return NULL; /* No free slots left */
+
+ r = drules + druleslen++;
+
+ /* Use [NULL,NULL] as the default rule if exists */
+ for (e = rules; e < END(rules); e++)
+ if (!e->title && !e->id) {
+ *r = *e;
+ 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);
+
+ /* No default rule found, set reasoble defaults */
+ if (e >= END(rules)) {
+ r->monitor = -1;
+ }
+
+ /* Only set title if appid is unset */
+ if (appid == broken)
+ r->title = strdup(title);
+ else
+ r->id = strdup(appid);
+
+found:
+ return r;
+}
+
void
gpureset(struct wl_listener *listener, void *data)
{
@@ -2417,6 +2463,15 @@ setpsel(struct wl_listener *listener, void *data)
@@ -2417,6 +2469,15 @@ setpsel(struct wl_listener *listener, void *data)
wlr_seat_set_primary_selection(seat, event->source, event->serial);
}
@ -138,6 +139,17 @@ index def2562..82929ca 100644
void
setsel(struct wl_listener *listener, void *data)
{
@@ -2645,6 +2706,10 @@ setup(void)
fprintf(stderr, "failed to setup XWayland X server, continuing without it\n");
}
#endif
+
+ drules = ecalloc(LENGTH(rules) + RULES_MAX, sizeof(Rule));
+ memcpy(drules, rules, sizeof(rules));
+ druleslen = LENGTH(rules);
}
void
--
2.48.1
2.49.0