commit 63bfca78aab30c6aed18bdbab86cee356b6292f0 Author: Nikita Ivanov Date: Sat May 18 19:27:05 2024 +0200 Add menu command diff --git a/config.def.h b/config.def.h index 8f498d2..e7c7654 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 5 -p Windows", menuwinfeed, menuwinaction }, + { "wmenu -i -p Layouts", menulayoutfeed, menulayoutaction }, +}; + static const Rule rules[] = { /* app_id title tags mask isfloating monitor */ /* examples: */ @@ -135,6 +141,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 bf763df..7adaccc 100644 --- a/dwl.c +++ b/dwl.c @@ -239,6 +239,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); @@ -295,6 +301,12 @@ static void locksession(struct wl_listener *listener, void *data); static void maplayersurfacenotify(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 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, @@ -405,6 +417,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; + #ifdef XWAYLAND static void activatex11(struct wl_listener *listener, void *data); static void associatex11(struct wl_listener *listener, void *data); @@ -659,6 +676,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); @@ -1645,6 +1663,136 @@ maximizenotify(struct wl_listener *listener, void *data) wlr_xdg_surface_schedule_configure(c->surface.xdg); } +void +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 (pipe(fd_right) == -1 || pipe(fd_left) == -1) + return; + if ((pid = fork()) == -1) + return; + if (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); + die("dwl: execl %s failed:", "/bin/sh"); + } + + close(fd_right[0]); + close(fd_left[1]); + + if (!(f = fdopen(fd_right[1], "w"))) + return; + menu_current->feed(f); + fclose(f); + + menu_pid = pid; + menu_fd = fd_left[0]; + wl_event_source_timer_update(menu_source, 10); +} + +int +menuloop(void *data) +{ + FILE *f; + pid_t pid; + char line[256], *s; + + /* 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); + return 0; + } + + menu_pid = 0; + + if (!(f = fdopen(menu_fd, "r"))) + return 0; + if (fgets(line, sizeof(line), f)) { + if ((s = strchr(line, '\n'))) + *s = '\0'; + menu_current->action(line); + } + fclose(f); + return 0; +} + +void +menuwinfeed(FILE *f) +{ + Client *c; + const char *title; + + wl_list_for_each(c, &fstack, flink) { + if (!(title = client_get_title(c))) + continue; + fprintf(f, "%s\n", title); + } +} + +void +menuwinaction(char *line) +{ + Client *c; + Monitor *prevm = selmon; + const char *title; + + if (!selmon) + return; + + wl_list_for_each(c, &fstack, flink) { + if (!(title = client_get_title(c))) + continue; + if (strcmp(line, title) == 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(); +} + +void +menulayoutfeed(FILE *f) +{ + unsigned int i; + for (i = 0; i < LENGTH(layouts); i++) + fprintf(f, "%s\n", layouts[i].symbol); +} + +void +menulayoutaction(char *line) +{ + unsigned int i; + Arg a; + for (i = 0; i < LENGTH(layouts); i++) + if (strcmp(line, layouts[i].symbol) == 0) + goto found; + return; + +found: + a.v = &layouts[i]; + setlayout(&a); +} + void monocle(Monitor *m) { @@ -2510,6 +2658,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.