Introduction
Introduction Statistics Contact Development Disclaimer Help
dwm-dynamicswallow-20240320-061e9fe.diff - sites - public wiki contents of suck…
git clone git://git.suckless.org/sites
Log
Files
Refs
---
dwm-dynamicswallow-20240320-061e9fe.diff (29463B)
---
1 From 20175007156c243ee577e0aa518a05282950e766 Mon Sep 17 00:00:00 2001
2 From: visil <[email protected]>
3 Date: Wed, 20 Mar 2024 17:48:47 +0300
4 Subject: [PATCH] Dynamic swallowing patch for 6.5
5
6 ---
7 Makefile | 3 +
8 config.def.h | 6 +
9 config.mk | 4 +-
10 dwm.c | 588 +++++++++++++++++++++++++++++++++++++++++++++++++--
11 dwmswallow | 120 +++++++++++
12 util.c | 29 +++
13 util.h | 1 +
14 7 files changed, 728 insertions(+), 23 deletions(-)
15 create mode 100644 dwmswallow
16
17 diff --git a/Makefile b/Makefile
18 index ffa69b4..67aa239 100644
19 --- a/Makefile
20 +++ b/Makefile
21 @@ -34,12 +34,15 @@ install: all
22 mkdir -p ${DESTDIR}${PREFIX}/bin
23 cp -f dwm ${DESTDIR}${PREFIX}/bin
24 chmod 755 ${DESTDIR}${PREFIX}/bin/dwm
25 + cp -f dwmswallow ${DESTDIR}${PREFIX}/bin
26 + chmod 755 ${DESTDIR}${PREFIX}/bin/dwmswallow
27 mkdir -p ${DESTDIR}${MANPREFIX}/man1
28 sed "s/VERSION/${VERSION}/g" < dwm.1 > ${DESTDIR}${MANPREFIX}/m…
29 chmod 644 ${DESTDIR}${MANPREFIX}/man1/dwm.1
30
31 uninstall:
32 rm -f ${DESTDIR}${PREFIX}/bin/dwm\
33 + ${DESTDIR}${MANPREFIX}/bin/dwmswallow\
34 ${DESTDIR}${MANPREFIX}/man1/dwm.1
35
36 .PHONY: all clean dist install uninstall
37 diff --git a/config.def.h b/config.def.h
38 index 9efa774..bf94c30 100644
39 --- a/config.def.h
40 +++ b/config.def.h
41 @@ -30,6 +30,10 @@ static const Rule rules[] = {
42 { "Gimp", NULL, NULL, 0, 1, …
43 { "Firefox", NULL, NULL, 1 << 8, 0, …
44 };
45 +/* window swallowing */
46 +static const int swaldecay = 3;
47 +static const int swalretroactive = 1;
48 +static const char swalsymbol[] = "👅";
49
50 /* layout(s) */
51 static const float mfact = 0.55; /* factor of master area size [0.0…
52 @@ -85,6 +89,7 @@ static const Key keys[] = {
53 { MODKEY, XK_period, focusmon, {.i …
54 { MODKEY|ShiftMask, XK_comma, tagmon, {.i …
55 { MODKEY|ShiftMask, XK_period, tagmon, {.i …
56 + { MODKEY, XK_u, swalstopsel, {0} },
57 TAGKEYS( XK_1, 0)
58 TAGKEYS( XK_2, 1)
59 TAGKEYS( XK_3, 2)
60 @@ -108,6 +113,7 @@ static const Button buttons[] = {
61 { ClkClientWin, MODKEY, Button1, movemou…
62 { ClkClientWin, MODKEY, Button2, togglef…
63 { ClkClientWin, MODKEY, Button3, resizem…
64 + { ClkClientWin, MODKEY|ShiftMask, Button1, swalmouse, …
65 { ClkTagBar, 0, Button1, view, …
66 { ClkTagBar, 0, Button3, togglev…
67 { ClkTagBar, MODKEY, Button1, tag, …
68 diff --git a/config.mk b/config.mk
69 index 8efca9a..6a1883c 100644
70 --- a/config.mk
71 +++ b/config.mk
72 @@ -27,8 +27,8 @@ LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIB…
73
74 # flags
75 CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700L -DVERSI…
76 -#CFLAGS = -g -std=c99 -pedantic -Wall -O0 ${INCS} ${CPPFLAGS}
77 -CFLAGS = -std=c99 -pedantic -Wall -Wno-deprecated-declarations -Os ${…
78 +CFLAGS = -g -std=c99 -pedantic -Wall -O0 ${INCS} ${CPPFLAGS}
79 +#CFLAGS = -std=c99 -pedantic -Wall -Wno-deprecated-declarations -Os $…
80 LDFLAGS = ${LIBS}
81
82 # Solaris
83 diff --git a/dwm.c b/dwm.c
84 index f1d86b2..c01cf41 100644
85 --- a/dwm.c
86 +++ b/dwm.c
87 @@ -58,7 +58,7 @@
88 #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad)
89
90 /* enums */
91 -enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */
92 +enum { CurNormal, CurResize, CurMove, CurSwal, CurLast }; /* cursor */
93 enum { SchemeNorm, SchemeSel }; /* color schemes */
94 enum { NetSupported, NetWMName, NetWMState, NetWMCheck,
95 NetWMFullscreen, NetActiveWindow, NetWMWindowType,
96 @@ -66,6 +66,7 @@ enum { NetSupported, NetWMName, NetWMState, NetWMCheck,
97 enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* defaul…
98 enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle,
99 ClkClientWin, ClkRootWin, ClkLast }; /* clicks */
100 +enum { ClientRegular = 1, ClientSwallowee, ClientSwallower }; /* client…
101
102 typedef union {
103 int i;
104 @@ -95,6 +96,7 @@ struct Client {
105 int isfixed, isfloating, isurgent, neverfocus, oldstate, isfull…
106 Client *next;
107 Client *snext;
108 + Client *swer; /* swallower of client, NULL by default */
109 Monitor *mon;
110 Window win;
111 };
112 @@ -140,6 +142,27 @@ typedef struct {
113 int isfloating;
114 int monitor;
115 } Rule;
116 +typedef struct Swallow Swallow;
117 +struct Swallow {
118 + /* Window class name, instance name (WM_CLASS) and title
119 + * (WM_NAME/_NET_WM_NAME, latter preferred if it exists). An em…
120 + * implies a wildcard as per strstr(). */
121 + char class[256];
122 + char inst[256];
123 + char title[256];
124 +
125 + /* Used to delete swallow instance after 'swaldecay' windows we…
126 + * without the swallow having been consumed. 'decay' keeps trac…
127 + * remaining "charges". */
128 + int decay;
129 +
130 + /* The swallower, i.e. the client which will swallow the next m…
131 + * whose filters match the above properties. */
132 + Client *client;
133 +
134 + /* Linked list of registered swallow instances. */
135 + Swallow *next;
136 +};
137
138 /* function declarations */
139 static void applyrules(Client *c);
140 @@ -165,6 +188,7 @@ static void drawbar(Monitor *m);
141 static void drawbars(void);
142 static void enternotify(XEvent *e);
143 static void expose(XEvent *e);
144 +static int fakesignal(void);
145 static void focus(Client *c);
146 static void focusin(XEvent *e);
147 static void focusmon(const Arg *arg);
148 @@ -206,6 +230,16 @@ static void setup(void);
149 static void seturgent(Client *c, int urg);
150 static void showhide(Client *c);
151 static void spawn(const Arg *arg);
152 +static void swal(Client *swer, Client *swee, int manage);
153 +static void swalreg(Client *c, const char* class, const char* inst, con…
154 +static void swaldecayby(int decayby);
155 +static void swalmanage(Swallow *s, Window w, XWindowAttributes *wa);
156 +static Swallow *swalmatch(Window w);
157 +static void swalmouse(const Arg *arg);
158 +static void swalrm(Swallow *s);
159 +static void swalunreg(Client *c);
160 +static void swalstop(Client *c, Client *root);
161 +static void swalstopsel(const Arg *unused);
162 static void tag(const Arg *arg);
163 static void tagmon(const Arg *arg);
164 static void tile(Monitor *m);
165 @@ -228,6 +262,7 @@ static void updatewindowtype(Client *c);
166 static void updatewmhints(Client *c);
167 static void view(const Arg *arg);
168 static Client *wintoclient(Window w);
169 +static int wintoclient2(Window w, Client **pc, Client **proot);
170 static Monitor *wintomon(Window w);
171 static int xerror(Display *dpy, XErrorEvent *ee);
172 static int xerrordummy(Display *dpy, XErrorEvent *ee);
173 @@ -266,6 +301,7 @@ static Clr **scheme;
174 static Display *dpy;
175 static Drw *drw;
176 static Monitor *mons, *selmon;
177 +static Swallow *swallows;
178 static Window root, wmcheckwin;
179
180 /* configuration, allows nested code to access above variables */
181 @@ -586,7 +622,9 @@ configurerequest(XEvent *e)
182 XConfigureRequestEvent *ev = &e->xconfigurerequest;
183 XWindowChanges wc;
184
185 - if ((c = wintoclient(ev->window))) {
186 + switch (wintoclient2(ev->window, &c, NULL)) {
187 + case ClientRegular: /* fallthrough */
188 + case ClientSwallowee:
189 if (ev->value_mask & CWBorderWidth)
190 c->bw = ev->border_width;
191 else if (c->isfloating || !selmon->lt[selmon->sellt]->a…
192 @@ -617,7 +655,13 @@ configurerequest(XEvent *e)
193 XMoveResizeWindow(dpy, c->win, c->x, c-…
194 } else
195 configure(c);
196 - } else {
197 + break;
198 + case ClientSwallower:
199 + /* Reject any move/resize requests for swallowers and c…
200 + * refusal to client via a synthetic ConfigureNotify (I…
201 + configure(c);
202 + break;
203 + default:
204 wc.x = ev->x;
205 wc.y = ev->y;
206 wc.width = ev->width;
207 @@ -626,6 +670,7 @@ configurerequest(XEvent *e)
208 wc.sibling = ev->above;
209 wc.stack_mode = ev->detail;
210 XConfigureWindow(dpy, ev->window, ev->value_mask, &wc);
211 + break;
212 }
213 XSync(dpy, False);
214 }
215 @@ -650,11 +695,30 @@ createmon(void)
216 void
217 destroynotify(XEvent *e)
218 {
219 - Client *c;
220 + Client *c, *swee, *root;
221 XDestroyWindowEvent *ev = &e->xdestroywindow;
222
223 - if ((c = wintoclient(ev->window)))
224 + switch (wintoclient2(ev->window, &c, &root)) {
225 + case ClientRegular:
226 unmanage(c, 1);
227 + break;
228 + case ClientSwallowee:
229 + swalstop(c, NULL);
230 + unmanage(c, 1);
231 + break;
232 + case ClientSwallower:
233 + /* If the swallower is swallowed by another client, ter…
234 + * swallow. This cuts off the swallow chain after the c…
235 + swalstop(c, root);
236 +
237 + /* Cut off the swallow chain before the client. */
238 + for (swee = root; swee->swer != c; swee = swee->swer);
239 + swee->swer = NULL;
240 +
241 + free(c);
242 + updateclientlist();
243 + break;
244 + }
245 }
246
247 void
248 @@ -734,6 +798,12 @@ drawbar(Monitor *m)
249 drw_setscheme(drw, scheme[SchemeNorm]);
250 x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0);
251
252 + /* Draw swalsymbol next to ltsymbol. */
253 + if (m->sel && m->sel->swer) {
254 + w = TEXTW(swalsymbol);
255 + x = drw_text(drw, x, 0, w, bh, lrpad / 2, swalsymbol, 0…
256 + }
257 +
258 if ((w = m->ww - tw - x) > bh) {
259 if (m->sel) {
260 drw_setscheme(drw, scheme[m == selmon ? SchemeS…
261 @@ -786,6 +856,81 @@ expose(XEvent *e)
262 drawbar(m);
263 }
264
265 +int
266 +fakesignal(void)
267 +{
268 + /* Command syntax: <PREFIX><COMMAND>[<SEP><ARG>]... */
269 + static const char sep[] = "###";
270 + static const char prefix[] = "#!";
271 +
272 + size_t numsegments, numargs;
273 + char rootname[256];
274 + char *segments[16] = {0};
275 +
276 + /* Get root name, split by separator and find the prefix */
277 + if (!gettextprop(root, XA_WM_NAME, rootname, sizeof(rootname))
278 + || strncmp(rootname, prefix, sizeof(prefix) - 1)) {
279 + return 0;
280 + }
281 + numsegments = split(rootname + sizeof(prefix) - 1, sep, segment…
282 + numargs = numsegments - 1; /* number of arguments to COMMAND */
283 +
284 + if (!strcmp(segments[0], "swalreg")) {
285 + /* Params: windowid, [class], [instance], [title] */
286 + Window w;
287 + Client *c;
288 +
289 + if (numargs >= 1) {
290 + w = strtoul(segments[1], NULL, 0);
291 + switch (wintoclient2(w, &c, NULL)) {
292 + case ClientRegular: /* fallthrough */
293 + case ClientSwallowee:
294 + swalreg(c, segments[2], segments[3], se…
295 + break;
296 + }
297 + }
298 + }
299 + else if (!strcmp(segments[0], "swal")) {
300 + /* Params: swallower's windowid, swallowee's window-id …
301 + Client *swer, *swee;
302 + Window winswer, winswee;
303 + int typeswer, typeswee;
304 +
305 + if (numargs >= 2) {
306 + winswer = strtoul(segments[1], NULL, 0);
307 + typeswer = wintoclient2(winswer, &swer, NULL);
308 + winswee = strtoul(segments[2], NULL, 0);
309 + typeswee = wintoclient2(winswee, &swee, NULL);
310 + if ((typeswer == ClientRegular || typeswer == C…
311 + && (typeswee == ClientRegular || typesw…
312 + swal(swer, swee, 0);
313 + }
314 + }
315 + else if (!strcmp(segments[0], "swalunreg")) {
316 + /* Params: swallower's windowid */
317 + Client *swer;
318 + Window winswer;
319 +
320 + if (numargs == 1) {
321 + winswer = strtoul(segments[1], NULL, 0);
322 + if ((swer = wintoclient(winswer)))
323 + swalunreg(swer);
324 + }
325 + }
326 + else if (!strcmp(segments[0], "swalstop")) {
327 + /* Params: swallowee's windowid */
328 + Client *swee;
329 + Window winswee;
330 +
331 + if (numargs == 1) {
332 + winswee = strtoul(segments[1], NULL, 0);
333 + if ((swee = wintoclient(winswee)))
334 + swalstop(swee, NULL);
335 + }
336 + }
337 + return 1;
338 +}
339 +
340 void
341 focus(Client *c)
342 {
343 @@ -1101,13 +1246,35 @@ mappingnotify(XEvent *e)
344 void
345 maprequest(XEvent *e)
346 {
347 + Client *c, *swee, *root;
348 static XWindowAttributes wa;
349 XMapRequestEvent *ev = &e->xmaprequest;
350 + Swallow *s;
351
352 if (!XGetWindowAttributes(dpy, ev->window, &wa) || wa.override_…
353 return;
354 - if (!wintoclient(ev->window))
355 - manage(ev->window, &wa);
356 + switch (wintoclient2(ev->window, &c, &root)) {
357 + case ClientRegular: /* fallthrough */
358 + case ClientSwallowee:
359 + /* Regulars and swallowees are always mapped. Nothing t…
360 + break;
361 + case ClientSwallower:
362 + /* Remapping a swallower will simply stop the swallow. …
363 + for (swee = root; swee->swer != c; swee = swee->swer);
364 + swalstop(swee, root);
365 + break;
366 + default:
367 + /* No client is managing the window. See if any swallow…
368 + if ((s = swalmatch(ev->window)))
369 + swalmanage(s, ev->window, &wa);
370 + else
371 + manage(ev->window, &wa);
372 + break;
373 + }
374 +
375 + /* Reduce decay counter of all swallow instances. */
376 + if (swaldecay)
377 + swaldecayby(1);
378 }
379
380 void
381 @@ -1223,11 +1390,13 @@ propertynotify(XEvent *e)
382 {
383 Client *c;
384 Window trans;
385 + Swallow *s;
386 XPropertyEvent *ev = &e->xproperty;
387
388 - if ((ev->window == root) && (ev->atom == XA_WM_NAME))
389 - updatestatus();
390 - else if (ev->state == PropertyDelete)
391 + if ((ev->window == root) && (ev->atom == XA_WM_NAME)) {
392 + if (!fakesignal())
393 + updatestatus();
394 + } else if (ev->state == PropertyDelete)
395 return; /* ignore */
396 else if ((c = wintoclient(ev->window))) {
397 switch(ev->atom) {
398 @@ -1249,6 +1418,9 @@ propertynotify(XEvent *e)
399 updatetitle(c);
400 if (c == c->mon->sel)
401 drawbar(c->mon);
402 + if (swalretroactive && (s = swalmatch(c->win)))…
403 + swal(s->client, c, 0);
404 + }
405 }
406 if (ev->atom == netatom[NetWMWindowType])
407 updatewindowtype(c);
408 @@ -1583,6 +1755,7 @@ setup(void)
409 cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr);
410 cursor[CurResize] = drw_cur_create(drw, XC_sizing);
411 cursor[CurMove] = drw_cur_create(drw, XC_fleur);
412 + cursor[CurSwal] = drw_cur_create(drw, XC_bottom_side);
413 /* init appearance */
414 scheme = ecalloc(LENGTH(colors), sizeof(Clr *));
415 for (i = 0; i < LENGTH(colors); i++)
416 @@ -1666,6 +1839,326 @@ spawn(const Arg *arg)
417 }
418 }
419
420 +/*
421 + * Perform immediate swallow of client 'swee' by client 'swer'. 'manage…
422 + * be set if swal() is called from swalmanage(). 'swer' and 'swee' must…
423 + * regular or swallowee, but not swallower.
424 + */
425 +void
426 +swal(Client *swer, Client *swee, int manage)
427 +{
428 + Client *c, **pc;
429 + int sweefocused = selmon->sel == swee;
430 +
431 + /* No self-swallowing! */
432 + if (swer == swee)
433 + return;
434 +
435 + /* Remove any swallows registered for the swer. Asking a swallo…
436 + * swallow another window is ambiguous and is thus avoided alto…
437 + * contrast, a swallowee can swallow in a well-defined manner b…
438 + * to the head of the swallow chain. */
439 + if (!manage)
440 + swalunreg(swer);
441 +
442 + /* Disable fullscreen prior to swallow. Swallows involving full…
443 + * windows produces quirky artefacts such as fullscreen termina…
444 + * pseudo-fullscreen windows. */
445 + setfullscreen(swer, 0);
446 + setfullscreen(swee, 0);
447 +
448 + /* Swap swallowee into client and focus lists. Keeps current fo…
449 + * the swer (which gets unmapped) is focused in which case the …
450 + * receive focus. */
451 + detach(swee);
452 + for (pc = &swer->mon->clients; *pc && *pc != swer; pc = &(*pc)-…
453 + *pc = swee;
454 + swee->next = swer->next;
455 + detachstack(swee);
456 + for (pc = &swer->mon->stack; *pc && *pc != swer; pc = &(*pc)->s…
457 + *pc = swee;
458 + swee->snext = swer->snext;
459 + swee->mon = swer->mon;
460 + if (sweefocused) {
461 + detachstack(swee);
462 + attachstack(swee);
463 + selmon = swer->mon;
464 + }
465 + swee->tags = swer->tags;
466 + swee->isfloating = swer->isfloating;
467 + for (c = swee; c->swer; c = c->swer);
468 + c->swer = swer;
469 +
470 + /* Configure geometry params obtained from patches (e.g. cfacts…
471 + // swee->cfact = swer->cfact;
472 +
473 + /* ICCCM 4.1.3.1 */
474 + setclientstate(swer, WithdrawnState);
475 + if (manage)
476 + setclientstate(swee, NormalState);
477 +
478 + if (swee->isfloating || !swee->mon->lt[swee->mon->sellt]->arran…
479 + XRaiseWindow(dpy, swee->win);
480 + resize(swee, swer->x, swer->y, swer->w, swer->h, 0);
481 +
482 + focus(NULL);
483 + arrange(NULL);
484 + if (manage)
485 + XMapWindow(dpy, swee->win);
486 + XUnmapWindow(dpy, swer->win);
487 + restack(swer->mon);
488 +}
489 +
490 +/*
491 + * Register a future swallow with swallower 'c'. 'class', 'inst' and 't…
492 + * shall point null-terminated strings and must not be NULL. If an alre…
493 + * existing swallow instance targets 'c' its filters are updated and no…
494 + * swallow instance is created. 'c' may be ClientRegular or ClientSwall…
495 + * Complement to swalrm().
496 + */
497 +void
498 +swalreg(Client *c, const char *class, const char *inst, const char *tit…
499 +{
500 + Swallow *s;
501 +
502 + if (!c)
503 + return;
504 +
505 + /* Update existing swallow */
506 + for (s = swallows; s; s = s->next) {
507 + if (s->client == c) {
508 + strncpy(s->class, class, sizeof(s->class) - 1);
509 + strncpy(s->inst, inst, sizeof(s->inst) - 1);
510 + strncpy(s->title, title, sizeof(s->title) - 1);
511 + s->decay = swaldecay;
512 +
513 + /* Only one swallow per client. May return afte…
514 + return;
515 + }
516 + }
517 +
518 + s = ecalloc(1, sizeof(Swallow));
519 + s->decay = swaldecay;
520 + s->client = c;
521 + strncpy(s->class, class, sizeof(s->class) - 1);
522 + strncpy(s->inst, inst, sizeof(s->inst) - 1);
523 + strncpy(s->title, title, sizeof(s->title) - 1);
524 +
525 + s->next = swallows;
526 + swallows = s;
527 +}
528 +
529 +/*
530 + * Decrease decay counter of all registered swallows by 'decayby' and r…
531 + * swallow instances whose counter is less than or equal to zero.
532 + */
533 +void
534 +swaldecayby(int decayby)
535 +{
536 + Swallow *s, *t;
537 +
538 + for (s = swallows; s; s = t) {
539 + s->decay -= decayby;
540 + t = s->next;
541 + if (s->decay <= 0)
542 + swalrm(s);
543 + }
544 +}
545 +
546 +/*
547 + * Window configuration and client setup for new windows which are to be
548 + * swallowed immediately. Pendant to manage() for such windows.
549 + */
550 +void
551 +swalmanage(Swallow *s, Window w, XWindowAttributes *wa)
552 +{
553 + Client *swee, *swer;
554 + XWindowChanges wc;
555 +
556 + swer = s->client;
557 + swalrm(s);
558 +
559 + /* Perform bare minimum setup of a client for window 'w' such t…
560 + * may be used to perform the swallow. The following lines are …
561 + * minimal implementation of manage() with a few chunks delegat…
562 + * swal(). */
563 + swee = ecalloc(1, sizeof(Client));
564 + swee->win = w;
565 + swee->mon = swer->mon;
566 + swee->oldbw = wa->border_width;
567 + swee->bw = borderpx;
568 + attach(swee);
569 + attachstack(swee);
570 + updatetitle(swee);
571 + updatesizehints(swee);
572 + XSelectInput(dpy, swee->win, EnterWindowMask|FocusChangeMask|Pr…
573 + wc.border_width = swee->bw;
574 + XConfigureWindow(dpy, swee->win, CWBorderWidth, &wc);
575 + grabbuttons(swee, 0);
576 + XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 3…
577 + (unsigned char *) &(swee->win), 1);
578 +
579 + swal(swer, swee, 1);
580 +}
581 +
582 +/*
583 + * Return swallow instance which targets window 'w' as determined by it…
584 + * name, instance name and window title. Returns NULL if none is found.…
585 + * to wintoclient().
586 + */
587 +Swallow *
588 +swalmatch(Window w)
589 +{
590 + XClassHint ch = { NULL, NULL };
591 + Swallow *s = NULL;
592 + char title[sizeof(s->title)];
593 +
594 + XGetClassHint(dpy, w, &ch);
595 + if (!gettextprop(w, netatom[NetWMName], title, sizeof(title)))
596 + gettextprop(w, XA_WM_NAME, title, sizeof(title));
597 +
598 + for (s = swallows; s; s = s->next) {
599 + if ((!ch.res_class || strstr(ch.res_class, s->class))
600 + && (!ch.res_name || strstr(ch.res_name, s->inst…
601 + && (title[0] == '\0' || strstr(title, s->title)…
602 + break;
603 + }
604 +
605 + if (ch.res_class)
606 + XFree(ch.res_class);
607 + if (ch.res_name)
608 + XFree(ch.res_name);
609 + return s;
610 +}
611 +
612 +/*
613 + * Interactive drag-and-drop swallow.
614 + */
615 +void
616 +swalmouse(const Arg *arg)
617 +{
618 + Client *swer, *swee;
619 + XEvent ev;
620 +
621 + if (!(swee = selmon->sel))
622 + return;
623 +
624 + if (XGrabPointer(dpy, root, False, ButtonPressMask|ButtonReleas…
625 + GrabModeAsync, None, cursor[CurSwal]->cursor, CurrentTi…
626 + return;
627 +
628 + do {
629 + XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedi…
630 + switch(ev.type) {
631 + case ConfigureRequest: /* fallthrough */
632 + case Expose: /* fallthrough */
633 + case MapRequest:
634 + handler[ev.type](&ev);
635 + break;
636 + }
637 + } while (ev.type != ButtonRelease);
638 + XUngrabPointer(dpy, CurrentTime);
639 +
640 + if ((swer = wintoclient(ev.xbutton.subwindow))
641 + && swer != swee)
642 + swal(swer, swee, 0);
643 +
644 + /* Remove accumulated pending EnterWindow events caused by the …
645 + * movements. */
646 + XCheckMaskEvent(dpy, EnterWindowMask, &ev);
647 +}
648 +
649 +/*
650 + * Delete swallow instance swallows and free its resources. Complement …
651 + * swalreg(). If NULL is passed all registered swallows are deleted.
652 + */
653 +void
654 +swalrm(Swallow *s)
655 +{
656 + Swallow *t, **ps;
657 +
658 + if (s) {
659 + for (ps = &swallows; *ps && *ps != s; ps = &(*ps)->next…
660 + *ps = s->next;
661 + free(s);
662 + }
663 + else {
664 + for(s = swallows; s; s = t) {
665 + t = s->next;
666 + free(s);
667 + }
668 + swallows = NULL;
669 + }
670 +}
671 +
672 +/*
673 + * Removes swallow instance targeting 'c' if it exists. Complement to s…
674 + */
675 +void
676 +swalunreg(Client *c) { Swallow *s;
677 +
678 + for (s = swallows; s; s = s->next) {
679 + if (c == s->client) {
680 + swalrm(s);
681 + /* Max. 1 registered swallow per client. No nee…
682 + break;
683 + }
684 + }
685 +}
686 +
687 +/*
688 + * Stop an active swallow of swallowed client 'swee' and remap the swal…
689 + * If 'swee' is a swallower itself 'root' must point the root client of…
690 + * swallow chain containing 'swee'.
691 + */
692 +void
693 +swalstop(Client *swee, Client *root)
694 +{
695 + Client *swer;
696 +
697 + if (!swee || !(swer = swee->swer))
698 + return;
699 +
700 + swee->swer = NULL;
701 + root = root ? root : swee;
702 + swer->mon = root->mon;
703 + swer->tags = root->tags;
704 + swer->next = root->next;
705 + root->next = swer;
706 + swer->snext = root->snext;
707 + root->snext = swer;
708 + swer->isfloating = swee->isfloating;
709 +
710 + /* Configure geometry params obtained from patches (e.g. cfacts…
711 + // swer->cfact = 1.0;
712 +
713 + /* If swer is not in tiling mode reuse swee's geometry. */
714 + if (swer->isfloating || !root->mon->lt[root->mon->sellt]->arran…
715 + XRaiseWindow(dpy, swer->win);
716 + resize(swer, swee->x, swee->y, swee->w, swee->h, 0);
717 + }
718 +
719 + /* Override swer's border scheme which may be using SchemeSel. …
720 + XSetWindowBorder(dpy, swer->win, scheme[SchemeNorm][ColBorder].…
721 +
722 + /* ICCCM 4.1.3.1 */
723 + setclientstate(swer, NormalState);
724 +
725 + XMapWindow(dpy, swer->win);
726 + focus(NULL);
727 + arrange(swer->mon);
728 +}
729 +
730 +/*
731 + * Stop active swallow for currently selected client.
732 + */
733 +void
734 +swalstopsel(const Arg *unused)
735 +{
736 + if (selmon->sel)
737 + swalstop(selmon->sel, NULL);
738 +}
739 +
740 void
741 tag(const Arg *arg)
742 {
743 @@ -1806,12 +2299,24 @@ unmapnotify(XEvent *e)
744 {
745 Client *c;
746 XUnmapEvent *ev = &e->xunmap;
747 + int type;
748
749 - if ((c = wintoclient(ev->window))) {
750 - if (ev->send_event)
751 - setclientstate(c, WithdrawnState);
752 - else
753 - unmanage(c, 0);
754 + type = wintoclient2(ev->window, &c, NULL);
755 + if (type && ev->send_event) {
756 + setclientstate(c, WithdrawnState);
757 + return;
758 + }
759 + switch (type) {
760 + case ClientRegular:
761 + unmanage(c, 0);
762 + break;
763 + case ClientSwallowee:
764 + swalstop(c, NULL);
765 + unmanage(c, 0);
766 + break;
767 + case ClientSwallower:
768 + /* Swallowers are never mapped. Nothing to do. */
769 + break;
770 }
771 }
772
773 @@ -1853,15 +2358,19 @@ updatebarpos(Monitor *m)
774 void
775 updateclientlist()
776 {
777 - Client *c;
778 + Client *c, *d;
779 Monitor *m;
780
781 XDeleteProperty(dpy, root, netatom[NetClientList]);
782 - for (m = mons; m; m = m->next)
783 - for (c = m->clients; c; c = c->next)
784 - XChangeProperty(dpy, root, netatom[NetClientLis…
785 - XA_WINDOW, 32, PropModeAppend,
786 - (unsigned char *) &(c->win), 1);
787 + for (m = mons; m; m = m->next) {
788 + for (c = m->clients; c; c = c->next) {
789 + for (d = c; d; d = d->swer) {
790 + XChangeProperty(dpy, root, netatom[NetC…
791 + XA_WINDOW, 32, PropModeAppend,
792 + (unsigned char *) &(c->win), 1);
793 + }
794 + }
795 + }
796 }
797
798 int
799 @@ -2075,6 +2584,43 @@ wintoclient(Window w)
800 return NULL;
801 }
802
803 +/*
804 + * Writes client managing window 'w' into 'pc' and returns type of clie…
805 + * no client is found NULL is written to 'pc' and zero is returned. If …
806 + * is found and is a swallower (ClientSwallower) and proot is not NULL …
807 + * client of the swallow chain is written to 'proot'.
808 + */
809 +int
810 +wintoclient2(Window w, Client **pc, Client **proot)
811 +{
812 + Monitor *m;
813 + Client *c, *d;
814 +
815 + for (m = mons; m; m = m->next) {
816 + for (c = m->clients; c; c = c->next) {
817 + if (c->win == w) {
818 + *pc = c;
819 + if (c->swer)
820 + return ClientSwallowee;
821 + else
822 + return ClientRegular;
823 + }
824 + else {
825 + for (d = c->swer; d; d = d->swer) {
826 + if (d->win == w) {
827 + if (proot)
828 + *proot = c;
829 + *pc = d;
830 + return ClientSwallower;
831 + }
832 + }
833 + }
834 + }
835 + }
836 + *pc = NULL;
837 + return 0;
838 +}
839 +
840 Monitor *
841 wintomon(Window w)
842 {
843 diff --git a/dwmswallow b/dwmswallow
844 new file mode 100644
845 index 0000000..eaab3bb
846 --- /dev/null
847 +++ b/dwmswallow
848 @@ -0,0 +1,120 @@
849 +#!/usr/bin/env sh
850 +
851 +# Separator and command prefix, as defined in dwm.c:fakesignal()
852 +SEP='###'
853 +PREFIX='#!'
854 +
855 +# Asserts that all arguments are valid X11 window IDs, i.e. positive in…
856 +# For the purpose of this script 0 is declared invalid.
857 +is_winid() {
858 + while :; do
859 + # Given input incompatible to %d, some implementations …
860 + # an error while others silently evaluate the expressio…
861 + if ! wid=$(printf '%d' "$1" 2>/dev/null) || [ "$wid" -l…
862 + return 1
863 + fi
864 +
865 + [ -n "$2" ] && shift || break
866 + done
867 +}
868 +
869 +# Prints usage help. If "$1" is provided, function exits script after
870 +# execution.
871 +usage() {
872 + [ -t 1 ] && myprintf=printf || myprintf=true
873 + msg="$(cat <<-EOF
874 + dwm window swallowing command-line interface. Usage:
875 +
876 + $($myprintf "\033[1m")dwmswallow $($myprintf "\033[3m")SWALLO…
877 + Register window $($myprintf "\033[3m")SWALLOWER$($myprintf …
878 + match the $($myprintf "\033[3m")CLASS$($myprintf "\033[0m")…
879 + string-matching. An omitted filter will match anything.
880 +
881 + $($myprintf "\033[1m")dwmswallow $($myprintf "\033[3m")SWALLO…
882 + Deregister queued swallow for window $($myprintf "\033[3m")…
883 +
884 + $($myprintf "\033[1m")dwmswallow $($myprintf "\033[3m")SWALLO…
885 + Perform immediate swallow of window $($myprintf "\033[3m")S…
886 +
887 + $($myprintf "\033[1m")dwmswallow $($myprintf "\033[3m")SWALLO…
888 + Stop swallow of window $($myprintf "\033[3m")SWALLOWEE$($my…
889 + windows only.
890 +
891 + $($myprintf "\033[1m")dwmswallow -h$($myprintf "\033[0m")
892 + Show this usage information.
893 + EOF
894 + )"
895 +
896 + if [ -n "$1" ]; then
897 + echo "$msg" >&2
898 + exit "$1"
899 + else
900 + echo "$msg"
901 + fi
902 +}
903 +
904 +# Determine number of leading positional arguments
905 +arg1="$1" # save for later
906 +arg2="$2" # save for later
907 +num_pargs=0
908 +while :; do
909 + case "$1" in
910 + -*|"") break ;;
911 + *) num_pargs=$((num_pargs + 1)); shift ;;
912 + esac
913 +done
914 +
915 +case "$num_pargs" in
916 +1)
917 + ! is_winid "$arg1" && usage 1
918 +
919 + widswer="$arg1"
920 + if [ "$1" = "-d" ] && [ "$#" -eq 1 ]; then
921 + if name="$(printf "${PREFIX}swalunreg${SEP}%u" "$widswe…
922 + xsetroot -name "$name"
923 + else
924 + usage 1
925 + fi
926 + elif [ "$1" = "-s" ] && [ "$#" -eq 1 ]; then
927 + widswee="$arg1"
928 + if name="$(printf "${PREFIX}swalstop${SEP}%u" "$widswee…
929 + xsetroot -name "$name"
930 + else
931 + usage 1
932 + fi
933 + else
934 + while :; do
935 + case "$1" in
936 + -c) [ -n "$2" ] && { class="$2"; shift 2; } || …
937 + -i) [ -n "$2" ] && { instance="$2"; shift 2; } …
938 + -t) [ -n "$2" ] && { title="$2"; shift 2; } || …
939 + "") break ;;
940 + *) usage 1 ;;
941 + esac
942 + done
943 + widswer="$arg1"
944 + if name="$(printf "${PREFIX}swalreg${SEP}%u${SEP}%s${SE…
945 + xsetroot -name "$name"
946 + else
947 + usage 1
948 + fi
949 + fi
950 + ;;
951 +2)
952 + ! is_winid "$arg1" "$arg2" || [ -n "$1" ] && usage 1
953 +
954 + widswer="$arg1"
955 + widswee="$arg2"
956 + if name="$(printf "${PREFIX}swal${SEP}%u${SEP}%u" "$widswer" "$…
957 + xsetroot -name "$name"
958 + else
959 + usage 1
960 + fi
961 + ;;
962 +*)
963 + if [ "$arg1" = "-h" ] && [ $# -eq 1 ]; then
964 + usage
965 + else
966 + usage 1
967 + fi
968 +esac
969 diff --git a/util.c b/util.c
970 index 96b82c9..3af7b6c 100644
971 --- a/util.c
972 +++ b/util.c
973 @@ -24,6 +24,35 @@ die(const char *fmt, ...)
974
975 exit(1);
976 }
977 +/*
978 + * Splits a string into segments according to a separator. A '\0' is wr…
979 + * the end of every segment. The beginning of every segment is written …
980 + * 'pbegin'. Only the first 'maxcount' segments will be written if
981 + * maxcount > 0. Inspired by python's split.
982 + *
983 + * Used exclusively by fakesignal() to split arguments.
984 + */
985 +size_t
986 +split(char *s, const char* sep, char **pbegin, size_t maxcount) {
987 +
988 + char *p, *q;
989 + const size_t seplen = strlen(sep);
990 + size_t count = 0;
991 +
992 + maxcount = maxcount == 0 ? (size_t)-1 : maxcount;
993 + p = s;
994 + while ((q = strstr(p, sep)) != NULL && count < maxcount) {
995 + pbegin[count] = p;
996 + *q = '\0';
997 + p = q + seplen;
998 + count++;
999 + }
1000 + if (count < maxcount) {
1001 + pbegin[count] = p;
1002 + count++;
1003 + }
1004 + return count;
1005 +}
1006
1007 void *
1008 ecalloc(size_t nmemb, size_t size)
1009 diff --git a/util.h b/util.h
1010 index f633b51..670345f 100644
1011 --- a/util.h
1012 +++ b/util.h
1013 @@ -6,3 +6,4 @@
1014
1015 void die(const char *fmt, ...);
1016 void *ecalloc(size_t nmemb, size_t size);
1017 +size_t split(char *s, const char* sep, char **pbegin, size_t maxcount);
1018 --
1019 2.44.0
1020
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.