dwm-noxz

[fork] suckless dwm - personal fork
git clone git://git.noxz.tech/dwm-noxz
Log | Files | Refs | README | LICENSE

commit 4079fc13aaa446101e020d0c50069ed495db6a0a
parent 6ea8b683de816b459da845acd91fca1da933a1f2
Author: Chris Noxz <chris@noxz.tech>
Date:   Tue,  4 Feb 2020 22:13:12 +0100

Implement fifo support, sticky clients, command dispatching, stack management, layout management and pertag support.

Diffstat:
Mconfig.def.h | 56+++++++++++++++++++++++++++++++++++++++++++++++++-------
Mdwm.c | 404++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
2 files changed, 441 insertions(+), 19 deletions(-)

diff --git a/config.def.h b/config.def.h @@ -50,16 +50,16 @@ static const Rule rules[] = { }; /* layout(s) */ -static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */ -static const int nmaster = 0; /* number of clients in master area */ -static const int resizehints = 0; /* 1 means respect size hints in tiled resizals */ +static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */ +static const int nmaster = 0; /* number of clients in master area */ +static const int resizehints = 0; /* 1 means respect size hints in tiled resizals */ static const Layout layouts[] = { /* symbol arrange function */ - { "###", nrowgrid },/* first entry is default */ - { "[]=", tile }, - { "><>", NULL }, /* no layout function means floating behavior */ - { "[M]", monocle }, + [LayoutGrid] = { "###", nrowgrid }, /* default */ + [LayoutTiled] = { "[]=", tile }, + [LayoutMonocle] = { "[M]", monocle }, + [LayoutFloating] = { "><>", NULL }, /* no layout function means floating behavior */ }; /* key definitions */ @@ -133,3 +133,45 @@ static Button buttons[] = { { ClkTagBar, MODKEY, Button3, toggletag, {0} }, }; +static const char *dwmfifo = "/tmp/dwm.fifo"; +static Command commands[] = { + { "spawn ...", spawn, {.i = DispCmdLine} }, + { "quit", quit, {0} }, + { "toggle bar", togglebar, {0} }, + { "focus stack +", focusstack, {.i = +1} }, + { "focus stack -", focusstack, {.i = -1} }, + { "move stack +", movestack, {.i = +1} }, + { "move stack -", movestack, {.i = -1} }, + { "rotate stack +", rotatestack, {.i = +1} }, + { "rotate stack -", rotatestack, {.i = -1} }, + { "inc nmaster +", incnmaster, {.i = +1} }, + { "inc nmaster -", incnmaster, {.i = -1} }, + { "set mfact +", setmfact, {.f = +0.05} }, + { "set mfact -", setmfact, {.f = -0.05} }, + { "zoom", zoom, {0} }, + { "kill client", killclient, {0} }, + + { "set layout grid", setlayout, {.v = &layouts[LayoutGrid]} }, + { "set layout tiled", setlayout, {.v = &layouts[LayoutTiled]} }, + { "set layout float", setlayout, {.v = &layouts[LayoutFloating]} }, + { "set layout monocle", setlayout, {.v = &layouts[LayoutMonocle]} }, + { "toggle monocle", togglelayout, {.v = &layouts[LayoutMonocle]} }, + { "toggle layout", setlayout, {0} }, + { "rotate layout +", rotatelayout, {.i = +1} }, + { "rotate layout -", rotatelayout, {.i = -1} }, + + { "toggle floating", togglefloating, {0} }, + { "toggle sticky", togglesticky, {0} }, + { "focus mon +", focusmon, {.i = +1} }, + { "focus mon -", focusmon, {.i = -1} }, + { "tag mon +", tagmon, {.i = +1} }, + { "tag mon -", tagmon, {.i = -1} }, + + { "view", view, {0} }, + { "view all", view, {.ui = ~0} }, + { "tag all", tag, {.ui = ~0} }, + { "view ...", view, {.i = DispUi} }, + { "toggle view ...", toggleview, {.i = DispUi} }, + { "tag ...", tag, {.i = DispUi} }, + { "toggle tag ...", toggletag, {.i = DispUi} }, +}; diff --git a/dwm.c b/dwm.c @@ -21,6 +21,7 @@ * To understand everything else, start reading main(). */ #include <errno.h> +#include <fcntl.h> #include <locale.h> #include <signal.h> #include <stdarg.h> @@ -28,6 +29,7 @@ #include <stdlib.h> #include <string.h> #include <unistd.h> +#include <sys/select.h> #include <sys/types.h> #include <sys/wait.h> #include <X11/cursorfont.h> @@ -49,7 +51,7 @@ #define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)) #define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \ * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy))) -#define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags])) +#define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags]) || C->issticky) #define LENGTH(X) (sizeof X / sizeof X[0]) #define MOUSEMASK (BUTTONMASK|PointerMotionMask) #define WIDTH(X) ((X)->w + 2 * (X)->bw) @@ -58,6 +60,8 @@ #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) /* enums */ +enum { DispUi, DispCmdLine }; /* dispatch types */ +enum { LayoutGrid, LayoutTiled, LayoutMonocle, LayoutFloating }; /* layouts, first is default */ enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ enum { SchemeNorm, SchemeSel, SchemeTagsNorm, SchemeTagsSel, SchemeTitleNorm, SchemeTitleSel, SchemeStatus }; /* color schemes */ enum { NetSupported, NetWMName, NetWMState, NetWMCheck, @@ -82,6 +86,7 @@ typedef struct { const Arg arg; } Button; +typedef struct Pertag Pertag; typedef struct Monitor Monitor; typedef struct Client Client; struct Client { @@ -92,7 +97,7 @@ struct Client { int basew, baseh, incw, inch, maxw, maxh, minw, minh; int bw, oldbw; unsigned int tags; - int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen; + int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen, issticky; Client *next; Client *snext; Monitor *mon; @@ -130,6 +135,7 @@ struct Monitor { Monitor *next; Window barwin; const Layout *lt[2]; + Pertag *pertag; }; typedef struct { @@ -141,6 +147,12 @@ typedef struct { int monitor; } Rule; +typedef struct { + const char *name; + void (*func)(const Arg *arg); + const Arg arg; +} Command; + /* function declarations */ static void applyrules(Client *c); static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact); @@ -161,9 +173,15 @@ static void destroynotify(XEvent *e); static void detach(Client *c); static void detachstack(Client *c); static Monitor *dirtomon(int dir); +static void strsplit(char *, char ***, const char *); +static void dispatchline(const char*, int, void (*)(const Arg*), const Arg*); +static void dispatchcmd(void); static void drawbar(Monitor *m); static void drawbars(void); +static void enqueue(Client *c); +static void enqueuestack(Client *c); static void enternotify(XEvent *e); +static Bool evpredicate(); static void expose(XEvent *e); static void focus(Client *c); static void focusin(XEvent *e); @@ -192,6 +210,8 @@ static void resize(Client *c, int x, int y, int w, int h, int interact); static void resizeclient(Client *c, int x, int y, int w, int h); static void resizemouse(const Arg *arg); static void restack(Monitor *m); +static void movestack(const Arg *arg); +static void rotatestack(const Arg *arg); static void run(void); static void scan(void); static int sendevent(Client *c, Atom proto); @@ -200,6 +220,8 @@ static void setclientstate(Client *c, long state); static void setfocus(Client *c); static void setfullscreen(Client *c, int fullscreen); static void setlayout(const Arg *arg); +static void togglelayout(const Arg *arg); +static void rotatelayout(const Arg *arg); static void setmfact(const Arg *arg); static void get_vt_colors(void); static int get_luminance(char *rgb); @@ -214,6 +236,7 @@ static void tile(Monitor *); static void nrowgrid(Monitor *); static void togglebar(const Arg *arg); static void togglefloating(const Arg *arg); +static void togglesticky(const Arg *arg); static void toggletag(const Arg *arg); static void toggleview(const Arg *arg); static void unfocus(Client *c, int setfocus); @@ -270,10 +293,20 @@ static Display *dpy; static Drw *drw; static Monitor *mons, *selmon; static Window root, wmcheckwin; +static int fifofd; /* configuration, allows nested code to access above variables */ #include "config.h" +struct Pertag { + unsigned int curtag, prevtag; /* current and previous tag */ + int nmasters[LENGTH(tags) + 1]; /* number of windows in master area */ + float mfacts[LENGTH(tags) + 1]; /* mfacts per tag */ + unsigned int sellts[LENGTH(tags) + 1]; /* selected layouts */ + const Layout *ltidxs[LENGTH(tags) + 1][2]; /* matrix of tags and layouts indexes */ + int showbars[LENGTH(tags) + 1]; /* display bar for the current tag */ +}; + /* compile-time check if all tags fit into an unsigned int bit array. */ struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; @@ -494,6 +527,7 @@ cleanup(void) XSync(dpy, False); XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); XDeleteProperty(dpy, root, netatom[NetActiveWindow]); + close(fifofd); } void @@ -634,6 +668,7 @@ Monitor * createmon(void) { Monitor *m; + unsigned int i; m = ecalloc(1, sizeof(Monitor)); m->tagset[0] = m->tagset[1] = 1; @@ -644,6 +679,18 @@ createmon(void) m->lt[0] = &layouts[0]; m->lt[1] = &layouts[1 % LENGTH(layouts)]; strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); + m->pertag = ecalloc(1, sizeof(Pertag)); + m->pertag->curtag = m->pertag->prevtag = 1; + for (i = 0; i <= LENGTH(tags); i++) { + m->pertag->nmasters[i] = m->nmaster; + m->pertag->mfacts[i] = m->mfact; + + m->pertag->ltidxs[i][0] = m->lt[0]; + m->pertag->ltidxs[i][1] = m->lt[1]; + m->pertag->sellts[i] = m->sellt; + + m->pertag->showbars[i] = m->showbar; + } return m; } @@ -696,6 +743,106 @@ dirtomon(int dir) } void +strsplit(char *str, char ***arr, const char *toks) +{ + char *p = strtok((char *)str, toks); + int len = 0; + + while (p) { + if ((*arr = realloc(*arr, sizeof (char*) * len++)) == NULL) + die("realloc: failed\n"); + (*arr)[len - 1] = p; + p = strtok(NULL, toks); + } + + if ((*arr = realloc(*arr, sizeof (char*) * (len + 1))) == NULL) + die("realloc: failed\n"); + (*arr)[len] = 0; +} + +void +dispatchline(const char *c, int n, void (*func)(const Arg *), const Arg *arg) +{ + char *cmd = NULL; + char *line = NULL; + char **arr = NULL; + Arg a; + + if (strlen(c) < n) + return; + + cmd = malloc(n + 1); + line = malloc(strlen(c) + 1); + + strncpy(cmd, c, n); + strcpy(line, c + n); + strsplit(line, &arr, " "); + + switch (arg->i) { + case DispUi: { + if (sscanf(arr[0], "%d", &a.i)) + a.ui = 1 << a.i; + } break; + case DispCmdLine: + a.v = (const char **)arr; + break; + } + + func(&a); + + free(cmd); + free(line); + free(arr); +} + +void +dispatchcmd(void) +{ + char buf[BUFSIZ]; + char *ptr, *line, *next; + ssize_t n, m; + int i; + + if ((n = read(fifofd, buf, sizeof(buf) - 1)) == -1) + return; + + buf[n] = '\0'; + line = buf; + + /* read each line as a single command */ + while (line) { + next = strchr(line, '\n'); + if (next) + *next = '\0'; + for (i = 0; i < LENGTH(commands); i++) { + m = MAX(strlen(line), strlen(commands[i].name)); + n = (((ptr = strstr(commands[i].name, "..."))) + ? ptr - commands[i].name + : m + ); + if (strncmp(commands[i].name, line, n) == 0) { + if (n != m) + dispatchline( + line, n, + commands[i].func, + &commands[i].arg + ); + else + commands[i].func(&commands[i].arg); + break; + } + } + if (next) + *next = '\n'; + line = next ? next + 1 : NULL; + } + + /* make sure fifo is empty */ + while (errno != EWOULDBLOCK) + read(fifofd, buf, sizeof(buf) - 1); +} + +void drawbar(Monitor *m) { int x, w, mid, sw = 0; @@ -756,6 +903,28 @@ drawbars(void) } void +enqueue(Client *c) +{ + Client *l; + for (l = c->mon->clients; l && l->next; l = l->next); + if (l) { + l->next = c; + c->next = NULL; + } +} + +void +enqueuestack(Client *c) +{ + Client *l; + for (l = c->mon->stack; l && l->snext; l = l->snext); + if (l) { + l->snext = c; + c->snext = NULL; + } +} + +void enternotify(XEvent *e) { Client *c; @@ -774,6 +943,12 @@ enternotify(XEvent *e) focus(c); } +Bool +evpredicate() +{ + return True; +} + void expose(XEvent *e) { @@ -973,7 +1148,7 @@ grabkeys(void) void incnmaster(const Arg *arg) { - selmon->nmaster = MAX(selmon->nmaster + arg->i, 0); + selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag] = MAX(selmon->nmaster + arg->i, 0); arrange(selmon); } @@ -1389,14 +1564,115 @@ restack(Monitor *m) } void +movestack(const Arg *arg) +{ + Client *c = NULL, *p = NULL, *pc = NULL, *i; + + if(arg->i > 0) { + /* find the client after selmon->sel */ + for(c = selmon->sel->next; c && (!ISVISIBLE(c) || c->isfloating); c = c->next); + if(!c) + for(c = selmon->clients; c && (!ISVISIBLE(c) || c->isfloating); c = c->next); + + } + else { + /* find the client before selmon->sel */ + for(i = selmon->clients; i != selmon->sel; i = i->next) + if(ISVISIBLE(i) && !i->isfloating) + c = i; + if(!c) + for(; i; i = i->next) + if(ISVISIBLE(i) && !i->isfloating) + c = i; + } + /* find the client before selmon->sel and c */ + for(i = selmon->clients; i && (!p || !pc); i = i->next) { + if(i->next == selmon->sel) + p = i; + if(i->next == c) + pc = i; + } + + /* swap c and selmon->sel selmon->clients in the selmon->clients list */ + if(c && c != selmon->sel) { + Client *temp = selmon->sel->next==c?selmon->sel:selmon->sel->next; + selmon->sel->next = c->next==selmon->sel?c:c->next; + c->next = temp; + + if(p && p != c) + p->next = c; + if(pc && pc != selmon->sel) + pc->next = selmon->sel; + + if(selmon->sel == selmon->clients) + selmon->clients = c; + else if(c == selmon->clients) + selmon->clients = selmon->sel; + + arrange(selmon); + } +} + +void +rotatestack(const Arg *arg) +{ + Client *c = NULL, *f; + + if (!selmon->sel) + return; + f = selmon->sel; + if (arg->i > 0) { + for (c = nexttiled(selmon->clients); c && nexttiled(c->next); c = nexttiled(c->next)); + if (c){ + detach(c); + attach(c); + detachstack(c); + attachstack(c); + } + } else { + if ((c = nexttiled(selmon->clients))){ + detach(c); + enqueue(c); + detachstack(c); + enqueuestack(c); + } + } + if (c){ + arrange(selmon); + //unfocus(f, 1); + focus(f); + restack(selmon); + } +} + +void run(void) { XEvent ev; + fd_set rfds; + int n; + int dpyfd, maxfd; /* main event loop */ XSync(dpy, False); - while (running && !XNextEvent(dpy, &ev)) - if (handler[ev.type]) - handler[ev.type](&ev); /* call handler */ + dpyfd = ConnectionNumber(dpy); + maxfd = fifofd; + if (dpyfd > maxfd) + maxfd = dpyfd; + maxfd++; + while (running) { + FD_ZERO(&rfds); + FD_SET(fifofd, &rfds); + FD_SET(dpyfd, &rfds); + n = select(maxfd, &rfds, NULL, NULL, NULL); + if (n > 0) { + if (FD_ISSET(fifofd, &rfds)) + dispatchcmd(); + if (FD_ISSET(dpyfd, &rfds)) + while (XCheckIfEvent(dpy, &ev, evpredicate, NULL)) + if (handler[ev.type]) + handler[ev.type](&ev); /* call handler */ + } + } } void @@ -1520,9 +1796,51 @@ void setlayout(const Arg *arg) { if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) - selmon->sellt ^= 1; + selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag] ^= 1; if (arg && arg->v) - selmon->lt[selmon->sellt] = (Layout *)arg->v; + selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt] = (Layout *)arg->v; + strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol); + if (selmon->sel) + arrange(selmon); + else + drawbar(selmon); +} + +void +togglelayout(const Arg *arg) +{ + if (selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt] != (Layout *)arg->v) + setlayout(arg); + else + setlayout(0); +} + +void +rotatelayout(const Arg *arg) +{ + int i, + idx = -1, + max = LENGTH(layouts); + + if (!arg || !arg->i || arg->i == 0) + return; + + for (i = 0; i < max && idx == -1; i++) { + if (selmon->lt[selmon->sellt] == &layouts[i]) + idx = i; + } + + if (idx == -1) + return; + + if (arg->i < 0 && idx-- == 0) + idx = max - 1; + + if (arg->i > 0 && idx++ == max - 1) + idx = 0; + + selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt] = &layouts[idx]; + selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol); if (selmon->sel) arrange(selmon); @@ -1530,6 +1848,7 @@ setlayout(const Arg *arg) drawbar(selmon); } + /* arg > 1.0 will set mfact absolutely */ void setmfact(const Arg *arg) @@ -1541,7 +1860,7 @@ setmfact(const Arg *arg) f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0; if (f < 0.1 || f > 0.9) return; - selmon->mfact = f; + selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag] = f; arrange(selmon); } @@ -1687,8 +2006,13 @@ setup(void) XSelectInput(dpy, root, wa.event_mask); grabkeys(); focus(NULL); -} + /* fifo */ + mkfifo(dwmfifo, 0600); + fifofd = open(dwmfifo, O_RDWR | O_NONBLOCK); + if (fifofd < 0) + die("Failed to open() DWM fifo %s:", dwmfifo); +} void seturgent(Client *c, int urg) @@ -1838,7 +2162,7 @@ nrowgrid(Monitor *m) void togglebar(const Arg *arg) { - selmon->showbar = !selmon->showbar; + selmon->showbar = selmon->pertag->showbars[selmon->pertag->curtag] = !selmon->showbar; updatebarpos(selmon); XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); arrange(selmon); @@ -1863,6 +2187,15 @@ togglefloating(const Arg *arg) } void +togglesticky(const Arg *arg) +{ + if (!selmon->sel) + return; + selmon->sel->issticky = !selmon->sel->issticky; + arrange(selmon); +} + +void toggletag(const Arg *arg) { unsigned int newtags; @@ -1881,9 +2214,31 @@ void toggleview(const Arg *arg) { unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK); + int i; if (newtagset) { selmon->tagset[selmon->seltags] = newtagset; + if (newtagset == ~0) { + selmon->pertag->prevtag = selmon->pertag->curtag; + selmon->pertag->curtag = 0; + } + + /* test if the user did not select the same tag */ + if (!(newtagset & 1 << (selmon->pertag->curtag - 1))) { + selmon->pertag->prevtag = selmon->pertag->curtag; + for (i = 0; !(newtagset & 1 << i); i++) ; + selmon->pertag->curtag = i + 1; + } + + /* apply settings for this view */ + selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag]; + selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag]; + selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; + selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; + selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1]; + + if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag]) + togglebar(NULL); focus(NULL); arrange(selmon); } @@ -2178,11 +2533,36 @@ updatewmhints(Client *c) void view(const Arg *arg) { + int i; + unsigned int tmptag; + if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) return; selmon->seltags ^= 1; /* toggle sel tagset */ - if (arg->ui & TAGMASK) + if (arg->ui & TAGMASK) { selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; + selmon->pertag->prevtag = selmon->pertag->curtag; + + if (arg->ui == ~0) + selmon->pertag->curtag = 0; + else { + for (i = 0; !(arg->ui & 1 << i); i++) ; + selmon->pertag->curtag = i + 1; + } + } else { + tmptag = selmon->pertag->prevtag; + selmon->pertag->prevtag = selmon->pertag->curtag; + selmon->pertag->curtag = tmptag; + } + + selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag]; + selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag]; + selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; + selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; + selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1]; + + if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag]) + togglebar(NULL); focus(NULL); arrange(selmon); }