diff -up -x dwm -x '*.o' -x compile_commands.json dwm-6.3-orig/config.mk dwm-6.3/config.mk --- 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 -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} #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/dwm.c --- dwm-6.3-orig/dwm.c 2024-06-25 01:55:26.771203825 +0200 +++ dwm-6.3/dwm.c 2025-01-16 21:07:02.942737362 +0100 @@ -40,6 +40,7 @@ #include #endif /* XINERAMA */ #include +#include #include "drw.h" #include "util.h" @@ -49,7 +50,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->swallowed == NULL && (C->tags & C->mon->tagset[C->mon->seltags])) #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 interact); @@ -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->mon->wx) && (c->x + (c->w / 2) < c->mon->wx + c->mon->ww)) ? bh : c->mon->my); 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, PropModeAppend, (unsigned char *) &(c->win), 1); XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */ @@ -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 swallow another one. + 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", False); netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); 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,55 @@ 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) { + Client **prev = &c->swallower->swallowed; + for(; *prev != c; prev = &(*prev)->next_swallowed) + ; + *prev = 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 +1925,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); }