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