| [dwm][patch][betterswallow] Add patch - sites - public wiki contents of suckles… | |
| git clone git://git.suckless.org/sites | |
| Log | |
| Files | |
| Refs | |
| --- | |
| commit 97b34849b8179f4d669569b2756a0e45415457dd | |
| parent 173810e61fec7eb56b7e94b260678ab71375a840 | |
| Author: Fishhh <[email protected]> | |
| Date: Sun, 15 Dec 2024 13:09:29 +0100 | |
| [dwm][patch][betterswallow] Add patch | |
| This patch is yet another take on swallowing, this time with a PID-based | |
| mechanism for detecting swallowed windows and the simple IPC mechanism of | |
| a ClientMessage. | |
| The PID-based mechanism is, in my opinion, significantly better than the | |
| crude string matching mechanisms of `dynamicswallow`, not to mention the | |
| `devour` program which just unmaps the active window. | |
| Since IPC is handled with a ClientMessage, the code for IPC is | |
| significantly simpler than alternative approaches and is entirely | |
| self-contained in this patch. | |
| Diffstat: | |
| A dwm.suckless.org/patches/betterswa… | 277 +++++++++++++++++++++++++++… | |
| A dwm.suckless.org/patches/betterswa… | 56 +++++++++++++++++++++++++++… | |
| 2 files changed, 333 insertions(+), 0 deletions(-) | |
| --- | |
| diff --git a/dwm.suckless.org/patches/betterswallow/dwm-betterswallow-20241215-… | |
| @@ -0,0 +1,277 @@ | |
| +diff -up -x dwm -x '*.o' -x compile_commands.json dwm-6.3-orig/config.mk dwm-6… | |
| +--- dwm-6.3-orig/config.mk 2024-06-25 01:55:26.769203813 +0200 | |
| ++++ dwm-6.3/config.mk 2024-12-15 01:12:39.132847648 +0100 | |
| +@@ -24,6 +24,8 @@ FREETYPEINC = /usr/include/freetype2 | |
| + INCS = -I${X11INC} -I${FREETYPEINC} | |
| + LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} | |
| + | |
| ++LIBS += -lXRes | |
| ++ | |
| + # flags | |
| + CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L -DVERSIO… | |
| + #CFLAGS = -g -std=c99 -pedantic -Wall -O0 ${INCS} ${CPPFLAGS} | |
| +diff -up -x dwm -x '*.o' -x compile_commands.json dwm-6.3-orig/dwm.c dwm-6.3/d… | |
| +--- dwm-6.3-orig/dwm.c 2024-06-25 01:55:26.771203825 +0200 | |
| ++++ dwm-6.3/dwm.c 2024-12-15 01:44:35.963138226 +0100 | |
| +@@ -40,6 +40,7 @@ | |
| + #include <X11/extensions/Xinerama.h> | |
| + #endif /* XINERAMA */ | |
| + #include <X11/Xft/Xft.h> | |
| ++#include <X11/extensions/XRes.h> | |
| + | |
| + #include "drw.h" | |
| + #include "util.h" | |
| +@@ -49,7 +50,7 @@ | |
| + #define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|… | |
| + #define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x… | |
| + * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y… | |
| +-#define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags])) | |
| ++#define ISVISIBLE(C) (C->swallowed == NULL && (C->tags & C->mon->t… | |
| + #define LENGTH(X) (sizeof X / sizeof X[0]) | |
| + #define MOUSEMASK (BUTTONMASK|PointerMotionMask) | |
| + #define WIDTH(X) ((X)->w + 2 * (X)->bw) | |
| +@@ -93,6 +94,11 @@ struct Client { | |
| + int bw, oldbw; | |
| + unsigned int tags; | |
| + int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen; | |
| ++ | |
| ++ Client *swallower; | |
| ++ Client *swallowed; | |
| ++ Client *next_swallowed; | |
| ++ | |
| + Client *next; | |
| + Client *snext; | |
| + Monitor *mon; | |
| +@@ -141,6 +147,12 @@ typedef struct { | |
| + int monitor; | |
| + } Rule; | |
| + | |
| ++typedef struct SwallowDef { | |
| ++ pid_t pid; | |
| ++ Client *swallower; | |
| ++ struct SwallowDef *next; | |
| ++} SwallowDef; | |
| ++ | |
| + /* function declarations */ | |
| + static void applyrules(Client *c); | |
| + static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int inte… | |
| +@@ -260,6 +272,8 @@ static void (*handler[LASTEvent]) (XEven | |
| + [PropertyNotify] = propertynotify, | |
| + [UnmapNotify] = unmapnotify | |
| + }; | |
| ++static Atom swallow_atom; | |
| ++static SwallowDef *swallowlist; | |
| + static Atom wmatom[WMLast], netatom[NetLast]; | |
| + static int running = 1; | |
| + static Cur *cursor[CurLast]; | |
| +@@ -400,6 +414,69 @@ arrangemon(Monitor *m) | |
| + m->lt[m->sellt]->arrange(m); | |
| + } | |
| + | |
| ++pid_t | |
| ++wintopid(Window window) { | |
| ++ XResClientIdSpec spec; | |
| ++ spec.client = window; | |
| ++ spec.mask = XRES_CLIENT_ID_XID; | |
| ++ | |
| ++ long count; | |
| ++ XResClientIdValue *output; | |
| ++ XResQueryClientIds(dpy, 1, &spec, &count, &output); | |
| ++ | |
| ++ pid_t pid = -1; | |
| ++ | |
| ++ for (int i = 0; i < count; ++i) | |
| ++ if (output[i].spec.mask == XRES_CLIENT_ID_PID_MASK) { | |
| ++ pid = *(pid_t *)output[i].value; | |
| ++ break; | |
| ++ } | |
| ++ | |
| ++ XResClientIdsDestroy(count, output); | |
| ++ | |
| ++ return pid; | |
| ++} | |
| ++ | |
| ++void | |
| ++copyclientpos(Client *dst, Client *src) { | |
| ++ dst->bw = src->bw; | |
| ++ resizeclient(dst, src->x, src->y, src->w, src->h); | |
| ++ dst->oldx = src->oldx; | |
| ++ dst->oldy = src->oldy; | |
| ++ dst->oldw = src->oldw; | |
| ++ dst->oldh = src->oldh; | |
| ++ dst->oldbw = src->oldbw; | |
| ++ dst->oldstate = src->oldstate; | |
| ++ dst->isfullscreen = src->isfullscreen; | |
| ++ dst->isfloating = src->isfloating; | |
| ++ dst->tags = src->tags; | |
| ++ dst->mon = src->mon; | |
| ++} | |
| ++ | |
| ++void | |
| ++checkswallowed(Client *c) { | |
| ++ pid_t pid = wintopid(c->win); | |
| ++ | |
| ++ if(pid < 0) return; | |
| ++ for(SwallowDef *sd = swallowlist; sd != NULL; sd = sd->next) { | |
| ++ if(pid == sd->pid) { | |
| ++ c->swallower = sd->swallower; | |
| ++ copyclientpos(c, sd->swallower); | |
| ++ | |
| ++ c->next_swallowed = c->swallower->swallowed; | |
| ++ c->swallower->swallowed = c; | |
| ++ | |
| ++ c->next = c->swallower->next; | |
| ++ c->swallower->next = c; | |
| ++ | |
| ++ c->snext = c->swallower->snext; | |
| ++ c->swallower->snext = c; | |
| ++ | |
| ++ return; | |
| ++ } | |
| ++ } | |
| ++} | |
| ++ | |
| + void | |
| + attach(Client *c) | |
| + { | |
| +@@ -526,7 +603,15 @@ clientmessage(XEvent *e) | |
| + } else if (cme->message_type == netatom[NetActiveWindow]) { | |
| + if (c != selmon->sel && !c->isurgent) | |
| + seturgent(c, 1); | |
| ++ } else if(cme->message_type == swallow_atom) { | |
| ++ SwallowDef *node = ecalloc(1, sizeof(SwallowDef)); | |
| ++ node->pid = cme->data.l[0]; | |
| ++ node->swallower = c; | |
| ++ node->next = swallowlist; | |
| ++ swallowlist = node; | |
| ++ return; | |
| + } | |
| ++ | |
| + } | |
| + | |
| + void | |
| +@@ -1052,6 +1137,7 @@ manage(Window w, XWindowAttributes *wa) | |
| + c->y = MAX(c->y, ((c->mon->by == c->mon->my) && (c->x + (c->w / 2) >=… | |
| + && (c->x + (c->w / 2) < c->mon->wx + c->mon->ww)) ? bh : c->m… | |
| + c->bw = borderpx; | |
| ++ checkswallowed(c); | |
| + | |
| + wc.border_width = c->bw; | |
| + XConfigureWindow(dpy, w, CWBorderWidth, &wc); | |
| +@@ -1066,8 +1152,10 @@ manage(Window w, XWindowAttributes *wa) | |
| + c->isfloating = c->oldstate = trans != None || c->isfixed; | |
| + if (c->isfloating) | |
| + XRaiseWindow(dpy, c->win); | |
| +- attach(c); | |
| +- attachstack(c); | |
| ++ if(!c->swallower) { | |
| ++ attach(c); | |
| ++ attachstack(c); | |
| ++ } | |
| + XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, Pro… | |
| + (unsigned char *) &(c->win), 1); | |
| + XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* s… | |
| +@@ -1164,6 +1252,10 @@ movemouse(const Arg *arg) | |
| + case Expose: | |
| + case MapRequest: | |
| + handler[ev.type](&ev); | |
| ++ | |
| ++ // A MapRequest could've caused the current window to… | |
| ++ if(c->swallowed) | |
| ++ c = c->swallowed; | |
| + break; | |
| + case MotionNotify: | |
| + if ((ev.xmotion.time - lasttime) <= (1000 / 60)) | |
| +@@ -1318,6 +1410,9 @@ resizemouse(const Arg *arg) | |
| + case Expose: | |
| + case MapRequest: | |
| + handler[ev.type](&ev); | |
| ++ | |
| ++ if(c->swallowed) | |
| ++ c = c->swallowed; | |
| + break; | |
| + case MotionNotify: | |
| + if ((ev.xmotion.time - lasttime) <= (1000 / 60)) | |
| +@@ -1566,6 +1661,7 @@ setup(void) | |
| + netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", Fa… | |
| + netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYP… | |
| + netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False); | |
| ++ swallow_atom = XInternAtom(dpy, "_BETTER_SWALLOW", False); | |
| + /* init cursors */ | |
| + cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr); | |
| + cursor[CurResize] = drw_cur_create(drw, XC_sizing); | |
| +@@ -1583,6 +1679,8 @@ setup(void) | |
| + PropModeReplace, (unsigned char *) &wmcheckwin, 1); | |
| + XChangeProperty(dpy, wmcheckwin, netatom[NetWMName], utf8string, 8, | |
| + PropModeReplace, (unsigned char *) "dwm", 3); | |
| ++ XChangeProperty(dpy, root, swallow_atom, utf8string, 8, | |
| ++ PropModeReplace, (unsigned char *) "supported", 9); | |
| + XChangeProperty(dpy, root, netatom[NetWMCheck], XA_WINDOW, 32, | |
| + PropModeReplace, (unsigned char *) &wmcheckwin, 1); | |
| + /* EWMH support per view */ | |
| +@@ -1766,11 +1864,52 @@ unfocus(Client *c, int setfocus) | |
| + } | |
| + | |
| + void | |
| ++deleteswallower(Client *c) { | |
| ++ SwallowDef **prevnext = &swallowlist; | |
| ++ for(SwallowDef *sd = swallowlist; sd != NULL;) { | |
| ++ if(sd->swallower == c) { | |
| ++ SwallowDef *next = sd->next; | |
| ++ *prevnext = next; | |
| ++ free(sd); | |
| ++ sd = next; | |
| ++ } else { | |
| ++ prevnext = &sd->next; | |
| ++ sd = sd->next; | |
| ++ } | |
| ++ } | |
| ++ | |
| ++ Client *sw = c->swallowed; | |
| ++ while(sw) { | |
| ++ sw->swallower = NULL; | |
| ++ Client *next = sw->next_swallowed; | |
| ++ sw->next_swallowed = NULL; | |
| ++ sw = next; | |
| ++ } | |
| ++} | |
| ++ | |
| ++void | |
| + unmanage(Client *c, int destroyed) | |
| + { | |
| + Monitor *m = c->mon; | |
| + XWindowChanges wc; | |
| + | |
| ++ if(c->swallower) { | |
| ++ c->swallower->swallowed = c->next_swallowed; | |
| ++ c->next_swallowed = NULL; | |
| ++ | |
| ++ if(c->swallower->swallowed == NULL) { | |
| ++ detach(c->swallower); | |
| ++ detachstack(c->swallower); | |
| ++ | |
| ++ c->swallower->next = c->next; | |
| ++ c->next = c->swallower; | |
| ++ c->swallower->snext = c->snext; | |
| ++ c->snext = c->swallower; | |
| ++ | |
| ++ copyclientpos(c->swallower, c); | |
| ++ } | |
| ++ } | |
| ++ | |
| + detach(c); | |
| + detachstack(c); | |
| + if (!destroyed) { | |
| +@@ -1783,9 +1922,10 @@ unmanage(Client *c, int destroyed) | |
| + XSync(dpy, False); | |
| + XSetErrorHandler(xerror); | |
| + XUngrabServer(dpy); | |
| +- } | |
| ++ } else deleteswallower(c); | |
| ++ if(c->swallower) focus(c->swallower); | |
| ++ else focus(NULL); | |
| + free(c); | |
| +- focus(NULL); | |
| + updateclientlist(); | |
| + arrange(m); | |
| + } | |
| diff --git a/dwm.suckless.org/patches/betterswallow/index.md b/dwm.suckless.org… | |
| @@ -0,0 +1,56 @@ | |
| +# betterswallow | |
| + | |
| +## Description | |
| + | |
| +This patch adds "window swallowing" to dwm, as a few existing patches already … | |
| +but with another take on dynamic swallowing. In contrast to the existing | |
| +[dynamicswallow](https://dwm.suckless.org/patches/dynamicswallow/) patch, | |
| +`betterswallow` uses PID-based swallowing and a non-standard X11 ClientMessage | |
| +for communication. As with `dynamicswallow`, an external tool `better-swallow` | |
| +is required to use it. `better-swallow` is a separate tool that does not | |
| +*require* this patch but is only enhanced by its presence, without the patch | |
| +it falls back to the devour mechanism of unmapping the parent window itself. | |
| + | |
| +Development of this patch should only happen on the | |
| +[GitHub repository](https://github.com/afishhh/better-swallow) of | |
| +`better-swallow`, if you find any bugs feel free to open an issue. | |
| + | |
| +Currently only a version for dwm-6.3 exists, if you wish to update the patch to | |
| +a newer version of DWM you can open a pull request to the `better-swallow` | |
| +repository. | |
| + | |
| +## Download | |
| + | |
| +- [dwm-betterswallow-20241215-6.3.diff](dwm-betterswallow-20241215-6.3.diff) | |
| +- [better-swallow](https://github.com/afishhh/better-swallow) | |
| + | |
| +## Patching | |
| + | |
| +The patch requires the `Xres` library, make sure you have it in your build env… | |
| + | |
| +Unlike `dynamicswallow` you don't have to worry about any IPC patches, because | |
| +this patch uses the simpler method of a ClientMessage for registering swallowe… | |
| + | |
| +If you have any patches that store geometry parameters in the `Client` struct, | |
| +make sure they're copied in the `copyclientpos` function. | |
| + | |
| +## Usage | |
| + | |
| +Run any graphical program you want to be swallowed as | |
| +`better-swallow <CMD>`, this will cause any windows it spawns to replace the | |
| +parent window. | |
| + | |
| +Since `better-swallow` is a pretty long name I recommend creating an alias suc… | |
| +`bs`. | |
| + | |
| +## Limitations | |
| + | |
| +- Due to the reliance on the `Xres` extension and PIDs, this will fail if the … | |
| +is not running on the same machine as `better-swallow` and possibly add nonsen… | |
| +entries to the "swallow queue". | |
| +- If a swallowed process opens the window deeper in the process tree it will n… | |
| +swallowed. This may be fixed in the future by traversing the whole process cha… | |
| +instead of just one step up. Open an issue if you actually encounter this. | |
| + | |
| +## Author | |
| +- Hubert Głuchowski (<[email protected]>) |