1 module old;
2 import core.memory;
3 
4 import core.sys.posix.signal;
5 import core.sys.posix.sys.wait;
6 import core.sys.posix.unistd;
7 
8 import std.c.locale;
9 import std.c.string;
10 import std.c.stdlib;
11 
12 import std.stdio;
13 import std.string;
14 import std.algorithm;
15 import std.conv;
16 import std.process;
17 import std.traits;
18 
19 import deimos.X11.X;
20 import deimos.X11.Xlib;
21 import deimos.X11.keysymdef;
22 import deimos.X11.Xutil;
23 import deimos.X11.Xatom;
24 
25 import types;
26 import kernel;
27 import legacy;
28 import utils;
29 import config;
30 import cboxapp;
31 import window;
32 import helper.x11;
33 import gui.cursor;
34 import gui.font;
35 import theme.layout;
36 import theme.manager;
37 import gui.bar;
38 import monitor;
39 
40 void updatenumlockmask() 
41 {
42     XModifierKeymap *modmap;
43 
44     numlockmask = 0;
45     modmap = XGetModifierMapping(AppDisplay.instance().dpy);
46     foreach_reverse(i; 0..7) {
47         if(numlockmask == 0) {
48             break;
49         }
50         foreach_reverse(j; 0..modmap.max_keypermod-1) {
51             if(modmap.modifiermap[i * modmap.max_keypermod + j] ==
52                     XKeysymToKeycode(AppDisplay.instance().dpy, XK_Num_Lock)) {
53                 numlockmask = (1 << i);
54                 break;
55             }
56         }
57     }
58     XFreeModifiermap(modmap);
59 }
60 
61 void updateclientlist() 
62 {
63     
64     Client *c;
65     Monitor *m;
66 
67     XDeleteProperty(AppDisplay.instance().dpy, rootWin, netatom[NetClientList]);
68     for(m = mons; m; m = m.next)
69         for(c = m.clients; c; c = c.next)
70             XChangeProperty(AppDisplay.instance().dpy, rootWin, netatom[NetClientList],
71                             XA_WINDOW, 32, PropModeAppend,
72                             cast(ubyte *)&(c.win), 1);
73 }
74 
75 
76 
77 bool getrootptr(int *x, int *y) 
78 {
79     int di;
80     uint dui;
81     Window dummy;
82 
83     return XQueryPointer(AppDisplay.instance().dpy, rootWin, &dummy, &dummy, x, y, &di, &di, &dui) != 0;
84 }
85 
86 void setfocus(Client *c) 
87 {
88     
89     if(!c.neverfocus) {
90         XSetInputFocus(AppDisplay.instance().dpy, c.win, RevertToPointerRoot, CurrentTime);
91         XChangeProperty(AppDisplay.instance().dpy, rootWin, netatom[NetActiveWindow],
92                         XA_WINDOW, 32, PropModeReplace,
93                         cast(ubyte *) &(c.win), 1);
94     }
95     sendevent(c, wmatom[WMTakeFocus]);
96 }
97 
98 
99 bool sendevent(Client *c, Atom proto) 
100 {
101     int n;
102     Atom *protocols;
103     bool exists = false;
104     XEvent ev;
105 
106     if(XGetWMProtocols(AppDisplay.instance().dpy, c.win, &protocols, &n)) {
107         while(!exists && n--)
108             exists = protocols[n] == proto;
109         XFree(protocols);
110     }
111     if(exists) {
112         ev.type = ClientMessage;
113         ev.xclient.window = c.win;
114         ev.xclient.message_type = wmatom[WMProtocols];
115         ev.xclient.format = 32;
116         ev.xclient.data.l[0] = proto;
117         ev.xclient.data.l[1] = CurrentTime;
118         XSendEvent(AppDisplay.instance().dpy, c.win, false, NoEventMask, &ev);
119     }
120     return exists;
121 }
122 
123 Client* nexttiled(Client *c) 
124 {
125     return c.range!"next".find!(a => !a.isfloating && ISVISIBLE(a)).front;
126 }
127 
128 void pop(Client *c) 
129 {
130     detach(c);
131     attach(c);
132     focus(c);
133     arrange(c.mon);
134 }
135 
136 void setfullscreen(Client *c, bool fullscreen) 
137 {
138     if(fullscreen) {
139         XChangeProperty(AppDisplay.instance().dpy, c.win, netatom[NetWMState], XA_ATOM, 32,
140                         PropModeReplace, cast(ubyte*)&netatom[NetWMFullscreen], 1);
141         c.isfullscreen = true;
142         c.oldstate = c.isfloating;
143         c.oldbw = c.bw;
144         c.bw = 0;
145         c.isfloating = true;
146         resizeclient(c, c.mon.mx, c.mon.my, c.mon.mw, c.mon.mh);
147         XRaiseWindow(AppDisplay.instance().dpy, c.win);
148     } else {
149         XChangeProperty(AppDisplay.instance().dpy, c.win, netatom[NetWMState], XA_ATOM, 32,
150                         PropModeReplace, cast(ubyte*)0, 0);
151         c.isfullscreen = false;
152         c.isfloating = c.oldstate;
153         c.bw = c.oldbw;
154         c.x = c.oldx;
155         c.y = c.oldy;
156         c.w = c.oldw;
157         c.h = c.oldh;
158         resizeclient(c, c.x, c.y, c.w, c.h);
159         arrange(c.mon);
160     }
161 }
162 
163 void resize(Client *c, int x, int y, int w, int h, bool interact) 
164 {
165     if(applysizehints(c, x, y, w, h, interact))
166         resizeclient(c, x, y, w, h);
167 }
168 
169 void resizeclient(Client *c, int x, int y, int w, int h) 
170 {
171     XWindowChanges wc;
172 
173     c.oldx = c.x;
174     c.x = wc.x = x;
175     c.oldy = c.y;
176     c.y = wc.y = y;
177     c.oldw = c.w;
178     c.w = wc.width = w;
179     c.oldh = c.h;
180     c.h = wc.height = h;
181     wc.border_width = c.bw;
182     XConfigureWindow(AppDisplay.instance().dpy, c.win, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc);
183     configure(c);
184     XSync(AppDisplay.instance().dpy, false);
185 }
186 
187 void sendmon(Client *c, Monitor *m) 
188 {
189     if(c.mon == m)
190         return;
191     unfocus(c, true);
192     detach(c);
193     detachstack(c);
194     c.mon = m;
195     c.tags = m.tagset[m.seltags]; /* assign tags of target monitor */
196     attach(c);
197     attachstack(c);
198     focus(null);
199     arrange(null);
200 }
201 
202 
203 bool applysizehints(Client *c, ref int x, ref int y, ref int w, ref int h, bool interact) 
204 {
205     bool baseismin;
206     Monitor *m = c.mon;
207 
208     /* set minimum possible */
209     w = max(1, w);
210     h = max(1, h);
211     if(interact) {
212         if(x > sw)
213             x = sw - WIDTH(c);
214         if(y > sh)
215             y = sh - HEIGHT(c);
216         if(x + w + 2 * c.bw < 0)
217             x = 0;
218         if(y + h + 2 * c.bw < 0)
219             y = 0;
220     } else {
221         if(x >= m.wx + m.ww)
222             x = m.wx + m.ww - WIDTH(c);
223         if(y >= m.wy + m.wh)
224             y = m.wy + m.wh - HEIGHT(c);
225         if(x + w + 2 * c.bw <= m.wx)
226             x = m.wx;
227         if(y + h + 2 * c.bw <= m.wy)
228             y = m.wy;
229     }
230     if(h < bh)
231         h = bh;
232     if(w < bh)
233         w = bh;
234     if(resizehints || c.isfloating || !c.mon.lt[c.mon.sellt].arrange) {
235         /* see last two sentences in ICCCM 4.1.2.3 */
236         baseismin = c.basew == c.minw && c.baseh == c.minh;
237         if(!baseismin) { /* temporarily remove base dimensions */
238             w -= c.basew;
239             h -= c.baseh;
240         }
241 import std.math :
242         nearbyint;
243         /* adjust for aspect limits */
244         if(c.mina > 0 && c.maxa > 0) {
245             if(c.maxa < float(w) / h)
246                 w = cast(int)(h * c.maxa + 0.5);
247             else if(c.mina < float(h) / w)
248                 h = cast(int)(w * c.mina + 0.5);
249         }
250         if(baseismin) { /* increment calculation requires this */
251             w -= c.basew;
252             h -= c.baseh;
253         }
254         /* adjust for increment value */
255         if(c.incw)
256             w -= w % c.incw;
257         if(c.inch)
258             h -= h % c.inch;
259         /* restore base dimensions */
260         w = max(w + c.basew, c.minw);
261         h = max(h + c.baseh, c.minh);
262         if(c.maxw)
263             w = min(w, c.maxw);
264         if(c.maxh)
265             h = min(h, c.maxh);
266     }
267     return x != c.x || y != c.y || w != c.w || h != c.h;
268 }
269 
270 void clientmessage(XEvent *e) 
271 {
272     XClientMessageEvent *cme = &e.xclient;
273     Client *c = wintoclient(cme.window);
274 
275     if(!c)
276         return;
277     if(cme.message_type == netatom[NetWMState]) {
278         if(cme.data.l[1] == netatom[NetWMFullscreen] || cme.data.l[2] == netatom[NetWMFullscreen]) {
279             setfullscreen(c, (cme.data.l[0] == 1 /* _NET_WM_STATE_ADD    */
280                               || (cme.data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */ && !c.isfullscreen)));
281         }
282     } else if(cme.message_type == netatom[NetActiveWindow]) {
283         if(!ISVISIBLE(c)) {
284             c.mon.seltags ^= 1;
285             c.mon.tagset[c.mon.seltags] = c.tags;
286         }
287         pop(c);
288     }
289 }
290 
291 void togglefloating(const Arg *arg) 
292 {
293     if(!selmon.sel)
294         return;
295     if(selmon.sel.isfullscreen) /* no support for fullscreen windows */
296         return;
297     selmon.sel.isfloating = !selmon.sel.isfloating || selmon.sel.isfixed;
298     if(selmon.sel.isfloating)
299         resize(selmon.sel, selmon.sel.x, selmon.sel.y,
300                selmon.sel.w, selmon.sel.h, false);
301     arrange(selmon);
302 }
303 
304 void tag(const Arg *arg) {
305     
306     if(selmon.sel && arg.ui & TAGMASK) {
307         selmon.sel.tags = arg.ui & TAGMASK;
308         focus(null);
309         arrange(selmon);
310     }
311 }
312 
313 void tagmon(const Arg *arg) {
314     
315     if(!selmon.sel || !mons.next)
316         return;
317     sendmon(selmon.sel, dirtomon(arg.i));
318 }
319 
320 void toggletag(const Arg *arg) {
321     
322     uint newtags;
323 
324     if(!selmon.sel)
325         return;
326     newtags = selmon.sel.tags ^ (arg.ui & TAGMASK);
327     if(newtags) {
328         selmon.sel.tags = newtags;
329         focus(null);
330         arrange(selmon);
331     }
332 }
333 
334 void toggleview(const Arg *arg) {
335     
336     uint newtagset = selmon.tagset[selmon.seltags] ^ (arg.ui & TAGMASK);
337 
338     if(newtagset) {
339         selmon.tagset[selmon.seltags] = newtagset;
340         focus(null);
341         arrange(selmon);
342     }
343 }
344 
345 
346 void spawn(const Arg *arg) {
347     import std.variant;
348     Variant v = arg.val;
349     const(string[]) args = arg.s;
350     if(args[0] == dmenucmd[0]) {
351         dmenumon[0] =cast(char)('0' + selmon.num);
352     }
353     try {
354         auto pid = spawnProcess(args);
355     } catch {
356         die("Failed to spawn '%s'", args);
357     }
358 }
359 
360 void zoom(const Arg *arg) {
361     
362     Client *c = selmon.sel;
363 
364     if(!selmon.lt[selmon.sellt].arrange ||
365             (selmon.sel && selmon.sel.isfloating)) {
366         return;
367     }
368     if(c == nexttiled(selmon.clients)) {
369         if(c) {
370             c = nexttiled(c.next);
371         }
372         if(!c) {
373             return;
374         }
375     }
376     pop(c);
377 }
378 
379 void setlayout(const Arg *arg) 
380 {
381     if(!arg || !arg.v || arg.v != selmon.lt[selmon.sellt])
382         selmon.sellt ^= 1;
383     if(arg && arg.v) {
384         selmon.lt[selmon.sellt] = cast(Layout *)arg.v;
385     }
386     selmon.ltsymbol = selmon.lt[selmon.sellt].symbol;
387     if(selmon.sel)
388         arrange(selmon);
389     else
390         drawbar(selmon);
391 }
392 
393 void view(const Arg *arg) {
394     
395     if((arg.ui & TAGMASK) == selmon.tagset[selmon.seltags]) {
396         return;
397     }
398     selmon.seltags ^= 1; /* toggle sel tagset */
399     if(arg.ui & TAGMASK) {
400         selmon.tagset[selmon.seltags] = arg.ui & TAGMASK;
401     }
402     focus(null);
403     arrange(selmon);
404 }
405 
406 void killclient(const Arg *arg) {
407     
408     if(!selmon.sel)
409         return;
410 
411     if(!sendevent(selmon.sel, wmatom[WMDelete])) {
412         XGrabServer(AppDisplay.instance().dpy);
413         XSetErrorHandler(&xerrordummy);
414         XSetCloseDownMode(AppDisplay.instance().dpy, CloseDownMode.DestroyAll);
415         XKillClient(AppDisplay.instance().dpy, selmon.sel.win);
416         XSync(AppDisplay.instance().dpy, false);
417         XSetErrorHandler(&xerror);
418         XUngrabServer(AppDisplay.instance().dpy);
419     }
420 }
421 
422 void incnmaster(const Arg *arg) {
423     
424     selmon.nmaster = max(selmon.nmaster + arg.i, 0);
425     arrange(selmon);
426 }
427 
428 void setmfact(const Arg *arg) {
429     
430     float f;
431 
432     if(!arg || !selmon.lt[selmon.sellt].arrange)
433         return;
434     f = arg.f < 1.0 ? arg.f + selmon.mfact : arg.f - 1.0;
435     if(f < 0.1 || f > 0.9)
436         return;
437     selmon.mfact = f;
438     arrange(selmon);
439 }
440 
441 void tile(Monitor *m) 
442 {
443     uint i, n, h, mw, my, ty;
444     Client *c;
445 
446     for(n = 0, c = nexttiled(m.clients); c; c = nexttiled(c.next), n++) {}
447     if(n == 0) {
448         return;
449     }
450 
451     if(n > m.nmaster) {
452         mw = cast(uint)(m.nmaster ? m.ww * m.mfact : 0);
453     } else {
454         mw = m.ww;
455     }
456     for(i = my = ty = 0, c = nexttiled(m.clients); c; c = nexttiled(c.next), i++) {
457         if(i < m.nmaster) {
458             h = (m.wh - my) / (min(n, m.nmaster) - i);
459             resize(c, m.wx, m.wy + my, mw - (2*c.bw), h - (2*c.bw), false);
460             my += HEIGHT(c);
461         } else {
462             h = (m.wh - ty) / (n - i);
463             resize(c, m.wx + mw, m.wy + ty, m.ww - mw - (2*c.bw), h - (2*c.bw), false);
464             ty += HEIGHT(c);
465         }
466     }
467 }
468 
469 void monocle(Monitor *m) 
470 {
471     uint n = 0;
472 
473     n = m.clients.range!"next".map!(a=>ISVISIBLE(a)).sum;
474     if(n > 0) { /* override layout symbol */
475         m.ltsymbol = format("[%d]", n);
476     }
477     for(auto c = nexttiled(m.clients); c; c = nexttiled(c.next)) {
478         resize(c, m.wx, m.wy, m.ww - 2 * c.bw, m.wh - 2 * c.bw, false);
479     }
480 }
481 
482 bool updategeom() {
483     
484     bool dirty = false;
485 
486     Bool isXineramaActive = false;
487 
488     version(XINERAMA) {
489         isXineramaActive = XineramaIsActive(AppDisplay.instance().dpy);
490     }
491     if(isXineramaActive) {
492         version(XINERAMA) {
493             import std.range;
494             int nn;
495             Client *c;
496             XineramaScreenInfo *info = XineramaQueryScreens(AppDisplay.instance().dpy, &nn);
497             auto n = mons.range.walkLength;
498             XineramaScreenInfo[] unique = new XineramaScreenInfo[nn];
499             if(!unique.length) {
500                 die("fatal: could not malloc() %u bytes\n", XineramaScreenInfo.sizeof * nn);
501             }
502 
503             /* only consider unique geometries as separate screens */
504             int j=0;
505             foreach(i; 0..nn) {
506                 if(isuniquegeom(&unique[j], j, &info[i])) {
507                     unique[j++] = info[i];
508                 }
509             }
510             XFree(info);
511             nn = j;
512             if(n <= nn) {
513                 foreach(i; 0..(nn-n)) { /* new monitors available */
514                     auto m = mons.range.find!"a.next is null".front;
515                     if(m) {
516                         m.next = createmon();
517                     } else {
518                         mons = createmon();
519                     }
520                 }
521                 foreach(i, m; iota(nn).lockstep(mons.range)) {
522                     if(i >= n ||
523                             (unique[i].x_org != m.mx || unique[i].y_org != m.my ||
524                              unique[i].width != m.mw || unique[i].height != m.mh)) {
525                         dirty = true;
526                         m.num = i;
527                         m.mx = m.wx = unique[i].x_org;
528                         m.my = m.wy = unique[i].y_org;
529                         m.mw = m.ww = unique[i].width;
530                         m.mh = m.wh = unique[i].height;
531                         updatebarpos(m);
532                     }
533                 }
534             } else { /* less monitors available nn < n */
535                 foreach(i; nn..n) {
536                     auto m = mons.range.find!"a.next is null".front;
537                     if(m) {
538                         while(m.clients) {
539                             dirty = true;
540                             c = m.clients;
541                             m.clients = c.next;
542                             detachstack(c);
543                             c.mon = mons;
544                             attach(c);
545                             attachstack(c);
546                         }
547                         if(m == selmon)
548                             selmon = mons;
549                         cleanupmon(m);
550                     }
551                 }
552             }
553             unique = null;
554         }
555     } else {
556         /* default monitor setup */
557         if(!mons) {
558             mons = createmon();
559         }
560         if(mons.mw != sw || mons.mh != sh) {
561             dirty = true;
562             mons.mw = mons.ww = sw;
563             mons.mh = mons.wh = sh;
564             updatebarpos(mons);
565         }
566     }
567     if(dirty) {
568         selmon = mons;
569         selmon = wintomon(rootWin);
570     }
571     return dirty;
572 }
573 
574 void updatewmhints(Client *c) {
575     
576     XWMHints *wmh;
577     wmh = XGetWMHints(AppDisplay.instance().dpy, c.win);
578     if(wmh) {
579         if(c == selmon.sel && wmh.flags & XUrgencyHint) {
580             wmh.flags &= ~XUrgencyHint;
581             XSetWMHints(AppDisplay.instance().dpy, c.win, wmh);
582         } else
583             c.isurgent = (wmh.flags & XUrgencyHint) ? true : false;
584         if(wmh.flags & InputHint)
585             c.neverfocus = !wmh.input;
586         else
587             c.neverfocus = false;
588         XFree(wmh);
589     }
590 }
591 
592 void setclientstate(Client *c, long state) {
593     
594     long[] data = [ state, None ];
595 
596     XChangeProperty(AppDisplay.instance().dpy, c.win, wmatom[WMState], wmatom[WMState], 32,
597                     PropModeReplace, cast(ubyte *)data, 2);
598 }
599 
600 void configure(Client *c) {
601     
602     XConfigureEvent ce;
603 
604     ce.type = ConfigureNotify;
605     ce.display = AppDisplay.instance().dpy;
606     ce.event = c.win;
607     ce.window = c.win;
608     ce.x = c.x;
609     ce.y = c.y;
610     ce.width = c.w;
611     ce.height = c.h;
612     ce.border_width = c.bw;
613     ce.above = None;
614     ce.override_redirect = false;
615     XSendEvent(AppDisplay.instance().dpy, c.win, false, StructureNotifyMask, cast(XEvent *)&ce);
616 }
617 
618 
619 
620 
621 void updatesizehints(Client *c) {
622     
623     long msize;
624     XSizeHints size;
625 
626     if(!XGetWMNormalHints(AppDisplay.instance().dpy, c.win, &size, &msize)) {
627         /* size is uninitialized, ensure that size.flags aren't used */
628         size.flags = PSize;
629     }
630     if(size.flags & PBaseSize) {
631         c.basew = size.base_width;
632         c.baseh = size.base_height;
633     } else if(size.flags & PMinSize) {
634         c.basew = size.min_width;
635         c.baseh = size.min_height;
636     } else {
637         c.basew = c.baseh = 0;
638     }
639 
640     if(size.flags & PResizeInc) {
641         c.incw = size.width_inc;
642         c.inch = size.height_inc;
643     } else {
644         c.incw = c.inch = 0;
645     }
646     if(size.flags & PMaxSize) {
647         c.maxw = size.max_width;
648         c.maxh = size.max_height;
649     } else {
650         c.maxw = c.maxh = 0;
651     }
652     if(size.flags & PMinSize) {
653         c.minw = size.min_width;
654         c.minh = size.min_height;
655     } else if(size.flags & PBaseSize) {
656         c.minw = size.base_width;
657         c.minh = size.base_height;
658     } else {
659         c.minw = c.minh = 0;
660     }
661     if(size.flags & PAspect) {
662         c.mina = cast(float)size.min_aspect.y / size.min_aspect.x;
663         c.maxa = cast(float)size.max_aspect.x / size.max_aspect.y;
664     } else {
665         c.maxa = c.mina = 0.0;
666     }
667     c.isfixed = (c.maxw && c.minw && c.maxh && c.minh
668                  && c.maxw == c.minw && c.maxh == c.minh);
669 }
670 
671 void focus(Client *c) {
672     if(!c || !ISVISIBLE(c)) {
673         c = selmon.stack.range!"snext".find!(a => ISVISIBLE(a)).front;
674     }
675     /* was if(selmon.sel) */
676     if(selmon.sel && selmon.sel != c)
677         unfocus(selmon.sel, false);
678     if(c) {
679         if(c.mon != selmon)
680             selmon = c.mon;
681         if(c.isurgent)
682             clearurgent(c);
683         detachstack(c);
684         attachstack(c);
685         mouseEventHandler.grabbuttons(c, true);
686         XSetWindowBorder(AppDisplay.instance().dpy, c.win, ThemeManager.instance().getScheme(SchemeSel).border.rgb);
687         setfocus(c);
688     } else {
689         XSetInputFocus(AppDisplay.instance().dpy, rootWin, RevertToPointerRoot, CurrentTime);
690         XDeleteProperty(AppDisplay.instance().dpy, rootWin, netatom[NetActiveWindow]);
691     }
692     selmon.sel = c;
693     drawbars();
694 }
695 
696 void focusmon(const Arg *arg) {
697     
698     Monitor *m;
699 
700     if(mons && !mons.next) {
701         return;
702     }
703     m = dirtomon(arg.i);
704     if(m == selmon) {
705         return;
706     }
707     unfocus(selmon.sel, false); /* s/true/false/ fixes input focus issues
708 					in gedit and anjuta */
709     selmon = m;
710     focus(null);
711 }
712 
713 void focusstack(const Arg *arg) {
714     
715     Client *c = null, i;
716 
717     if(!selmon.sel)
718         return;
719     if(arg.i > 0) {
720         c = selmon.sel.range!"next".find!(a => ISVISIBLE(a)).front;
721         if(!c) {
722             c = selmon.clients.range!"next".find!(a => ISVISIBLE(a)).front;
723         }
724     } else {
725         for(i = selmon.clients; i != selmon.sel; i = i.next) {
726             if(ISVISIBLE(i)) {
727                 c = i;
728             }
729         }
730         if(!c) {
731             for(; i; i = i.next) {
732                 if(ISVISIBLE(i)) {
733                     c = i;
734                 }
735             }
736         }
737     }
738     if(c) {
739         focus(c);
740         restack(selmon);
741     }
742 }
743 
744 Client* wintoclient(Window w) {
745     
746     foreach(m; mons.range) {
747         auto c = m.clients.range!"next".find!(client => client.win == w).front;
748         if(c) {
749             return c;
750         }
751     }
752     return null;
753 }
754 
755 void unfocus(Client *c, bool setfocus) {
756     
757     if(!c)
758         return;
759     mouseEventHandler.grabbuttons(c, false);
760     XSetWindowBorder(AppDisplay.instance().dpy, c.win, ThemeManager.instance().getScheme(SchemeNorm).border.rgb);
761     if(setfocus) {
762         XSetInputFocus(AppDisplay.instance().dpy, rootWin, RevertToPointerRoot, CurrentTime);
763         XDeleteProperty(AppDisplay.instance().dpy, rootWin, netatom[NetActiveWindow]);
764     }
765 }
766 
767 
768 
769 void restack(Monitor *m) 
770 {
771     XEvent ev;
772     XWindowChanges wc;
773 
774     drawbar(m);
775     if(!m.sel)
776         return;
777     if(m.sel.isfloating || !m.lt[m.sellt].arrange)
778         XRaiseWindow(AppDisplay.instance().dpy, m.sel.win);
779     if(m.lt[m.sellt].arrange) {
780         wc.stack_mode = Below;
781         wc.sibling = m.barwin;
782         auto stacks = m.stack.range!"snext".filter!(a => !a.isfloating && ISVISIBLE(a));
783         foreach(c; stacks) {
784             XConfigureWindow(AppDisplay.instance().dpy, c.win, CWSibling|CWStackMode, &wc);
785             wc.sibling = c.win;
786         }
787     }
788     XSync(AppDisplay.instance().dpy, false);
789     while(XCheckMaskEvent(AppDisplay.instance().dpy, EnterWindowMask, &ev)) {}
790 }
791 
792 auto TEXTW(X)(auto ref in X x)
793 if(isSomeString!X) {
794     return drw.font.getexts_width(x) + drw.font.h;
795 }
796 
797 void cleanupmon(Monitor *mon) 
798 {
799     if(mon && mon == mons) {
800         mons = mons.next;
801     } else {
802         auto m = mons.range.find!(a => a.next == mon).front;
803         if(m) {
804             m.next = mon.next;
805         }
806     }
807     XUnmapWindow(AppDisplay.instance().dpy, mon.barwin);
808     XDestroyWindow(AppDisplay.instance().dpy, mon.barwin);
809     DGC.free(mon);
810 }
811 
812 void clearurgent(Client *c) {
813     
814     XWMHints *wmh;
815 
816     c.isurgent = false;
817     wmh = XGetWMHints(AppDisplay.instance().dpy, c.win);
818     if(wmh is null) {
819         return;
820     }
821     wmh.flags &= ~XUrgencyHint;
822     XSetWMHints(AppDisplay.instance().dpy, c.win, wmh);
823     XFree(wmh);
824 }
825 
826 
827 
828 void detachstack(Client *c) 
829 {
830     Client **tc;
831 
832 
833     for(tc = &c.mon.stack; *tc && *tc != c; tc = &(*tc).snext) {}
834     *tc = c.snext;
835 
836     if(c && c == c.mon.sel) {
837         auto t = c.mon.stack.range!"snext".find!(a=>ISVISIBLE(a)).front;
838         c.mon.sel = t;
839     }
840 }
841 
842 void detach(Client *c) 
843 {
844     Client **tc;
845     for(tc = &c.mon.clients; *tc && *tc != c; tc = &(*tc).next) {}
846     *tc = c.next;
847 }
848