From a37879a10bbca1591a2677ddecd641a4bcb8ffbd Mon Sep 17 00:00:00 2001 From: Santtu Lakkala Date: Thu, 13 Jul 2023 15:48:14 +0300 Subject: [PATCH] Command FIFO for dwm Builds on the previous version of dwmfifo, but: - proper buffering and line detection - basic argument parsing - new commands to show a window by xid or name pattern --- config.def.h | 26 +++++++ dwm.c | 200 ++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 223 insertions(+), 3 deletions(-) diff --git a/config.def.h b/config.def.h index 9efa774..fc4db01 100644 --- a/config.def.h +++ b/config.def.h @@ -114,3 +114,29 @@ static const Button buttons[] = { { ClkTagBar, MODKEY, Button3, toggletag, {0} }, }; +static const char *dwmfifo = "/tmp/dwm.fifo"; +static Command commands[] = { + { "dmenu", spawn, {.v = dmenucmd} }, + { "term", spawn, {.v = termcmd} }, + { "quit", quit, {0} }, + { "togglebar", togglebar, {0} }, + { "focusstack", focusstack, .parse = parseplusminus }, + { "incnmaster", incnmaster, .parse = parseplusminus }, + { "setmfact", setmfact, .parse = parseplusminus }, + { "zoom", zoom, {0} }, + { "killclient", killclient, {0} }, + { "setlayout-tiled", setlayout, {.v = &layouts[0]} }, + { "setlayout-float", setlayout, {.v = &layouts[1]} }, + { "setlayout-mono", setlayout, {.v = &layouts[2]} }, + { "togglelayout", setlayout, {0} }, + { "togglefloating", togglefloating, {0} }, + { "viewwin", viewwin, .parse = parsexid }, + { "viewname", viewname, .parse = parsestr }, + { "viewall", view, {.ui = ~0} }, + { "focusmon", focusmon, .parse = parseplusminus }, + { "tagmon", tagmon, .parse = parseplusminus }, + { "view", view, .parse = parsetag }, + { "toggleview", toggleview, .parse = parsetag }, + { "tag", tag, .parse = parsetag }, + { "toggletag", toggletag, .parse = parsetag }, +}; diff --git a/dwm.c b/dwm.c index f1d86b2..202ed6a 100644 --- a/dwm.c +++ b/dwm.c @@ -21,6 +21,7 @@ * To understand everything else, start reading main(). */ #include +#include #include #include #include @@ -28,6 +29,8 @@ #include #include #include +#include +#include #include #include #include @@ -141,6 +144,13 @@ typedef struct { int monitor; } Rule; +typedef struct { + const char *name; + void (*func)(const Arg *arg); + const Arg arg; + int (*parse)(Arg *arg, const char *s, size_t len); +} Command; + /* function declarations */ static void applyrules(Client *c); static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact); @@ -161,6 +171,7 @@ static void destroynotify(XEvent *e); static void detach(Client *c); static void detachstack(Client *c); static Monitor *dirtomon(int dir); +static void dispatchcmd(void); static void drawbar(Monitor *m); static void drawbars(void); static void enternotify(XEvent *e); @@ -227,6 +238,8 @@ static void updatetitle(Client *c); static void updatewindowtype(Client *c); static void updatewmhints(Client *c); static void view(const Arg *arg); +static void viewwin(const Arg *arg); +static void viewname(const Arg *arg); static Client *wintoclient(Window w); static Monitor *wintomon(Window w); static int xerror(Display *dpy, XErrorEvent *ee); @@ -267,10 +280,63 @@ static Display *dpy; static Drw *drw; static Monitor *mons, *selmon; static Window root, wmcheckwin; +static int fifofd; + +static int parsetag(Arg *a, const char *s, size_t len); +static int parseplusminus(Arg *a, const char *s, size_t len); +static int parsexid(Arg *a, const char *s, size_t len); +static int parsestr(Arg *a, const char *s, size_t len); /* configuration, allows nested code to access above variables */ #include "config.h" +static int parsetag(Arg *a, const char *s, size_t len) +{ + char *end; + unsigned int rv = strtoul(s, &end, 10); + if (end == s) + a->ui = 0; + else if (rv > LENGTH(tags)) + return 0; + else if (rv == 0) + a->ui = ~0U; + else + a->ui = 1U << (rv - 1); + + return 1; +} + +static int parseplusminus(Arg *a, const char *s, size_t len) +{ + if (*s == '+') + a->i = +1; + else if (*s == '-') + a->i = -1; + else + return 0; + return 1; +} + +static int parsexid(Arg *a, const char *s, size_t len) +{ + char *end; + unsigned long long sv = strtoull(s, &end, 0); + + if (end == s) + return 0; + + a->v = (void *)(intptr_t)sv; + return 1; +} + +static int parsestr(Arg *a, const char *s, size_t len) +{ + while (*s == ' ' || *s == '\t') + s++; + a->v = s; + return 1; +} + /* compile-time check if all tags fit into an unsigned int bit array. */ struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; @@ -494,6 +560,7 @@ cleanup(void) XSync(dpy, False); XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); XDeleteProperty(dpy, root, netatom[NetActiveWindow]); + close(fifofd); } void @@ -695,6 +762,71 @@ dirtomon(int dir) return m; } +static const char * +strnprefix(const char *haystack, size_t hlen, const char *needle) +{ + while (*needle && hlen--) { + if (*haystack++ != *needle++) + return 0; + } + + if (*needle) + return NULL; + return haystack; +} + +void +dispatchcmd(void) +{ + static char buf[BUFSIZ]; + static char * const bend = 1[&buf]; + static char *bw = buf; + static int longline = 0; + ssize_t n; + char *nl; + char *pl = buf; + char *dend; + Command *i; + + n = read(fifofd, bw, bend - bw); + if (n == -1) + die("Failed to read() from DWM fifo %s:", dwmfifo); + dend = bw + n; + + if (longline) { + if (!(nl = memchr(bw, '\n', dend - bw))) + return; + bw = pl = nl + 1; + longline = 0; + } + + while ((nl = memchr(bw, '\n', dend - bw))) { + for (i = commands; i < 1[&commands]; i++) { + const char *arg; + + if (!(arg = strnprefix(pl, nl - pl, i->name))) + continue; + *nl = '\0'; + if (i->parse) { + Arg a; + if (i->parse(&a, arg, nl - arg)) + i->func(&a); + } else { + i->func(&i->arg); + } + + break; + } + bw = pl = nl + 1; + } + + memmove(buf, pl, dend - pl); + bw = dend - pl + buf; + + if (bw == bend) + bw = buf; +} + void drawbar(Monitor *m) { @@ -1379,15 +1511,31 @@ restack(Monitor *m) while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); } +static Bool evpredicate() +{ + return True; +} + void run(void) { XEvent ev; + struct pollfd fds[2] = { + { .events = POLLIN }, + { .fd = fifofd, .events = POLLIN } + }; /* main event loop */ XSync(dpy, False); - while (running && !XNextEvent(dpy, &ev)) - if (handler[ev.type]) - handler[ev.type](&ev); /* call handler */ + fds[0].fd = ConnectionNumber(dpy); + while (running) { + (void)poll(fds, 1[&fds] - fds, -1); + if (fds[1].revents & POLLIN) + dispatchcmd(); + if (fds[0].revents & POLLIN) + while (XCheckIfEvent(dpy, &ev, evpredicate, NULL)) + if (handler[ev.type]) + handler[ev.type](&ev); /* call handler */ + } } void @@ -1611,6 +1759,9 @@ setup(void) XSelectInput(dpy, root, wa.event_mask); grabkeys(); focus(NULL); + fifofd = open(dwmfifo, O_RDONLY | O_CLOEXEC | O_NONBLOCK); + if (fifofd < 0) + die("Failed to open() DWM fifo %s:", dwmfifo); } void @@ -2062,6 +2213,49 @@ view(const Arg *arg) arrange(selmon); } +void +viewclient(Client *c) +{ + if (!(c->tags & c->mon->tagset[c->mon->seltags])) + view(&(Arg){ .ui = c->tags }); + focus(c); +} + +void +viewwin(const Arg *arg) +{ + Client *c = wintoclient((Window)(intptr_t)arg->v); + + if (!c) + return; + + viewclient(c); +} + +Client * +pattoclient(const char *pattern) +{ + Client *c; + Monitor *m; + + for (m = mons; m; m = m->next) + for (c = m->clients; c; c = c->next) + if (!fnmatch(pattern, c->name, 0)) + return c; + return NULL; +} + +void +viewname(const Arg *arg) +{ + Client *c = pattoclient(arg->v); + + if (!c) + return; + + viewclient(c); +} + Client * wintoclient(Window w) { -- 2.25.1