summaryrefslogtreecommitdiffstats
path: root/dwl-patches/patches/menu/menu.patch
blob: c1dea4d699564db16e06a2c0b2584ec3dfd7c59a (plain)
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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
From da9861cf0448ca94011470634fd61c3ef2129a25 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

---
 config.def.h |   8 +++
 dwl.c        | 156 +++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 164 insertions(+)

diff --git a/config.def.h b/config.def.h
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
 /* logging */
 static int log_level = WLR_ERROR;
 
+static const Menu menus[] = {
+	/* command                            feed function        action function */
+	{ "wmenu -i -l 10 -p Windows",        menuwinfeed,         menuwinaction    },
+	{ "wmenu -i -p Layouts",              menulayoutfeed,      menulayoutaction },
+};
+
 /* 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 */
@@ -140,6 +146,8 @@ static const Key keys[] = {
 	{ MODKEY,                    XKB_KEY_f,          setlayout,      {.v = &layouts[1]} },
 	{ MODKEY,                    XKB_KEY_m,          setlayout,      {.v = &layouts[2]} },
 	{ 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|WLR_MODIFIER_SHIFT, XKB_KEY_space,      togglefloating, {0} },
 	{ MODKEY,                    XKB_KEY_e,         togglefullscreen, {0} },
 	{ MODKEY,                    XKB_KEY_0,          view,           {.ui = ~0} },
diff --git a/dwl.c b/dwl.c
index def2562..b0e8310 100644
--- a/dwl.c
+++ b/dwl.c
@@ -242,6 +242,12 @@ typedef struct {
 	struct wl_listener destroy;
 } SessionLock;
 
+typedef struct {
+	const char *cmd; /* command to run a menu */
+	void (*feed)(FILE *f); /* feed input to menu */
+	void (*action)(char *line); /* do action based on menu output */
+} Menu;
+
 /* function declarations */
 static void applybounds(Client *c, struct wlr_box *bbox);
 static void applyrules(Client *c);
@@ -302,6 +308,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 menuread(int fd, uint32_t mask, void *data);
+static void menuwinfeed(FILE *f);
+static void menuwinaction(char *line);
+static void menulayoutfeed(FILE *f);
+static void menulayoutaction(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,
@@ -413,6 +425,11 @@ static struct wlr_box sgeom;
 static struct wl_list mons;
 static Monitor *selmon;
 
+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);
@@ -1768,6 +1785,145 @@ maximizenotify(struct wl_listener *listener, void *data)
 		wlr_xdg_surface_schedule_configure(c->surface.xdg);
 }
 
+void
+menu(const Arg *arg)
+{
+	FILE *f;
+	int fd_right[2], fd_left[2];
+
+	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 ((menu_pid = fork()) == -1)
+		return;
+	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 *)(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 (fd_set_nonblock(menu_fd) == -1)
+		return;
+	if (!(f = fdopen(fd_right[1], "w")))
+		return;
+	menu_current = arg->v;
+	menu_current->feed(f);
+	fclose(f);
+	menu_source = wl_event_loop_add_fd(event_loop,
+			menu_fd, WL_EVENT_READABLE, menuread, NULL);
+}
+
+int
+menuread(int fd, uint32_t mask, void *data)
+{
+	char *s;
+	int n;
+	static char line[512];
+	static int i = 0;
+
+	if (mask & (WL_EVENT_HANGUP | WL_EVENT_ERROR)) {
+		i = 0;
+		menu(&(const Arg){ .v = NULL });
+		return 0;
+	}
+	if ((n = read(menu_fd, line + i, LENGTH(line) - 1 - i)) == -1) {
+		if (errno != EAGAIN) {
+			i = 0;
+			menu(&(const Arg){ .v = NULL });
+		}
+		return 0;
+	}
+	line[i + n] = '\0';
+	if (!(s = strchr(line + i, '\n'))) {
+		i += n;
+		return 0;
+	}
+	i = 0;
+	*s = '\0';
+	menu_current->action(line);
+	return 0;
+}
+
+void
+menuwinfeed(FILE *f)
+{
+	Client *c;
+	const char *title, *appid;
+
+	wl_list_for_each(c, &fstack, flink) {
+		if (!(title = client_get_title(c)))
+			continue;
+		fprintf(f, "%s", title);
+		if ((appid = client_get_appid(c)))
+			fprintf(f, " | %s", appid);
+		fputc('\n', f);
+	}
+}
+
+void
+menuwinaction(char *line)
+{
+	Client *c;
+	const char *appid, *title;
+	static char buf[512];
+
+	wl_list_for_each(c, &fstack, flink) {
+		if (!(title = client_get_title(c)))
+			continue;
+		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:
+	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)
+{
+	const Layout *l;
+	for (l = layouts; l < END(layouts); l++)
+		fprintf(f, "%s\n", l->symbol);
+}
+
+void
+menulayoutaction(char *line)
+{
+	const Layout *l;
+	for (l = layouts; l < END(layouts); l++)
+		if (strcmp(line, l->symbol) == 0)
+			goto found;
+	return;
+
+found:
+	setlayout(&(const Arg){ .v = l });
+}
+
 void
 monocle(Monitor *m)
 {
-- 
2.49.0