dwm-betterswallow-6.6.diff (8351B)
1 diff -up -x dwm -x '*.o' -x compile_commands.json dwm-6.6-orig/config.mk dwm-6.6/config.mk 2 --- dwm-6.6-orig/config.mk 2025-12-22 13:50:11.321481527 +0100 3 +++ dwm-6.6/config.mk 2025-12-22 13:51:06.720057380 +0100 4 @@ -25,6 +25,8 @@ FREETYPEINC = /usr/include/freetype2 5 INCS = -I${X11INC} -I${FREETYPEINC} 6 LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} 7 8 +LIBS += -lXRes 9 + 10 # flags 11 CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} 12 #CFLAGS = -g -std=c99 -pedantic -Wall -O0 ${INCS} ${CPPFLAGS} 13 diff -up -x dwm -x '*.o' -x compile_commands.json dwm-6.6-orig/dwm.c dwm-6.6/dwm.c 14 --- dwm-6.6-orig/dwm.c 2025-12-22 13:50:11.322481536 +0100 15 +++ dwm-6.6/dwm.c 2025-12-22 13:52:08.649643404 +0100 16 @@ -40,6 +40,7 @@ 17 #include <X11/extensions/Xinerama.h> 18 #endif /* XINERAMA */ 19 #include <X11/Xft/Xft.h> 20 +#include <X11/extensions/XRes.h> 21 22 #include "drw.h" 23 #include "util.h" 24 @@ -49,7 +50,7 @@ 25 #define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)) 26 #define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \ 27 * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy))) 28 -#define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags])) 29 +#define ISVISIBLE(C) (C->swallowed == NULL && (C->tags & C->mon->tagset[C->mon->seltags])) 30 #define MOUSEMASK (BUTTONMASK|PointerMotionMask) 31 #define WIDTH(X) ((X)->w + 2 * (X)->bw) 32 #define HEIGHT(X) ((X)->h + 2 * (X)->bw) 33 @@ -92,6 +93,11 @@ struct Client { 34 int bw, oldbw; 35 unsigned int tags; 36 int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen; 37 + 38 + Client *swallower; 39 + Client *swallowed; 40 + Client *next_swallowed; 41 + 42 Client *next; 43 Client *snext; 44 Monitor *mon; 45 @@ -140,6 +146,12 @@ typedef struct { 46 int monitor; 47 } Rule; 48 49 +typedef struct SwallowDef { 50 + pid_t pid; 51 + Client *swallower; 52 + struct SwallowDef *next; 53 +} SwallowDef; 54 + 55 /* function declarations */ 56 static void applyrules(Client *c); 57 static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact); 58 @@ -258,6 +270,8 @@ static void (*handler[LASTEvent]) (XEven 59 [PropertyNotify] = propertynotify, 60 [UnmapNotify] = unmapnotify 61 }; 62 +static Atom swallow_atom; 63 +static SwallowDef *swallowlist; 64 static Atom wmatom[WMLast], netatom[NetLast]; 65 static int running = 1; 66 static Cur *cursor[CurLast]; 67 @@ -400,6 +414,69 @@ arrangemon(Monitor *m) 68 m->lt[m->sellt]->arrange(m); 69 } 70 71 +pid_t 72 +wintopid(Window window) { 73 + XResClientIdSpec spec; 74 + spec.client = window; 75 + spec.mask = XRES_CLIENT_ID_XID; 76 + 77 + long count; 78 + XResClientIdValue *output; 79 + XResQueryClientIds(dpy, 1, &spec, &count, &output); 80 + 81 + pid_t pid = -1; 82 + 83 + for (int i = 0; i < count; ++i) 84 + if (output[i].spec.mask == XRES_CLIENT_ID_PID_MASK) { 85 + pid = *(pid_t *)output[i].value; 86 + break; 87 + } 88 + 89 + XResClientIdsDestroy(count, output); 90 + 91 + return pid; 92 +} 93 + 94 +void 95 +copyclientpos(Client *dst, Client *src) { 96 + dst->bw = src->bw; 97 + resizeclient(dst, src->x, src->y, src->w, src->h); 98 + dst->oldx = src->oldx; 99 + dst->oldy = src->oldy; 100 + dst->oldw = src->oldw; 101 + dst->oldh = src->oldh; 102 + dst->oldbw = src->oldbw; 103 + dst->oldstate = src->oldstate; 104 + dst->isfullscreen = src->isfullscreen; 105 + dst->isfloating = src->isfloating; 106 + dst->tags = src->tags; 107 + dst->mon = src->mon; 108 +} 109 + 110 +void 111 +checkswallowed(Client *c) { 112 + pid_t pid = wintopid(c->win); 113 + 114 + if(pid < 0) return; 115 + for(SwallowDef *sd = swallowlist; sd != NULL; sd = sd->next) { 116 + if(pid == sd->pid) { 117 + c->swallower = sd->swallower; 118 + copyclientpos(c, sd->swallower); 119 + 120 + c->next_swallowed = c->swallower->swallowed; 121 + c->swallower->swallowed = c; 122 + 123 + c->next = c->swallower->next; 124 + c->swallower->next = c; 125 + 126 + c->snext = c->swallower->snext; 127 + c->swallower->snext = c; 128 + 129 + return; 130 + } 131 + } 132 +} 133 + 134 void 135 attach(Client *c) 136 { 137 @@ -527,7 +604,15 @@ clientmessage(XEvent *e) 138 } else if (cme->message_type == netatom[NetActiveWindow]) { 139 if (c != selmon->sel && !c->isurgent) 140 seturgent(c, 1); 141 + } else if(cme->message_type == swallow_atom) { 142 + SwallowDef *node = ecalloc(1, sizeof(SwallowDef)); 143 + node->pid = cme->data.l[0]; 144 + node->swallower = c; 145 + node->next = swallowlist; 146 + swallowlist = node; 147 + return; 148 } 149 + 150 } 151 152 void 153 @@ -714,9 +799,11 @@ drawbar(Monitor *m) 154 } 155 156 for (c = m->clients; c; c = c->next) { 157 - occ |= c->tags; 158 - if (c->isurgent) 159 - urg |= c->tags; 160 + if (!c->swallowed) { 161 + occ |= c->tags; 162 + if (c->isurgent) 163 + urg |= c->tags; 164 + } 165 } 166 x = 0; 167 for (i = 0; i < LENGTH(tags); i++) { 168 @@ -1059,6 +1146,7 @@ manage(Window w, XWindowAttributes *wa) 169 c->x = MAX(c->x, c->mon->wx); 170 c->y = MAX(c->y, c->mon->wy); 171 c->bw = borderpx; 172 + checkswallowed(c); 173 174 wc.border_width = c->bw; 175 XConfigureWindow(dpy, w, CWBorderWidth, &wc); 176 @@ -1073,8 +1161,10 @@ manage(Window w, XWindowAttributes *wa) 177 c->isfloating = c->oldstate = trans != None || c->isfixed; 178 if (c->isfloating) 179 XRaiseWindow(dpy, c->win); 180 - attach(c); 181 - attachstack(c); 182 + if(!c->swallower) { 183 + attach(c); 184 + attachstack(c); 185 + } 186 XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, 187 (unsigned char *) &(c->win), 1); 188 XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */ 189 @@ -1169,6 +1259,10 @@ movemouse(const Arg *arg) 190 case Expose: 191 case MapRequest: 192 handler[ev.type](&ev); 193 + 194 + // A MapRequest could've caused the current window to swallow another one. 195 + if(c->swallowed) 196 + c = c->swallowed; 197 break; 198 case MotionNotify: 199 if ((ev.xmotion.time - lasttime) <= (1000 / 60)) 200 @@ -1323,6 +1417,9 @@ resizemouse(const Arg *arg) 201 case Expose: 202 case MapRequest: 203 handler[ev.type](&ev); 204 + 205 + if(c->swallowed) 206 + c = c->swallowed; 207 break; 208 case MotionNotify: 209 if ((ev.xmotion.time - lasttime) <= (1000 / 60)) 210 @@ -1578,6 +1675,7 @@ setup(void) 211 netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); 212 netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); 213 netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False); 214 + swallow_atom = XInternAtom(dpy, "_BETTER_SWALLOW", False); 215 /* init cursors */ 216 cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr); 217 cursor[CurResize] = drw_cur_create(drw, XC_sizing); 218 @@ -1595,6 +1693,8 @@ setup(void) 219 PropModeReplace, (unsigned char *) &wmcheckwin, 1); 220 XChangeProperty(dpy, wmcheckwin, netatom[NetWMName], utf8string, 8, 221 PropModeReplace, (unsigned char *) "dwm", 3); 222 + XChangeProperty(dpy, root, swallow_atom, utf8string, 8, 223 + PropModeReplace, (unsigned char *) "supported", 9); 224 XChangeProperty(dpy, root, netatom[NetWMCheck], XA_WINDOW, 32, 225 PropModeReplace, (unsigned char *) &wmcheckwin, 1); 226 /* EWMH support per view */ 227 @@ -1775,11 +1875,55 @@ unfocus(Client *c, int setfocus) 228 } 229 230 void 231 +deleteswallower(Client *c) { 232 + SwallowDef **prevnext = &swallowlist; 233 + for(SwallowDef *sd = swallowlist; sd != NULL;) { 234 + if(sd->swallower == c) { 235 + SwallowDef *next = sd->next; 236 + *prevnext = next; 237 + free(sd); 238 + sd = next; 239 + } else { 240 + prevnext = &sd->next; 241 + sd = sd->next; 242 + } 243 + } 244 + 245 + Client *sw = c->swallowed; 246 + while(sw) { 247 + sw->swallower = NULL; 248 + Client *next = sw->next_swallowed; 249 + sw->next_swallowed = NULL; 250 + sw = next; 251 + } 252 +} 253 + 254 +void 255 unmanage(Client *c, int destroyed) 256 { 257 Monitor *m = c->mon; 258 XWindowChanges wc; 259 260 + if(c->swallower) { 261 + Client **prev = &c->swallower->swallowed; 262 + for(; *prev != c; prev = &(*prev)->next_swallowed) 263 + ; 264 + *prev = c->next_swallowed; 265 + c->next_swallowed = NULL; 266 + 267 + if(c->swallower->swallowed == NULL) { 268 + detach(c->swallower); 269 + detachstack(c->swallower); 270 + 271 + c->swallower->next = c->next; 272 + c->next = c->swallower; 273 + c->swallower->snext = c->snext; 274 + c->snext = c->swallower; 275 + 276 + copyclientpos(c->swallower, c); 277 + } 278 + } 279 + 280 detach(c); 281 detachstack(c); 282 if (!destroyed) { 283 @@ -1793,9 +1937,10 @@ unmanage(Client *c, int destroyed) 284 XSync(dpy, False); 285 XSetErrorHandler(xerror); 286 XUngrabServer(dpy); 287 - } 288 + } else deleteswallower(c); 289 + if(c->swallower) focus(c->swallower); 290 + else focus(NULL); 291 free(c); 292 - focus(NULL); 293 updateclientlist(); 294 arrange(m); 295 }