1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
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
|