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