Introduction
Introduction Statistics Contact Development Disclaimer Help
[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]>)
You are viewing proxied material from suckless.org. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.