mirror of
				https://codeberg.org/dwl/dwl-patches.git
				synced 2025-10-31 12:04:23 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			228 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			228 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
| 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
 | |
| 
 | 
