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