dwm.c (60705B)
1 #include <errno.h> 2 #include <locale.h> 3 #include <signal.h> 4 #include <stdarg.h> 5 #include <stdio.h> 6 #include <stdlib.h> 7 #include <string.h> 8 #include <unistd.h> 9 #include <sys/types.h> 10 #include <sys/wait.h> 11 #include <X11/cursorfont.h> 12 #include <X11/keysym.h> 13 #include <X11/Xatom.h> 14 #include <X11/Xlib.h> 15 #include <X11/Xproto.h> 16 #include <X11/Xutil.h> 17 #ifdef XINERAMA 18 #include <X11/extensions/Xinerama.h> 19 #endif /* XINERAMA */ 20 #include <X11/Xft/Xft.h> 21 #include <X11/Xlib-xcb.h> 22 #include <xcb/res.h> 23 24 #include "drw.h" 25 #include "util.h" 26 27 /* macros */ 28 #define BUTTONMASK (ButtonPressMask | ButtonReleaseMask) 29 #define CLEANMASK(mask) \ 30 ( \ 31 mask & ~(numlockmask | LockMask) & \ 32 (ShiftMask | ControlMask | Mod1Mask | Mod2Mask | Mod3Mask | Mod4Mask \ 33 | Mod5Mask)) 34 #define INTERSECT(x, y, w, h, m) \ 35 ( \ 36 MAX(0, MIN((x) + (w), (m)->wx + (m)->ww) - MAX((x), (m)->wx)) * \ 37 MAX(0, MIN((y) + (h), (m)->wy + (m)->wh) - MAX((y), (m)->wy))) 38 #define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags])) 39 #define LENGTH(X) (sizeof X / sizeof X[0]) 40 #define MOUSEMASK (BUTTONMASK | PointerMotionMask) 41 #define WIDTH(X) ((X)->w + 2 * (X)->bw) 42 #define HEIGHT(X) ((X)->h + 2 * (X)->bw) 43 #define TAGMASK ((1 << LENGTH(tags)) - 1) 44 #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) 45 46 #define OPAQUE 0xffU 47 48 /* enums */ 49 enum { /* cursor */ 50 CurNormal, 51 CurResize, 52 CurMove, 53 CurLast 54 }; 55 enum { /* color schemes */ 56 SchemeNorm, 57 SchemeSel 58 }; 59 enum { /* EWMH atoms */ 60 NetSupported, 61 NetWMName, 62 NetWMState, 63 NetWMCheck, 64 NetWMFullscreen, 65 NetActiveWindow, 66 NetWMWindowType, 67 NetWMWindowTypeDialog, 68 NetClientList, 69 NetLast 70 }; 71 enum { /* default atoms */ 72 WMProtocols, 73 WMDelete, 74 WMState, 75 WMTakeFocus, 76 WMLast 77 }; 78 enum { /* clicks */ 79 ClkTagBar, 80 ClkLtSymbol, 81 ClkStatusText, 82 ClkWinTitle, 83 ClkClientWin, 84 ClkRootWin, 85 ClkLast 86 }; 87 88 typedef union { 89 int i; 90 uint ui; 91 float f; 92 const void* v; 93 } Arg; 94 95 typedef struct { 96 uint click; 97 uint mask; 98 uint button; 99 void (*func)(const Arg* arg); 100 const Arg arg; 101 } Button; 102 103 typedef struct Monitor Monitor; 104 typedef struct Client Client; 105 struct Client { 106 char name[256]; 107 float mina, maxa; 108 int x, y, w, h; 109 int oldx, oldy, oldw, oldh; 110 int basew, baseh, incw, inch, maxw, maxh, minw, minh, hintsvalid; 111 int bw, oldbw; 112 unsigned int tags; 113 int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen; 114 Client *next; 115 Client *snext; 116 Monitor *mon; 117 Window win; 118 int isterminal, noswallow; 119 pid_t pid; 120 Client* swallowing; 121 }; 122 123 typedef struct { 124 uint mod; 125 KeySym keysym; 126 void (*func)(const Arg*); 127 const Arg arg; 128 } Key; 129 130 typedef struct { 131 const char* symbol; 132 void (*arrange)(struct Monitor*); 133 } Layout; 134 135 struct Monitor { 136 float mfact; 137 int nmaster; 138 int num; 139 int by; /* bar geometry */ 140 int mx, my, mw, mh; /* screen size */ 141 int wx, wy, ww, wh; /* window area */ 142 int gappih; /* horizontal gap between windows */ 143 int gappiv; /* vertical gap between windows */ 144 int gappoh; /* horizontal outer gaps */ 145 int gappov; /* vertical outer gaps */ 146 uint seltags; 147 uint sellt; 148 uint tagset[2]; 149 int showbar; 150 int topbar; 151 Client* clients; 152 Client* sel; 153 Client* stack; 154 struct Monitor* next; 155 Window barwin; 156 const Layout* lt[2]; 157 }; 158 159 typedef struct { 160 const char* class; 161 const char* instance; 162 const char* title; 163 uint tags; 164 int isfloating; 165 int isterminal; 166 int noswallow; 167 int monitor; 168 } Rule; 169 170 /* function declarations */ 171 static void applyrules(Client *c); 172 static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact); 173 static void arrange(Monitor *m); 174 static void arrangemon(Monitor *m); 175 static void attach(Client *c); 176 static void attachstack(Client *c); 177 static void buttonpress(XEvent *e); 178 static void checkotherwm(void); 179 static void cleanup(void); 180 static void cleanupmon(Monitor *mon); 181 static void clientmessage(XEvent *e); 182 static void configure(Client *c); 183 static void configurenotify(XEvent *e); 184 static void configurerequest(XEvent *e); 185 static Monitor *createmon(void); 186 static void destroynotify(XEvent *e); 187 static void detach(Client *c); 188 static void detachstack(Client *c); 189 static Monitor *dirtomon(int dir); 190 static void drawbar(Monitor *m); 191 static void drawbars(void); 192 static void enternotify(XEvent *e); 193 static void expose(XEvent *e); 194 static void focus(Client *c); 195 static void focusin(XEvent *e); 196 static void focusmon(const Arg *arg); 197 static void focusstack(const Arg *arg); 198 static Atom getatomprop(Client *c, Atom prop); 199 static int getrootptr(int *x, int *y); 200 static long getstate(Window w); 201 static int gettextprop(Window w, Atom atom, char *text, unsigned int size); 202 static void grabbuttons(Client *c, int focused); 203 static void grabkeys(void); 204 static void keypress(XEvent *e); 205 static void killclient(const Arg *arg); 206 static void manage(Window w, XWindowAttributes *wa); 207 static void mappingnotify(XEvent *e); 208 static void maprequest(XEvent *e); 209 static void monocle(Monitor *m); 210 static void motionnotify(XEvent *e); 211 static void movemouse(const Arg *arg); 212 static Client *nexttiled(Client *c); 213 static void pop(Client *c); 214 static void propertynotify(XEvent *e); 215 static void quit(const Arg *arg); 216 static Monitor *recttomon(int x, int y, int w, int h); 217 static void resize(Client *c, int x, int y, int w, int h, int interact); 218 static void resizeclient(Client *c, int x, int y, int w, int h); 219 static void resizemouse(const Arg *arg); 220 static void restack(Monitor *m); 221 static void run(void); 222 static void scan(void); 223 static int sendevent(Client *c, Atom proto); 224 static void sendmon(Client *c, Monitor *m); 225 static void setclientstate(Client *c, long state); 226 static void setfocus(Client *c); 227 static void setfullscreen(Client *c, int fullscreen); 228 static void setlayout(const Arg *arg); 229 static void setmfact(const Arg *arg); 230 static void setup(void); 231 static void seturgent(Client *c, int urg); 232 static void showhide(Client *c); 233 static void spawn(const Arg *arg); 234 static void tag(const Arg *arg); 235 static void tagmon(const Arg *arg); 236 static void tile(Monitor *m); 237 static void togglebar(const Arg *arg); 238 static void togglefloating(const Arg *arg); 239 static void toggletag(const Arg *arg); 240 static void toggleview(const Arg *arg); 241 static void unfocus(Client *c, int setfocus); 242 static void unmanage(Client *c, int destroyed); 243 static void unmapnotify(XEvent *e); 244 static void updatebarpos(Monitor *m); 245 static void updatebars(void); 246 static void updateclientlist(void); 247 static int updategeom(void); 248 static void updatenumlockmask(void); 249 static void updatesizehints(Client *c); 250 static void updatestatus(void); 251 static void updatetitle(Client *c); 252 static void updatewindowtype(Client *c); 253 static void updatewmhints(Client *c); 254 static void view(const Arg *arg); 255 static Client *wintoclient(Window w); 256 static Monitor *wintomon(Window w); 257 static int xerror(Display *dpy, XErrorEvent *ee); 258 static int xerrordummy(Display *dpy, XErrorEvent *ee); 259 static int xerrorstart(Display *dpy, XErrorEvent *ee); 260 static void zoom(const Arg *arg); 261 262 static void fibonacci(Monitor *m); 263 static pid_t getparentprocess(pid_t p); 264 static int isdescprocess(pid_t p, pid_t c); 265 static void keyrelease(XEvent *e); 266 static void sigchld(int unused); 267 static Client* swallowingclient(Window w); 268 static Client* termforwin(const Client* c); 269 static void togglefullscr(const Arg* arg); 270 static void updatebar(void); 271 static pid_t winpid(Window w); 272 static void xinitvisual(); 273 274 /* variables */ 275 static const char broken[] = "broken"; 276 static char stext[256]; 277 static int screen; 278 static int sw, sh; /* X display screen geometry width, height */ 279 static int bh; /* bar height */ 280 static int lrpad; /* sum of left and right padding for text */ 281 static int (*xerrorxlib)(Display*, XErrorEvent*); 282 static uint numlockmask = 0; 283 static void (*handler[LASTEvent]) (XEvent *) = { 284 [ButtonPress] = buttonpress, 285 [ClientMessage] = clientmessage, 286 [ConfigureRequest] = configurerequest, 287 [ConfigureNotify] = configurenotify, 288 [DestroyNotify] = destroynotify, 289 [EnterNotify] = enternotify, 290 [Expose] = expose, 291 [FocusIn] = focusin, 292 [KeyRelease] = keyrelease, 293 [KeyPress] = keypress, 294 [MappingNotify] = mappingnotify, 295 [MapRequest] = maprequest, 296 [MotionNotify] = motionnotify, 297 [PropertyNotify] = propertynotify, 298 [UnmapNotify] = unmapnotify, 299 [ButtonRelease] = keyrelease 300 }; 301 static Atom wmatom[WMLast], netatom[NetLast]; 302 static int running = 1; 303 static Cur* cursor[CurLast]; 304 static Color** scheme; 305 static Display* dpy; 306 static Drw* drw; 307 static Monitor* mons, *selmon; 308 static Window root, wmcheckwin; 309 310 static Visual* visual; 311 static int depth; 312 static Colormap cmap; 313 static int scanner; 314 static int vp; /* vertical padding for bar */ 315 static int sp; /* side padding for bar */ 316 static xcb_connection_t* xcon; 317 318 /* configuration, allows nested code to access above variables */ 319 #include "config.h" 320 321 /* compile-time check if all tags fit into an uint bit array. */ 322 struct NumTags { 323 char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; 324 }; 325 326 /* function implementations */ 327 328 void 329 keyrelease(XEvent* e) 330 { 331 XEvent ev; 332 333 if (e->xkey.keycode != XKeysymToKeycode(dpy, XK_Super_L)) 334 return; 335 if (XEventsQueued(dpy, QueuedAfterReading)) { 336 XPeekEvent(dpy, &ev); 337 if (ev.type == KeyPress 338 && ev.xkey.time == e->xkey.time 339 && ev.xkey.keycode == e->xkey.keycode 340 ) { 341 XNextEvent(dpy, &ev); 342 return; 343 } 344 } 345 if (selmon->showbar == 1) { 346 selmon->showbar = 0; 347 updatebar(); 348 } 349 } 350 351 void 352 swallow(Client* p, Client* c) 353 { 354 Window win; 355 356 if (c->noswallow || c->isterminal) 357 return; 358 detach(c); 359 detachstack(c); 360 setclientstate(c, WithdrawnState); 361 XUnmapWindow(dpy, p->win); 362 p->swallowing = c; 363 c->mon = p->mon; 364 win = p->win; 365 p->win = c->win; 366 c->win = win; 367 updatetitle(p); 368 XMoveResizeWindow(dpy, p->win, p->x, p->y, p->w, p->h); 369 arrange(p->mon); 370 configure(p); 371 updateclientlist(); 372 } 373 374 void 375 unswallow(Client* c) 376 { 377 c->win = c->swallowing->win; 378 379 free(c->swallowing); 380 c->swallowing = NULL; 381 382 /* unfullscreen the client */ 383 setfullscreen(c, 0); 384 updatetitle(c); 385 arrange(c->mon); 386 XMapWindow(dpy, c->win); 387 XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); 388 setclientstate(c, NormalState); 389 focus(NULL); 390 arrange(c->mon); 391 } 392 393 void 394 applyrules(Client* c) 395 { 396 const char* class, *instance; 397 uint i; 398 const Rule* r; 399 Monitor* m; 400 XClassHint ch = { NULL, NULL }; 401 402 /* rule matching */ 403 c->isfloating = 0; 404 c->isterminal = 0; 405 c->noswallow = 0; 406 c->tags = 0; 407 XGetClassHint(dpy, c->win, &ch); 408 class = ch.res_class ? ch.res_class : broken; 409 instance = ch.res_name ? ch.res_name : broken; 410 411 for (i = 0; i < LENGTH(rules); i++) { 412 r = &rules[i]; 413 if ( 414 (!r->title || strstr(c->name, r->title)) 415 && (!r->class || strstr(class, r->class)) 416 && (!r->instance || strstr(instance, r->instance)) 417 ) { 418 c->isterminal = r->isterminal; 419 c->noswallow = r->noswallow; 420 c->isfloating = r->isfloating; 421 c->tags |= r->tags; 422 for (m = mons; m && m->num != r->monitor; m = m->next); 423 if (m) 424 c->mon = m; 425 } 426 } 427 if (ch.res_class) 428 XFree(ch.res_class); 429 if (ch.res_name) 430 XFree(ch.res_name); 431 c->tags = c->tags & TAGMASK 432 ? c->tags & TAGMASK 433 : c->mon->tagset[c->mon->seltags]; 434 } 435 436 int 437 applysizehints(Client* c, int* x, int* y, int* w, int* h, int interact) 438 { 439 int baseismin; 440 Monitor* m; 441 442 m = c->mon; 443 /* set minimum possible */ 444 *w = MAX(1, *w); 445 *h = MAX(1, *h); 446 if (interact) { 447 if (*x > sw) *x = sw - WIDTH(c); 448 if (*y > sh) *y = sh - HEIGHT(c); 449 if (*x + *w + 2 * c->bw < 0) *x = 0; 450 if (*y + *h + 2 * c->bw < 0) *y = 0; 451 } else { 452 if (*x >= m->wx + m->ww) *x = m->wx + m->ww - WIDTH(c); 453 if (*y >= m->wy + m->wh) *y = m->wy + m->wh - HEIGHT(c); 454 if (*x + *w + 2 * c->bw <= m->wx) *x = m->wx; 455 if (*y + *h + 2 * c->bw <= m->wy) *y = m->wy; 456 } 457 if (*h < bh) 458 *h = bh; 459 if (*w < bh) 460 *w = bh; 461 if (resizehints || c->isfloating || !c->mon->lt[c->mon->sellt]->arrange) { 462 if (!c->hintsvalid) 463 updatesizehints(c); 464 /* see last two sentences in ICCCM 4.1.2.3 */ 465 baseismin = c->basew == c->minw && c->baseh == c->minh; 466 if (!baseismin) { /* temporarily remove base dimensions */ 467 *w -= c->basew; 468 *h -= c->baseh; 469 } 470 /* adjust for aspect limits */ 471 if (c->mina > 0 && c->maxa > 0) { 472 if (c->maxa < (float)*w / *h) 473 *w = *h * c->maxa + 0.5; 474 else if (c->mina < (float)*h / *w) 475 *h = *w * c->mina + 0.5; 476 } 477 if (baseismin) { /* increment calculation requires this */ 478 *w -= c->basew; 479 *h -= c->baseh; 480 } 481 /* adjust for increment value */ 482 if (c->incw) 483 *w -= *w % c->incw; 484 if (c->inch) 485 *h -= *h % c->inch; 486 /* restore base dimensions */ 487 *w = MAX(*w + c->basew, c->minw); 488 *h = MAX(*h + c->baseh, c->minh); 489 if (c->maxw) 490 *w = MIN(*w, c->maxw); 491 if (c->maxh) 492 *h = MIN(*h, c->maxh); 493 } 494 return *x != c->x || *y != c->y || *w != c->w || *h != c->h; 495 } 496 497 void 498 arrange(Monitor* m) 499 { 500 if (m) 501 showhide(m->stack); 502 else 503 for (m = mons; m; m = m->next) 504 showhide(m->stack); 505 if (m) { 506 arrangemon(m); 507 restack(m); 508 } else 509 for (m = mons; m; m = m->next) 510 arrangemon(m); 511 } 512 513 void 514 arrangemon(Monitor* m) 515 { 516 if (m->lt[m->sellt]->arrange) 517 m->lt[m->sellt]->arrange(m); 518 } 519 520 void 521 attach(Client* c) 522 { 523 c->next = c->mon->clients; 524 c->mon->clients = c; 525 } 526 527 void 528 attachstack(Client* c) 529 { 530 c->snext = c->mon->stack; 531 c->mon->stack = c; 532 } 533 534 void 535 buttonpress(XEvent* e) 536 { 537 uint i, x, click; 538 Arg arg = { 0 }; 539 Client* c; 540 Monitor* m; 541 XButtonPressedEvent* ev = &e->xbutton; 542 543 click = ClkRootWin; 544 /* focus monitor if necessary */ 545 if ((m = wintomon(ev->window)) && m != selmon) { 546 unfocus(selmon->sel, 1); 547 selmon = m; 548 focus(NULL); 549 } 550 if (ev->window == selmon->barwin) { 551 i = x = 0; 552 unsigned int occ = 0; 553 for(c = m->clients; c; c=c->next) 554 occ |= c->tags; 555 do { 556 /* Do not reserve space for vacant tags */ 557 if (!(occ & 1 << i || m->tagset[m->seltags] & 1 << i)) 558 continue; 559 x += TEXTW(tags[i]); 560 } while (ev->x >= x && ++i < LENGTH(tags)); 561 if (i < LENGTH(tags)) { 562 click = ClkTagBar; 563 arg.ui = 1 << i; 564 } else if (ev->x < x) 565 click = ClkLtSymbol; 566 else if (ev->x > selmon->ww - (int)TEXTW(stext)) 567 click = ClkStatusText; 568 else 569 click = ClkWinTitle; 570 } else if ((c = wintoclient(ev->window))) { 571 focus(c); 572 restack(selmon); 573 XAllowEvents(dpy, ReplayPointer, CurrentTime); 574 click = ClkClientWin; 575 } 576 for (i = 0; i < LENGTH(buttons); i++) 577 if ( 578 click == buttons[i].click 579 && buttons[i].func 580 && buttons[i].button == ev->button 581 && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state) 582 ) 583 buttons[i].func( 584 click == ClkTagBar && buttons[i].arg.i == 0 585 ? &arg 586 : &buttons[i].arg 587 ); 588 } 589 590 void 591 checkotherwm(void) 592 { 593 xerrorxlib = XSetErrorHandler(xerrorstart); 594 /* this causes an error if some other window manager is running */ 595 XSelectInput(dpy, DefaultRootWindow(dpy), SubstructureRedirectMask); 596 XSync(dpy, False); 597 XSetErrorHandler(xerror); 598 XSync(dpy, False); 599 } 600 601 void 602 cleanup(void) 603 { 604 Arg a = { .ui = ~0 }; 605 Layout foo = { "", NULL }; 606 Monitor* m; 607 size_t i; 608 609 view(&a); 610 selmon->lt[selmon->sellt] = &foo; 611 for (m = mons; m; m = m->next) 612 while (m->stack) 613 unmanage(m->stack, 0); 614 XUngrabKey(dpy, AnyKey, AnyModifier, root); 615 while (mons) 616 cleanupmon(mons); 617 for (i = 0; i < CurLast; i++) 618 drw_cur_free(drw, cursor[i]); 619 for (i = 0; i < LENGTH(colors); i++) 620 free(scheme[i]); 621 free(scheme); 622 XDestroyWindow(dpy, wmcheckwin); 623 drw_free(drw); 624 XSync(dpy, False); 625 XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); 626 XDeleteProperty(dpy, root, netatom[NetActiveWindow]); 627 } 628 629 void 630 cleanupmon(Monitor* mon) 631 { 632 Monitor* m; 633 634 if (mon == mons) 635 mons = mons->next; 636 else { 637 for (m = mons; m && m->next != mon; m = m->next); 638 m->next = mon->next; 639 } 640 XUnmapWindow(dpy, mon->barwin); 641 XDestroyWindow(dpy, mon->barwin); 642 free(mon); 643 } 644 645 void 646 clientmessage(XEvent* e) 647 { 648 XClientMessageEvent* cme; 649 Client* c; 650 651 cme = &e->xclient; 652 c = wintoclient(cme->window); 653 if (!c) 654 return; 655 if (cme->message_type == netatom[NetWMState]) { 656 if (cme->data.l[1] == netatom[NetWMFullscreen] 657 || cme->data.l[2] == netatom[NetWMFullscreen] 658 ) 659 setfullscreen(c, 660 cme->data.l[0] == 1 /* _NET_WM_STATE_ADD */ 661 || (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */ 662 && !c->isfullscreen) 663 ); 664 } else if (cme->message_type == netatom[NetActiveWindow]) 665 if (c != selmon->sel && !c->isurgent) 666 seturgent(c, 1); 667 } 668 669 void 670 configure(Client* c) 671 { 672 XConfigureEvent ce; 673 674 ce.type = ConfigureNotify; 675 ce.display = dpy; 676 ce.event = c->win; 677 ce.window = c->win; 678 ce.x = c->x; 679 ce.y = c->y; 680 ce.width = c->w; 681 ce.height = c->h; 682 ce.border_width = c->bw; 683 ce.above = None; 684 ce.override_redirect = False; 685 XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent*)&ce); 686 } 687 688 void 689 configurenotify(XEvent* e) 690 { 691 Monitor* m; 692 Client* c; 693 XConfigureEvent* ev; 694 int dirty; 695 696 /* TODO: updategeom handling sucks, needs to be simplified */ 697 ev = &e->xconfigure; 698 if (ev->window == root) { 699 dirty = (sw != ev->width || sh != ev->height); 700 sw = ev->width; 701 sh = ev->height; 702 if (updategeom() || dirty) { 703 drw_resize(drw, sw, bh); 704 updatebars(); 705 for (m = mons; m; m = m->next) { 706 for (c = m->clients; c; c = c->next) 707 if (c->isfullscreen) 708 resizeclient(c, m->mx, m->my, m->mw, m->mh); 709 XMoveResizeWindow( 710 dpy, m->barwin, m->wx + sp, m->by + vp, m->ww - 2 * sp, bh 711 ); 712 } 713 focus(NULL); 714 arrange(NULL); 715 } 716 } 717 } 718 719 void 720 configurerequest(XEvent* e) 721 { 722 Client* c; 723 Monitor* m; 724 XConfigureRequestEvent* ev; 725 XWindowChanges wc; 726 727 ev = &e->xconfigurerequest; 728 if ((c = wintoclient(ev->window))) { 729 if (ev->value_mask & CWBorderWidth) 730 c->bw = ev->border_width; 731 else if (c->isfloating || !selmon->lt[selmon->sellt]->arrange) { 732 m = c->mon; 733 if (ev->value_mask & CWX) { 734 c->oldx = c->x; 735 c->x = m->mx + ev->x; 736 } 737 if (ev->value_mask & CWY) { 738 c->oldy = c->y; 739 c->y = m->my + ev->y; 740 } 741 if (ev->value_mask & CWWidth) { 742 c->oldw = c->w; 743 c->w = ev->width; 744 } 745 if (ev->value_mask & CWHeight) { 746 c->oldh = c->h; 747 c->h = ev->height; 748 } 749 if ((c->x + c->w) > m->mx + m->mw && c->isfloating) 750 c->x = m->mx + (m->mw / 2 - WIDTH(c) / 2); /* center in x direction */ 751 if ((c->y + c->h) > m->my + m->mh && c->isfloating) 752 c->y = m->my + (m->mh / 2 - HEIGHT(c) / 2); /* center in y direction */ 753 if ((ev->value_mask & (CWX | CWY)) 754 && !(ev->value_mask & (CWWidth | CWHeight)) 755 ) 756 configure(c); 757 if (ISVISIBLE(c)) 758 XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); 759 } else 760 configure(c); 761 } else { 762 wc.x = ev->x; 763 wc.y = ev->y; 764 wc.width = ev->width; 765 wc.height = ev->height; 766 wc.border_width = ev->border_width; 767 wc.sibling = ev->above; 768 wc.stack_mode = ev->detail; 769 XConfigureWindow(dpy, ev->window, ev->value_mask, &wc); 770 } 771 XSync(dpy, False); 772 } 773 774 Monitor* 775 createmon() 776 { 777 Monitor* m; 778 779 m = ecalloc(1, sizeof(Monitor)); 780 m->tagset[0] = m->tagset[1] = 1; 781 m->mfact = mfact; 782 m->nmaster = nmaster; 783 m->showbar = showbar; 784 m->topbar = topbar; 785 m->gappih = gappih; 786 m->gappiv = gappiv; 787 m->gappoh = gappoh; 788 m->gappov = gappov; 789 m->lt[0] = &layouts[0]; 790 m->lt[1] = &layouts[1 % LENGTH(layouts)]; 791 return m; 792 } 793 794 void 795 destroynotify(XEvent* e) 796 { 797 Client* c; 798 XDestroyWindowEvent* ev; 799 800 ev = &e->xdestroywindow; 801 if ((c = wintoclient(ev->window))) 802 unmanage(c, 1); 803 else if ((c = swallowingclient(ev->window))) 804 unmanage(c->swallowing, 1); 805 } 806 807 void 808 detach(Client* c) 809 { 810 Client** tc; 811 812 for (tc = &c->mon->clients; *tc && *tc != c; tc = &(*tc)->next) ; 813 *tc = c->next; 814 } 815 816 void 817 detachstack(Client* c) 818 { 819 Client **tc, *t; 820 821 for (tc = &c->mon->stack; *tc && *tc != c; tc = &(*tc)->snext); 822 *tc = c->snext; 823 if (c == c->mon->sel) { 824 for (t = c->mon->stack; t && !ISVISIBLE(t); t = t->snext); 825 c->mon->sel = t; 826 } 827 } 828 829 Monitor* dirtomon(int dir) 830 { 831 Monitor* m; 832 833 m = NULL; 834 if (dir > 0) { 835 if (!(m = selmon->next)) 836 m = mons; 837 } else if (selmon == mons) 838 for (m = mons; m->next; m = m->next); 839 else 840 for (m = mons; m->next != selmon; m = m->next); 841 return m; 842 } 843 844 void 845 drawbar(Monitor* m) 846 { 847 int x, w, tw; 848 int boxs, boxw; 849 uint i, occ, urg; 850 Client* c; 851 852 853 if (!m->showbar) 854 return; 855 856 /* draw status first so it can be overdrawn by tags later */ 857 tw = 0; 858 if (m == selmon && *stext) { /* status is only drawn on selected monitor */ 859 drw_setscheme(drw, scheme[SchemeNorm]); 860 tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ 861 drw_text(drw, m->ww - tw - 2 * sp, 0, tw, bh, 0, stext, 0); 862 } 863 864 occ = urg = 0; 865 for (c = m->clients; c; c = c->next) { 866 occ |= c->tags; 867 if (c->isurgent) 868 urg |= c->tags; 869 } 870 boxs = drw->fonts->h / 9; 871 boxw = drw->fonts->h / 6 + 2; 872 x = 0; 873 for (i = 0; i < LENGTH(tags); i++) { 874 if(!(occ & 1 << i || m->tagset[m->seltags] & 1 << i)) 875 continue; 876 w = TEXTW(tags[i]); 877 drw_setscheme( 878 drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm] 879 ); 880 drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i); 881 /* 882 if (occ & 1 << i) 883 drw_rect(drw, x + boxs, boxs, boxw, boxw, 884 m == selmon && selmon->sel && selmon->sel->tags & 1 << i, 885 urg & 1 << i 886 ); 887 */ 888 x += w; 889 } 890 drw_setscheme(drw, scheme[SchemeNorm]); 891 if ((w = m->ww - tw - x) > bh) { 892 if (m->sel) { 893 drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); 894 if (TEXTW(m->sel->name) > w) 895 drw_text(drw, x, 0, w - 2 * sp, bh, lrpad / 2, m->sel->name, 0); 896 else 897 drw_text( 898 drw, x, 0, w - 2 * sp, bh, (w - TEXTW(m->sel->name)) / 2, 899 m->sel->name, 0 900 ); 901 if (m->sel->isfloating) 902 drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0); 903 } else { 904 drw_setscheme(drw, scheme[SchemeNorm]); 905 drw_rect(drw, x, 0, w - 2 * sp, bh, 1, 1); 906 } 907 } 908 drw_map(drw, m->barwin, 0, 0, m->ww, bh); 909 } 910 911 void 912 drawbars(void) 913 { 914 Monitor* m; 915 916 for (m = mons; m; m = m->next) 917 drawbar(m); 918 } 919 920 void 921 enternotify(XEvent* e) 922 { 923 Client* c; 924 Monitor* m; 925 XCrossingEvent* ev; 926 927 ev = &e->xcrossing; 928 if ((ev->mode != NotifyNormal || ev->detail == NotifyInferior) 929 && ev->window != root 930 ) 931 return; 932 c = wintoclient(ev->window); 933 m = c ? c->mon : wintomon(ev->window); 934 if (m != selmon) { 935 unfocus(selmon->sel, 1); 936 selmon = m; 937 } else if (!c || c == selmon->sel) 938 return; 939 focus(c); 940 } 941 942 void 943 expose(XEvent* e) 944 { 945 Monitor* m; 946 XExposeEvent* ev; 947 948 ev = &e->xexpose; 949 if (ev->count == 0 && (m = wintomon(ev->window))) 950 drawbar(m); 951 } 952 953 void 954 fibonacci(Monitor* m) 955 { 956 uint i, n; 957 int cx, cy, cw, ch; 958 int nv, hrest, wrest, r; 959 Client* c; 960 961 for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), ++n); 962 if (!n) 963 return; 964 cx = m->wx + m->gappov; 965 cy = m->wy + m->gappoh; 966 cw = m->ww - 2 * m->gappov; 967 ch = m->wh - 2 * m->gappoh; 968 hrest = wrest = 0; 969 r = 1; 970 for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next)) { 971 if (r) { 972 if ((i % 2 && (ch - m->gappih) / 2 <= (bh + 2 * c->bw)) 973 || (!(i % 2) && (cw - m->gappiv) / 2 <= (bh + 2 * c->bw)) 974 ) 975 r = 0; 976 if (r && i < n - 1) { 977 if (i % 2) { 978 nv = (ch - m->gappih) / 2; 979 hrest = ch - 2 * nv - m->gappih; 980 ch = nv; 981 } else { 982 nv = (cw - m->gappiv) / 2; 983 wrest = cw - 2 * nv - m->gappiv; 984 cw = nv; 985 } 986 } 987 switch (i % 4) { 988 case 2: 989 if (i < n - 1) 990 cw += wrest; 991 case 0: /*fallthrough */ 992 cy += ch + m->gappih; 993 ch += hrest; 994 break; 995 case 1: 996 cx += cw + m->gappiv; 997 cw += wrest; 998 break; 999 case 3: 1000 cx += cw + m->gappiv; 1001 cw -= wrest; 1002 break; 1003 } 1004 if (!i) { 1005 if (n != 1) { 1006 cw = (m->ww - m->gappiv - 2 * m->gappov) 1007 - (m->ww - m->gappiv - 2 * m->gappov) 1008 * (1 - m->mfact) 1009 ; 1010 wrest = 0; 1011 } 1012 cy = m->wy + m->gappoh; 1013 } else if (i == 1) 1014 cw = m->ww - cw - m->gappiv - 2 * m->gappov; 1015 ++i; 1016 } 1017 resize(c, cx, cy, cw - (2 * c->bw), ch - (2 * c->bw), False); 1018 } 1019 } 1020 1021 void 1022 focus(Client* c) 1023 { 1024 if (!c || !ISVISIBLE(c)) 1025 for (c = selmon->stack; c && !ISVISIBLE(c); c = c->snext); 1026 if (selmon->sel && selmon->sel != c) 1027 unfocus(selmon->sel, 0); 1028 if (c) { 1029 if (c->mon != selmon) 1030 selmon = c->mon; 1031 if (c->isurgent) 1032 seturgent(c, 0); 1033 detachstack(c); 1034 attachstack(c); 1035 grabbuttons(c, 1); 1036 XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel); 1037 setfocus(c); 1038 } else { 1039 XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); 1040 XDeleteProperty(dpy, root, netatom[NetActiveWindow]); 1041 } 1042 selmon->sel = c; 1043 drawbars(); 1044 } 1045 1046 /* there are some broken focus acquiring clients needing extra handling */ 1047 void 1048 focusin(XEvent* e) 1049 { 1050 XFocusChangeEvent* ev; 1051 1052 ev = &e->xfocus; 1053 if (selmon->sel && ev->window != selmon->sel->win) 1054 setfocus(selmon->sel); 1055 } 1056 1057 void 1058 focusmon(const Arg* arg) 1059 { 1060 Monitor* m; 1061 1062 if (!mons->next) 1063 return; 1064 if ((m = dirtomon(arg->i)) == selmon) 1065 return; 1066 unfocus(selmon->sel, 0); 1067 selmon = m; 1068 focus(NULL); 1069 if (selmon->sel) 1070 XWarpPointer(dpy, None, selmon->sel->win, 0, 0, 0, 0, selmon->sel->w/2, selmon->sel->h/2); 1071 } 1072 1073 void 1074 focusstack(const Arg* arg) 1075 { 1076 Client* c, *i; 1077 1078 if (!selmon->sel || (selmon->sel->isfullscreen && lockfullscreen)) 1079 return; 1080 if (arg->i > sizeof(arg)) 1081 return; 1082 c = NULL; 1083 if (arg->i > 0) { 1084 for (c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next); 1085 if (!c) 1086 for (c = selmon->clients; c && !ISVISIBLE(c); c = c->next); 1087 } else { 1088 for (i = selmon->clients; i != selmon->sel; i = i->next) 1089 if (ISVISIBLE(i)) 1090 c = i; 1091 if (c == NULL) 1092 for (; i; i = i->next) 1093 if (ISVISIBLE(i)) 1094 c = i; 1095 } 1096 if (c != NULL) { 1097 focus(c); 1098 restack(selmon); 1099 XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w/2, c->h/2); 1100 } 1101 } 1102 1103 Atom 1104 getatomprop(Client* c, Atom prop) 1105 { 1106 int di; 1107 unsigned long dl; 1108 uchar* p; 1109 Atom da, atom; 1110 1111 p = NULL; 1112 atom = None; 1113 if (XGetWindowProperty( 1114 dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM, &da, &di, &dl, &dl, &p 1115 ) == Success && p 1116 ) { 1117 atom = *(Atom*)p; 1118 XFree(p); 1119 } 1120 return atom; 1121 } 1122 1123 int 1124 getrootptr(int* x, int* y) 1125 { 1126 Window dummy; 1127 uint dui; 1128 int di; 1129 1130 return XQueryPointer(dpy, root, &dummy, &dummy, x, y, &di, &di, &dui); 1131 } 1132 1133 long 1134 getstate(Window w) 1135 { 1136 Atom real; 1137 uchar* p; 1138 ulong n, null; 1139 long r; 1140 1141 if (XGetWindowProperty( 1142 dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState], &real, (int*)&null, 1143 &n, &null, (uchar**)&p 1144 ) != Success 1145 ) 1146 return -1; 1147 r = n ? *p : -1; 1148 XFree(p); 1149 return r; 1150 } 1151 1152 int 1153 gettextprop(Window w, Atom atom, char* text, uint size) 1154 { 1155 char** list = NULL; 1156 int n; 1157 XTextProperty name; 1158 1159 if (text == NULL || !size) 1160 return 0; 1161 text[0] = '\0'; 1162 if (!XGetTextProperty(dpy, w, &name, atom)) 1163 return 0; 1164 if (!name.nitems) 1165 return 0; 1166 if (name.encoding == XA_STRING) { 1167 strncpy(text, (char *)name.value, size - 1); 1168 } else if (XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success 1169 && n > 0 && *list 1170 ) { 1171 strncpy(text, *list, size - 1); 1172 XFreeStringList(list); 1173 } 1174 text[size - 1] = '\0'; 1175 XFree(name.value); 1176 return 1; 1177 } 1178 1179 void 1180 grabbuttons(Client* c, int focused) 1181 { 1182 updatenumlockmask(); 1183 { 1184 uint i, j; 1185 uint modifiers[] = { 0, LockMask, numlockmask, numlockmask | LockMask }; 1186 1187 XUngrabButton(dpy, AnyButton, AnyModifier, c->win); 1188 if (!focused) 1189 XGrabButton( 1190 dpy, AnyButton, AnyModifier, c->win, False, BUTTONMASK, GrabModeSync, 1191 GrabModeSync, None, None 1192 ); 1193 for (i = 0; i < LENGTH(buttons); i++) 1194 if (buttons[i].click == ClkClientWin) 1195 for (j = 0; j < LENGTH(modifiers); j++) 1196 XGrabButton( 1197 dpy, buttons[i].button, buttons[i].mask | modifiers[j], c->win, 1198 False, BUTTONMASK, GrabModeAsync, GrabModeSync, None, None 1199 ); 1200 } 1201 } 1202 1203 void 1204 grabkeys(void) 1205 { 1206 updatenumlockmask(); 1207 { 1208 unsigned int i, j, k; 1209 unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; 1210 int start, end, skip; 1211 KeySym *syms; 1212 1213 XUngrabKey(dpy, AnyKey, AnyModifier, root); 1214 XDisplayKeycodes(dpy, &start, &end); 1215 syms = XGetKeyboardMapping(dpy, start, end - start + 1, &skip); 1216 if (!syms) 1217 return; 1218 for (k = start; k <= end; k++) 1219 for (i = 0; i < LENGTH(keys); i++) 1220 /* skip modifier codes, we do that ourselves */ 1221 if (keys[i].keysym == syms[(k - start) * skip]) 1222 for (j = 0; j < LENGTH(modifiers); j++) 1223 XGrabKey(dpy, k, 1224 keys[i].mod | modifiers[j], 1225 root, True, 1226 GrabModeAsync, GrabModeAsync); 1227 XFree(syms); 1228 } 1229 } 1230 1231 #ifdef XINERAMA 1232 static int 1233 isuniquegeom(XineramaScreenInfo* unique, size_t n, XineramaScreenInfo* info) 1234 { 1235 for (;n--;) 1236 if (unique[n].x_org == info->x_org 1237 && unique[n].y_org == info->y_org 1238 && unique[n].width == info->width 1239 && unique[n].height == info->height 1240 ) 1241 return 0; 1242 return 1; 1243 } 1244 #endif /* XINERAMA */ 1245 1246 void 1247 keypress(XEvent* e) 1248 { 1249 uint i; 1250 KeySym keysym; 1251 XKeyEvent* ev; 1252 1253 ev = &e->xkey; 1254 keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0); 1255 for (i = 0; i < LENGTH(keys); i++) 1256 if (keysym == keys[i].keysym 1257 && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state) 1258 && keys[i].func 1259 ) 1260 keys[i].func(&(keys[i].arg)); 1261 } 1262 1263 void 1264 killclient(const Arg* arg) 1265 { 1266 if (!selmon->sel) 1267 return; 1268 if (!sendevent(selmon->sel, wmatom[WMDelete])) { 1269 XGrabServer(dpy); 1270 XSetErrorHandler(xerrordummy); 1271 XSetCloseDownMode(dpy, DestroyAll); 1272 XKillClient(dpy, selmon->sel->win); 1273 XSync(dpy, False); 1274 XSetErrorHandler(xerror); 1275 XUngrabServer(dpy); 1276 } 1277 } 1278 1279 void 1280 manage(Window w, XWindowAttributes* wa) 1281 { 1282 Client* c, *t, *term; 1283 Window trans; 1284 XWindowChanges wc; 1285 1286 t = NULL; 1287 term = NULL; 1288 c = ecalloc(1, sizeof(Client)); 1289 c->win = w; 1290 c->pid = winpid(w); 1291 /* geometry */ 1292 c->x = c->oldx = wa->x; 1293 c->y = c->oldy = wa->y; 1294 c->w = c->oldw = wa->width; 1295 c->h = c->oldh = wa->height; 1296 c->oldbw = wa->border_width; 1297 1298 trans = None; 1299 updatetitle(c); 1300 if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) { 1301 c->mon = t->mon; 1302 c->tags = t->tags; 1303 } else { 1304 c->mon = selmon; 1305 applyrules(c); 1306 term = termforwin(c); 1307 } 1308 1309 if (c->x + WIDTH(c) > c->mon->wx + c->mon->ww) 1310 c->x = c->mon->wx + c->mon->ww - WIDTH(c); 1311 if (c->y + HEIGHT(c) > c->mon->wy + c->mon->wh) 1312 c->y = c->mon->wy + c->mon->wh - HEIGHT(c); 1313 c->x = MAX(c->x, c->mon->wx); 1314 /* only fix client y-offset, if the client center might cover the bar */ 1315 c->y = MAX(c->y, 1316 ((c->mon->by == c->mon->my) 1317 && (c->x + (c->w / 2) >= c->mon->wx) 1318 && (c->x + (c->w / 2) < c->mon->wx + c->mon->ww) 1319 ) ? bh : c->mon->my 1320 ); 1321 c->bw = borderpx; 1322 wc.border_width = c->bw; 1323 XConfigureWindow(dpy, w, CWBorderWidth, &wc); 1324 XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel); 1325 configure(c); /* propagates border_width, if size doesn't change */ 1326 updatewindowtype(c); 1327 updatesizehints(c); 1328 updatewmhints(c); 1329 XSelectInput( 1330 dpy, w, 1331 EnterWindowMask | FocusChangeMask | PropertyChangeMask | StructureNotifyMask 1332 ); 1333 grabbuttons(c, 0); 1334 if (!c->isfloating) 1335 c->isfloating = c->oldstate = trans != None || c->isfixed; 1336 if (c->isfloating) 1337 XRaiseWindow(dpy, c->win); 1338 attach(c); 1339 attachstack(c); 1340 XChangeProperty( 1341 dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, 1342 (uchar*)&(c->win), 1 1343 ); 1344 XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */ 1345 setclientstate(c, NormalState); 1346 if (c->mon == selmon) 1347 unfocus(selmon->sel, 0); 1348 c->mon->sel = c; 1349 arrange(c->mon); 1350 XMapWindow(dpy, c->win); 1351 if (term) 1352 swallow(term, c); 1353 focus(NULL); 1354 } 1355 1356 void 1357 mappingnotify(XEvent* e) 1358 { 1359 XMappingEvent* ev; 1360 1361 ev = &e->xmapping; 1362 XRefreshKeyboardMapping(ev); 1363 if (ev->request == MappingKeyboard) 1364 grabkeys(); 1365 } 1366 1367 void 1368 maprequest(XEvent* e) 1369 { 1370 static XWindowAttributes wa; 1371 XMapRequestEvent* ev = &e->xmaprequest; 1372 1373 if (!XGetWindowAttributes(dpy, ev->window, &wa) || wa.override_redirect) 1374 return; 1375 if (!wintoclient(ev->window)) 1376 manage(ev->window, &wa); 1377 } 1378 1379 void 1380 motionnotify(XEvent* e) 1381 { 1382 static Monitor* mon = NULL; 1383 Monitor* m; 1384 XMotionEvent* ev; 1385 1386 ev = &e->xmotion; 1387 if (ev->window != root) 1388 return; 1389 if ((m = recttomon(ev->x_root, ev->y_root, 1, 1)) != mon && mon) { 1390 unfocus(selmon->sel, 1); 1391 selmon = m; 1392 focus(NULL); 1393 } 1394 mon = m; 1395 } 1396 1397 void 1398 movemouse(const Arg* arg) 1399 { 1400 int x, y, ocx, ocy, nx, ny; 1401 Client* c; 1402 Monitor* m; 1403 XEvent ev; 1404 Time lasttime; 1405 1406 1407 if (!(c = selmon->sel)) 1408 return; 1409 if (c->isfullscreen) /* no support moving fullscreen windows by mouse */ 1410 return; 1411 restack(selmon); 1412 ocx = c->x; 1413 ocy = c->y; 1414 if (XGrabPointer( 1415 dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, None, 1416 cursor[CurMove]->cursor, CurrentTime 1417 ) != GrabSuccess 1418 ) 1419 return; 1420 if (!getrootptr(&x, &y)) 1421 return; 1422 lasttime = 0; 1423 do { 1424 XMaskEvent(dpy, MOUSEMASK | ExposureMask | SubstructureRedirectMask, &ev); 1425 switch (ev.type) { 1426 case ConfigureRequest: 1427 case Expose: 1428 case MapRequest: 1429 handler[ev.type](&ev); 1430 break; 1431 case MotionNotify: 1432 if ((ev.xmotion.time - lasttime) <= (1000 / 60)) 1433 continue; 1434 lasttime = ev.xmotion.time; 1435 1436 nx = ocx + (ev.xmotion.x - x); 1437 ny = ocy + (ev.xmotion.y - y); 1438 if (abs(selmon->wx - nx) < snap) 1439 nx = selmon->wx; 1440 else if (abs((selmon->wx + selmon->ww) - (nx + WIDTH(c))) < snap) 1441 nx = selmon->wx + selmon->ww - WIDTH(c); 1442 if (abs(selmon->wy - ny) < snap) 1443 ny = selmon->wy; 1444 else if (abs((selmon->wy + selmon->wh) - (ny + HEIGHT(c))) < snap) 1445 ny = selmon->wy + selmon->wh - HEIGHT(c); 1446 if (!c->isfloating && selmon->lt[selmon->sellt]->arrange 1447 && (abs(nx - c->x) > snap || abs(ny - c->y) > snap) 1448 ) 1449 togglefloating(NULL); 1450 if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) 1451 resize(c, nx, ny, c->w, c->h, 1); 1452 break; 1453 } 1454 } while (ev.type != ButtonRelease); 1455 XUngrabPointer(dpy, CurrentTime); 1456 if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { 1457 sendmon(c, m); 1458 selmon = m; 1459 focus(NULL); 1460 } 1461 } 1462 1463 Client* 1464 nexttiled(Client* c) 1465 { 1466 for (; c && (c->isfloating || !ISVISIBLE(c)); c = c->next); 1467 return c; 1468 } 1469 1470 void 1471 pop(Client* c) 1472 { 1473 detach(c); 1474 attach(c); 1475 focus(c); 1476 arrange(c->mon); 1477 XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w/2, c->h/2); 1478 } 1479 1480 void 1481 propertynotify(XEvent* e) 1482 { 1483 XPropertyEvent* ev; 1484 Client* c; 1485 Window trans; 1486 1487 ev = &e->xproperty; 1488 if ((ev->window == root) && (ev->atom == XA_WM_NAME)) 1489 updatestatus(); 1490 else if (ev->state == PropertyDelete) 1491 return; 1492 else if ((c = wintoclient(ev->window))) { 1493 switch (ev->atom) { 1494 default: 1495 break; 1496 case XA_WM_TRANSIENT_FOR: 1497 if (!c->isfloating 1498 && XGetTransientForHint(dpy, c->win, &trans) 1499 && (c->isfloating = (wintoclient(trans)) != NULL) 1500 ) 1501 arrange(c->mon); 1502 break; 1503 case XA_WM_NORMAL_HINTS: 1504 updatesizehints(c); 1505 break; 1506 case XA_WM_HINTS: 1507 updatewmhints(c); 1508 drawbars(); 1509 break; 1510 } 1511 if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { 1512 updatetitle(c); 1513 if (c == c->mon->sel) 1514 drawbar(c->mon); 1515 } 1516 if (ev->atom == netatom[NetWMWindowType]) 1517 updatewindowtype(c); 1518 } 1519 } 1520 1521 void 1522 quit(const Arg* arg) 1523 { 1524 running = 0; 1525 } 1526 1527 Monitor* 1528 recttomon(int x, int y, int w, int h) 1529 { 1530 Monitor* m, *r; 1531 int a, area; 1532 1533 r = selmon; 1534 area = 0; 1535 for (m = mons; m; m = m->next) 1536 if ((a = INTERSECT(x, y, w, h, m)) > area) { 1537 area = a; 1538 r = m; 1539 } 1540 return r; 1541 } 1542 1543 void 1544 resize(Client* c, int x, int y, int w, int h, int interact) 1545 { 1546 if (applysizehints(c, &x, &y, &w, &h, interact)) 1547 resizeclient(c, x, y, w, h); 1548 } 1549 1550 void 1551 resizeclient(Client* c, int x, int y, int w, int h) 1552 { 1553 XWindowChanges wc; 1554 1555 c->oldx = c->x; 1556 c->x = wc.x = x; 1557 c->oldy = c->y; 1558 c->y = wc.y = y; 1559 c->oldw = c->w; 1560 c->w = wc.width = w; 1561 c->oldh = c->h; 1562 c->h = wc.height = h; 1563 wc.border_width = c->bw; 1564 XConfigureWindow(dpy, c->win, CWX | CWY | CWWidth | CWHeight | CWBorderWidth, &wc); 1565 configure(c); 1566 XSync(dpy, False); 1567 } 1568 1569 void 1570 resizemouse(const Arg* arg) 1571 { 1572 int ocx, ocy, nw, nh; 1573 Client* c; 1574 Monitor* m; 1575 XEvent ev; 1576 Time lasttime; 1577 1578 if (!(c = selmon->sel)) 1579 return; 1580 if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */ 1581 return; 1582 restack(selmon); 1583 ocx = c->x; 1584 ocy = c->y; 1585 if (XGrabPointer( 1586 dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, None, 1587 cursor[CurResize]->cursor, CurrentTime 1588 ) != GrabSuccess 1589 ) 1590 return; 1591 XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); 1592 lasttime = 0; 1593 do { 1594 XMaskEvent(dpy, MOUSEMASK | ExposureMask | SubstructureRedirectMask, &ev); 1595 switch (ev.type) { 1596 case ConfigureRequest: 1597 case Expose: 1598 case MapRequest: 1599 handler[ev.type](&ev); 1600 break; 1601 case MotionNotify: 1602 if ((ev.xmotion.time - lasttime) <= (1000 / 60)) 1603 continue; 1604 lasttime = ev.xmotion.time; 1605 1606 nw = MAX(ev.xmotion.x - ocx - 2 * c->bw + 1, 1); 1607 nh = MAX(ev.xmotion.y - ocy - 2 * c->bw + 1, 1); 1608 if (c->mon->wx + nw >= selmon->wx 1609 && c->mon->wx + nw <= selmon->wx + selmon->ww 1610 && c->mon->wy + nh >= selmon->wy 1611 && c->mon->wy + nh <= selmon->wy + selmon->wh 1612 ) { 1613 if (!c->isfloating && selmon->lt[selmon->sellt]->arrange 1614 && (abs(nw - c->w) > snap || abs(nh - c->h) > snap) 1615 ) 1616 togglefloating(NULL); 1617 } 1618 if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) 1619 resize(c, c->x, c->y, nw, nh, 1); 1620 break; 1621 } 1622 } while (ev.type != ButtonRelease); 1623 XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); 1624 XUngrabPointer(dpy, CurrentTime); 1625 while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); 1626 if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { 1627 sendmon(c, m); 1628 selmon = m; 1629 focus(NULL); 1630 } 1631 } 1632 1633 void 1634 restack(Monitor* m) 1635 { 1636 Client* c; 1637 XEvent ev; 1638 XWindowChanges wc; 1639 1640 drawbar(m); 1641 if (!m->sel) 1642 return; 1643 if (m->sel->isfloating || !m->lt[m->sellt]->arrange) 1644 XRaiseWindow(dpy, m->sel->win); 1645 if (m->lt[m->sellt]->arrange) { 1646 wc.stack_mode = Below; 1647 wc.sibling = m->barwin; 1648 for (c = m->stack; c; c = c->snext) 1649 if (!c->isfloating && ISVISIBLE(c)) { 1650 XConfigureWindow(dpy, c->win, CWSibling | CWStackMode, &wc); 1651 wc.sibling = c->win; 1652 } 1653 } 1654 XSync(dpy, False); 1655 while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); 1656 } 1657 1658 void 1659 run(void) 1660 { 1661 XEvent ev; 1662 1663 XSync(dpy, False); 1664 while (running && !XNextEvent(dpy, &ev)) 1665 if (handler[ev.type]) 1666 handler[ev.type](&ev); 1667 } 1668 1669 void 1670 scan() 1671 { 1672 uint i, num; 1673 char swin[256]; 1674 Window d1, d2, *wins; 1675 XWindowAttributes wa; 1676 1677 scanner = 1; 1678 wins = NULL; 1679 if (XQueryTree(dpy, root, &d1, &d2, &wins, &num)) { 1680 for (i = 0; i < num; ++i) { 1681 if (!XGetWindowAttributes(dpy, wins[i], &wa) 1682 || wa.override_redirect 1683 || XGetTransientForHint(dpy, wins[i], &d1) 1684 ) 1685 continue; 1686 if (wa.map_state == IsViewable || getstate(wins[i]) == IconicState) 1687 manage(wins[i], &wa); 1688 else if (gettextprop(wins[i], netatom[NetClientList], swin, sizeof swin)) 1689 manage(wins[i], &wa); 1690 } 1691 for (i = 0; i < num; i++) { /* now the transients */ 1692 if (!XGetWindowAttributes(dpy, wins[i], &wa)) 1693 continue; 1694 if (XGetTransientForHint(dpy, wins[i], &d1) 1695 && (wa.map_state == IsViewable || getstate(wins[i]) == IconicState) 1696 ) 1697 manage(wins[i], &wa); 1698 } 1699 if (wins != NULL) 1700 XFree(wins); 1701 } 1702 scanner = 0; 1703 } 1704 1705 void 1706 sendmon(Client* c, Monitor* m) 1707 { 1708 if (c->mon == m) 1709 return; 1710 unfocus(c, 1); 1711 detach(c); 1712 detachstack(c); 1713 c->mon = m; 1714 c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ 1715 attach(c); 1716 attachstack(c); 1717 focus(NULL); 1718 arrange(NULL); 1719 } 1720 1721 void 1722 setclientstate(Client* c, long state) 1723 { 1724 long data[] = { state, None }; 1725 1726 XChangeProperty( 1727 dpy, c->win, wmatom[WMState], wmatom[WMState], 32, PropModeReplace, 1728 (uchar*)data, 2 1729 ); 1730 } 1731 1732 int 1733 sendevent(Client* c, Atom proto) 1734 { 1735 int n; 1736 Atom* protocols; 1737 int exists; 1738 XEvent ev; 1739 1740 exists = 0; 1741 if (XGetWMProtocols(dpy, c->win, &protocols, &n)) { 1742 while (!exists && n--) 1743 exists = protocols[n] == proto; 1744 XFree(protocols); 1745 } 1746 if (exists) { 1747 ev.type = ClientMessage; 1748 ev.xclient.window = c->win; 1749 ev.xclient.message_type = wmatom[WMProtocols]; 1750 ev.xclient.format = 32; 1751 ev.xclient.data.l[0] = proto; 1752 ev.xclient.data.l[1] = CurrentTime; 1753 XSendEvent(dpy, c->win, False, NoEventMask, &ev); 1754 } 1755 return exists; 1756 } 1757 1758 void 1759 setfocus(Client* c) 1760 { 1761 if (!c->neverfocus) { 1762 XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); 1763 XChangeProperty( 1764 dpy, root, netatom[NetActiveWindow], XA_WINDOW, 32, PropModeReplace, 1765 (uchar*)&(c->win), 1 1766 ); 1767 } 1768 sendevent(c, wmatom[WMTakeFocus]); 1769 } 1770 1771 void 1772 setfullscreen(Client* client, int fullscreen) 1773 { 1774 if (fullscreen && !client->isfullscreen) { 1775 XChangeProperty( 1776 dpy, client->win, netatom[NetWMState], XA_ATOM, 32, PropModeReplace, 1777 (uchar*)&netatom[NetWMFullscreen], 1 1778 ); 1779 client->isfullscreen = 1; 1780 client->oldstate = client->isfloating; 1781 client->oldbw = client->bw; 1782 client->bw = 0; 1783 client->isfloating = 1; 1784 resizeclient( 1785 client, client->mon->mx, client->mon->my, client->mon->mw, client->mon->mh 1786 ); 1787 XRaiseWindow(dpy, client->win); 1788 } else if (!fullscreen && client->isfullscreen) { 1789 XChangeProperty( 1790 dpy, client->win, netatom[NetWMState], XA_ATOM, 32, PropModeReplace, 1791 (uchar*)0, 0 1792 ); 1793 client->isfullscreen = 0; 1794 client->isfloating = client->oldstate; 1795 client->bw = client->oldbw; 1796 client->x = client->oldx; 1797 client->y = client->oldy; 1798 client->w = client->oldw; 1799 client->h = client->oldh; 1800 resizeclient(client, client->x, client->y, client->w, client->h); 1801 arrange(client->mon); 1802 } 1803 } 1804 1805 void 1806 setlayout(const Arg* arg) 1807 { 1808 if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) 1809 selmon->sellt ^= 1; 1810 if (arg && arg->v) 1811 selmon->lt[selmon->sellt] = (Layout*)arg->v; 1812 if (selmon->sel) 1813 arrange(selmon); 1814 else 1815 drawbar(selmon); 1816 } 1817 1818 /* arg > 1.0 will set mfact absolutely */ 1819 void 1820 setmfact(const Arg* arg) 1821 { 1822 float f; 1823 1824 if (!arg || !selmon->lt[selmon->sellt]->arrange) 1825 return; 1826 f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0; 1827 if (f < 0.05 || f > 0.95) 1828 return; 1829 selmon->mfact = f; 1830 arrange(selmon); 1831 } 1832 1833 void 1834 setup() 1835 { 1836 XSetWindowAttributes wa; 1837 int i; 1838 Atom utf8string; 1839 struct sigaction sa; 1840 1841 /* do not transform children into zombies when they terminate */ 1842 sigemptyset(&sa.sa_mask); 1843 sa.sa_flags = SA_NOCLDSTOP | SA_NOCLDWAIT | SA_RESTART; 1844 sa.sa_handler = SIG_IGN; 1845 sigaction(SIGCHLD, &sa, NULL); 1846 1847 /* clean up any zombies (inherited from .xinitrc etc) immediately */ 1848 while (waitpid(-1, NULL, WNOHANG) > 0); 1849 1850 /* init screen */ 1851 screen = DefaultScreen(dpy); 1852 sw = DisplayWidth(dpy, screen); 1853 sh = DisplayHeight(dpy, screen); 1854 root = RootWindow(dpy, screen); 1855 xinitvisual(); 1856 drw = drw_create(dpy, screen, root, sw, sh, visual, depth, cmap); 1857 if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) 1858 die("no fonts could be loaded."); 1859 lrpad = drw->fonts->h; 1860 bh = drw->fonts->h + 2; 1861 sp = sidepad; 1862 vp = (topbar == 1) ? vertpad : - vertpad; 1863 updategeom(); 1864 1865 /* init atoms */ 1866 utf8string = XInternAtom(dpy, "UTF8_STRING", False); 1867 wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); 1868 wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); 1869 wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False); 1870 wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False); 1871 netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); 1872 netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); 1873 netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); 1874 netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False); 1875 netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False); 1876 netatom[NetWMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); 1877 netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); 1878 netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); 1879 netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False); 1880 1881 /* init cursors */ 1882 cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr); 1883 cursor[CurResize] = drw_cur_create(drw, XC_sizing); 1884 cursor[CurMove] = drw_cur_create(drw, XC_fleur); 1885 1886 /* init appearance */ 1887 scheme = ecalloc(LENGTH(colors), sizeof(Color*)); 1888 for (i = 0; i < LENGTH(colors); ++i) 1889 scheme[i] = drw_scm_create(drw, colors[i], alphas[i], 3); 1890 1891 /* init bars */ 1892 updatebars(); 1893 updatestatus(); 1894 1895 /* supporting window for NetWMCheck */ 1896 wmcheckwin = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 0, 0, 0); 1897 XChangeProperty( 1898 dpy, wmcheckwin, netatom[NetWMCheck], XA_WINDOW, 32, PropModeReplace, 1899 (uchar*)&wmcheckwin, 1 1900 ); 1901 XChangeProperty( 1902 dpy, wmcheckwin, netatom[NetWMName], utf8string, 8, PropModeReplace, 1903 (uchar*)"dwm", 3 1904 ); 1905 XChangeProperty( 1906 dpy, root, netatom[NetWMCheck], XA_WINDOW, 32, PropModeReplace, 1907 (uchar*)&wmcheckwin, 1 1908 ); 1909 1910 /* EWMH support per view */ 1911 XChangeProperty( 1912 dpy, root, netatom[NetSupported], XA_ATOM, 32, PropModeReplace, 1913 (uchar*)netatom, NetLast 1914 ); 1915 XDeleteProperty(dpy, root, netatom[NetClientList]); 1916 1917 /* select events */ 1918 wa.cursor = cursor[CurNormal]->cursor; 1919 wa.event_mask = 1920 SubstructureRedirectMask | SubstructureNotifyMask | ButtonPressMask 1921 | PointerMotionMask | EnterWindowMask | LeaveWindowMask | StructureNotifyMask 1922 | PropertyChangeMask 1923 ; 1924 XChangeWindowAttributes(dpy, root, CWEventMask | CWCursor, &wa); 1925 XSelectInput(dpy, root, wa.event_mask); 1926 grabkeys(); 1927 focus(NULL); 1928 } 1929 1930 void 1931 seturgent(Client* c, int urg) 1932 { 1933 XWMHints* wmh; 1934 1935 if (urg && !selmon->showbar) { 1936 selmon->showbar = 2; 1937 updatebar(); 1938 } 1939 c->isurgent = urg; 1940 if (!(wmh = XGetWMHints(dpy, c->win))) 1941 return; 1942 wmh->flags = urg ? (wmh->flags | XUrgencyHint) : (wmh->flags & ~XUrgencyHint); 1943 XSetWMHints(dpy, c->win, wmh); 1944 XFree(wmh); 1945 } 1946 1947 void 1948 showhide(Client* c) 1949 { 1950 if (!c) 1951 return; 1952 if (ISVISIBLE(c)) { 1953 /* show clients top down */ 1954 XMoveWindow(dpy, c->win, c->x, c->y); 1955 if ((!c->mon->lt[c->mon->sellt]->arrange || c->isfloating) && !c->isfullscreen) 1956 resize(c, c->x, c->y, c->w, c->h, 0); 1957 showhide(c->snext); 1958 } else { 1959 /* hide clients bottom up */ 1960 showhide(c->snext); 1961 XMoveWindow(dpy, c->win, WIDTH(c) * -2, c->y); 1962 } 1963 } 1964 1965 void 1966 sigchld(int unused) 1967 { 1968 if (signal(SIGCHLD, sigchld) == SIG_ERR) 1969 die("can't install SIGCHLD handler:"); 1970 while (0 < waitpid(-1, NULL, WNOHANG)); 1971 } 1972 1973 void 1974 spawn(const Arg *arg) 1975 { 1976 struct sigaction sa; 1977 1978 if (arg->v == dmenucmd) 1979 dmenumon[0] = '0' + selmon->num; 1980 if (fork() == 0) { 1981 if (dpy) 1982 close(ConnectionNumber(dpy)); 1983 setsid(); 1984 1985 sigemptyset(&sa.sa_mask); 1986 sa.sa_flags = 0; 1987 sa.sa_handler = SIG_DFL; 1988 sigaction(SIGCHLD, &sa, NULL); 1989 1990 execvp(((char **)arg->v)[0], (char **)arg->v); 1991 die("dwm: execvp '%s' failed:", ((char **)arg->v)[0]); 1992 } 1993 } 1994 1995 void 1996 tag(const Arg* arg) 1997 { 1998 if (selmon->sel && arg->ui & TAGMASK) { 1999 selmon->sel->tags = arg->ui & TAGMASK; 2000 focus(NULL); 2001 arrange(selmon); 2002 } 2003 } 2004 2005 void 2006 tagmon(const Arg* arg) 2007 { 2008 if (!selmon->sel || !mons->next) 2009 return; 2010 sendmon(selmon->sel, dirtomon(arg->i)); 2011 } 2012 2013 void 2014 togglebar(const Arg* arg) 2015 { 2016 selmon->showbar = selmon->showbar ? 0 : 2; 2017 updatebar(); 2018 } 2019 2020 void 2021 togglefloating(const Arg* arg) 2022 { 2023 if (!selmon->sel) 2024 return; 2025 if (selmon->sel->isfullscreen) 2026 return; /* no support for fullscreen windows */ 2027 selmon->sel->isfloating = !selmon->sel->isfloating || selmon->sel->isfixed; 2028 if (selmon->sel->isfloating) 2029 resize( 2030 selmon->sel, selmon->sel->x, selmon->sel->y, selmon->sel->w, 2031 selmon->sel->h, 0 2032 ); 2033 arrange(selmon); 2034 } 2035 2036 void 2037 togglefullscr(const Arg* arg) 2038 { 2039 if (selmon->sel) 2040 setfullscreen(selmon->sel, !selmon->sel->isfullscreen); 2041 } 2042 2043 void 2044 toggletag(const Arg* arg) 2045 { 2046 uint newtags; 2047 2048 if (!selmon->sel) 2049 return; 2050 newtags = selmon->sel->tags ^ (arg->ui & TAGMASK); 2051 if (newtags) { 2052 selmon->sel->tags = newtags; 2053 focus(NULL); 2054 arrange(selmon); 2055 } 2056 } 2057 2058 void 2059 toggleview(const Arg* arg) 2060 { 2061 uint newtagset; 2062 2063 newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK); 2064 if (newtagset) { 2065 selmon->tagset[selmon->seltags] = newtagset; 2066 focus(NULL); 2067 arrange(selmon); 2068 } 2069 } 2070 2071 void 2072 unfocus(Client* c, int setfocus) 2073 { 2074 if (!c) 2075 return; 2076 grabbuttons(c, 0); 2077 XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel); 2078 if (setfocus) { 2079 XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); 2080 XDeleteProperty(dpy, root, netatom[NetActiveWindow]); 2081 } 2082 } 2083 2084 void 2085 unmanage(Client* c, int destroyed) 2086 { 2087 Monitor* m; 2088 Client* s; 2089 XWindowChanges wc; 2090 2091 m = c->mon; 2092 if (c->swallowing) { 2093 unswallow(c); 2094 return; 2095 } 2096 s = swallowingclient(c->win); 2097 if (s) { 2098 free(s->swallowing); 2099 s->swallowing = NULL; 2100 arrange(m); 2101 focus(NULL); 2102 return; 2103 } 2104 detach(c); 2105 detachstack(c); 2106 if (!destroyed) { 2107 wc.border_width = c->oldbw; 2108 XGrabServer(dpy); /* avoid race conditions */ 2109 XSetErrorHandler(xerrordummy); 2110 XSelectInput(dpy, c->win, NoEventMask); 2111 XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */ 2112 XUngrabButton(dpy, AnyButton, AnyModifier, c->win); 2113 setclientstate(c, WithdrawnState); 2114 XSync(dpy, False); 2115 XSetErrorHandler(xerror); 2116 XUngrabServer(dpy); 2117 } 2118 free(c); 2119 if (!s) { 2120 arrange(m); 2121 focus(NULL); 2122 updateclientlist(); 2123 } 2124 } 2125 2126 void 2127 updatebar(void) 2128 { 2129 updatebarpos(selmon); 2130 XMoveResizeWindow( 2131 dpy, selmon->barwin, selmon->wx + sp, selmon->by + vp, selmon->ww - 2 * sp, bh 2132 ); 2133 arrange(selmon); 2134 } 2135 2136 void 2137 updatebarpos(Monitor* m) 2138 { 2139 m->wy = m->my; 2140 m->wh = m->mh; 2141 if (m->showbar) { 2142 m->wh = m->wh - vertpad - bh; 2143 m->by = m->topbar ? m->wy : m->wy + m->wh + vertpad; 2144 m->wy = m->topbar ? m->wy + bh + vp : m->wy; 2145 } else 2146 m->by = -bh - vp; 2147 } 2148 2149 void 2150 updatebars() 2151 { 2152 XClassHint ch = { "dwm", "dwm" }; 2153 XSetWindowAttributes wa; 2154 Monitor* m; 2155 2156 wa.override_redirect = True; 2157 wa.background_pixel = 0; 2158 wa.border_pixel = 0; 2159 wa.colormap = cmap; 2160 wa.event_mask = ButtonPressMask | ExposureMask; 2161 for (m = mons; m; m = m->next) { 2162 if (m->barwin) 2163 continue; 2164 m->barwin = XCreateWindow( 2165 dpy, root, m->wx + sp, m->by + vp, m->ww - 2 * sp, bh, 0, depth, 2166 InputOutput, visual, 2167 CWOverrideRedirect|CWBackPixel|CWBorderPixel|CWColormap|CWEventMask, &wa 2168 ); 2169 XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); 2170 XMapRaised(dpy, m->barwin); 2171 XSetClassHint(dpy, m->barwin, &ch); 2172 } 2173 } 2174 2175 void 2176 unmapnotify(XEvent* e) 2177 { 2178 Client* c; 2179 XUnmapEvent* ev; 2180 2181 ev = &e->xunmap; 2182 if ((c = wintoclient(ev->window))) { 2183 if (ev->send_event) 2184 setclientstate(c, WithdrawnState); 2185 else 2186 unmanage(c, 0); 2187 } 2188 } 2189 2190 void 2191 updateclientlist() 2192 { 2193 Client* c; 2194 Monitor* m; 2195 2196 XDeleteProperty(dpy, root, netatom[NetClientList]); 2197 for (m = mons; m; m = m->next) 2198 for (c = m->clients; c; c = c->next) 2199 XChangeProperty( 2200 dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, 2201 (uchar*)&(c->win), 1 2202 ); 2203 } 2204 2205 int 2206 updategeom() 2207 { 2208 int dirty; 2209 2210 dirty = 0; 2211 #ifdef XINERAMA 2212 if (XineramaIsActive(dpy)) { 2213 XineramaScreenInfo* info; 2214 XineramaScreenInfo* unique; 2215 Client* c; 2216 Monitor* m; 2217 int i, j, n, nn; 2218 2219 info = XineramaQueryScreens(dpy, &nn); 2220 unique = NULL; 2221 for (n = 0, m = mons; m; m = m->next, n++); 2222 /* only consider unique geometries as separate screens */ 2223 unique = ecalloc(nn, sizeof(XineramaScreenInfo)); 2224 for (i = 0, j = 0; i < nn; ++i) 2225 if (isuniquegeom(unique, j, &info[i])) 2226 memcpy(&unique[j++], &info[i], 2227 sizeof(XineramaScreenInfo) 2228 ); 2229 XFree(info); 2230 nn = j; 2231 /* new monitors if nn > n */ 2232 for (i = n; i < nn; i++) { 2233 for (m = mons; m && m->next; m = m->next); 2234 if (m) 2235 m->next = createmon(); 2236 else 2237 mons = createmon(); 2238 } 2239 for (i = 0, m = mons; i < nn && m; m = m->next, i++) 2240 if (i >= n 2241 || unique[i].x_org != m->mx || unique[i].y_org != m->my 2242 || unique[i].width != m->mw || unique[i].height != m->mh) 2243 { 2244 dirty = 1; 2245 m->num = i; 2246 m->mx = m->wx = unique[i].x_org; 2247 m->my = m->wy = unique[i].y_org; 2248 m->mw = m->ww = unique[i].width; 2249 m->mh = m->wh = unique[i].height; 2250 updatebarpos(m); 2251 } 2252 /* removed monitors if n > nn */ 2253 for (i = nn; i < n; i++) { 2254 for (m = mons; m && m->next; m = m->next); 2255 while ((c = m->clients)) { 2256 dirty = 1; 2257 m->clients = c->next; 2258 detachstack(c); 2259 c->mon = mons; 2260 attach(c); 2261 attachstack(c); 2262 } 2263 if (m == selmon) 2264 selmon = mons; 2265 cleanupmon(m); 2266 } 2267 free(unique); 2268 } else 2269 #endif /* XINERAMA */ 2270 { /* default monitor setup */ 2271 if (!mons) 2272 mons = createmon(); 2273 if (mons->mw != sw || mons->mh != sh) { 2274 dirty = 1; 2275 mons->mw = mons->ww = sw; 2276 mons->mh = mons->wh = sh; 2277 updatebarpos(mons); 2278 } 2279 } 2280 if (dirty) { 2281 selmon = mons; 2282 selmon = wintomon(root); 2283 } 2284 return dirty; 2285 } 2286 2287 void 2288 updatenumlockmask(void) 2289 { 2290 XModifierKeymap* modmap; 2291 uint i, j; 2292 2293 numlockmask = 0; 2294 modmap = XGetModifierMapping(dpy); 2295 for (i = 0; i < 8; ++i) 2296 for (j = 0; j < modmap->max_keypermod; ++j) 2297 if (modmap->modifiermap[i * modmap->max_keypermod + j] 2298 == XKeysymToKeycode(dpy, XK_Num_Lock) 2299 ) 2300 numlockmask = (1 << i); 2301 XFreeModifiermap(modmap); 2302 } 2303 2304 void 2305 updatesizehints(Client* c) 2306 { 2307 long msize; 2308 XSizeHints size; 2309 2310 if (!XGetWMNormalHints(dpy, c->win, &size, &msize)) /* size is uninitialized, ensure that size.flags aren't used */ 2311 size.flags = PSize; 2312 if (size.flags & PBaseSize) { 2313 c->basew = size.base_width; 2314 c->baseh = size.base_height; 2315 } else if (size.flags & PMinSize) { 2316 c->basew = size.min_width; 2317 c->baseh = size.min_height; 2318 } else 2319 c->basew = c->baseh = 0; 2320 if (size.flags & PResizeInc) { 2321 c->incw = size.width_inc; 2322 c->inch = size.height_inc; 2323 } else 2324 c->incw = c->inch = 0; 2325 if (size.flags & PMaxSize) { 2326 c->maxw = size.max_width; 2327 c->maxh = size.max_height; 2328 } else 2329 c->maxw = c->maxh = 0; 2330 if (size.flags & PMinSize) { 2331 c->minw = size.min_width; 2332 c->minh = size.min_height; 2333 } else if (size.flags & PBaseSize) { 2334 c->minw = size.base_width; 2335 c->minh = size.base_height; 2336 } else 2337 c->minw = c->minh = 0; 2338 if (size.flags & PAspect) { 2339 c->mina = (float)size.min_aspect.y / size.min_aspect.x; 2340 c->maxa = (float)size.max_aspect.x / size.max_aspect.y; 2341 } else 2342 c->maxa = c->mina = 0.0; 2343 c->isfixed = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh); 2344 c->hintsvalid = 1; 2345 } 2346 2347 void 2348 updatestatus() 2349 { 2350 if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) 2351 *stext = '\0'; 2352 drawbar(selmon); 2353 } 2354 2355 void 2356 updatetitle(Client* c) 2357 { 2358 if (!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name)) 2359 gettextprop(c->win, XA_WM_NAME, c->name, sizeof c->name); 2360 if (c->name[0] == '\0') /* hack to mark broken clients */ 2361 strcpy(c->name, broken); 2362 } 2363 2364 void 2365 updatewindowtype(Client* c) 2366 { 2367 Atom state; 2368 Atom wtype; 2369 2370 2371 state = getatomprop(c, netatom[NetWMState]); 2372 if (state == netatom[NetWMFullscreen]) 2373 setfullscreen(c, 1); 2374 wtype = getatomprop(c, netatom[NetWMWindowType]); 2375 if (wtype == netatom[NetWMWindowTypeDialog]) 2376 c->isfloating = 1; 2377 } 2378 2379 void 2380 updatewmhints(Client* c) 2381 { 2382 XWMHints* wmh; 2383 2384 if ((wmh = XGetWMHints(dpy, c->win))) { 2385 if (c == selmon->sel && wmh->flags & XUrgencyHint) { 2386 wmh->flags &= ~XUrgencyHint; 2387 XSetWMHints(dpy, c->win, wmh); 2388 } else 2389 c->isurgent = (wmh->flags & XUrgencyHint) ? 1 : 0; 2390 if (wmh->flags & InputHint) 2391 c->neverfocus = !wmh->input; 2392 else 2393 c->neverfocus = 0; 2394 XFree(wmh); 2395 } 2396 } 2397 2398 void 2399 view(const Arg* arg) 2400 { 2401 if (!selmon->showbar) { 2402 selmon->showbar = 1; 2403 updatebar(); 2404 } 2405 if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) 2406 return; 2407 selmon->seltags ^= 1; /* toggle sel tagset */ 2408 if (arg->ui & TAGMASK) 2409 selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; 2410 focus(NULL); 2411 arrange(selmon); 2412 } 2413 2414 pid_t 2415 winpid(Window w) 2416 { 2417 xcb_res_client_id_spec_t spec; 2418 xcb_res_query_client_ids_cookie_t c; 2419 xcb_generic_error_t* e; 2420 xcb_res_query_client_ids_reply_t* r; 2421 xcb_res_client_id_value_iterator_t i; 2422 pid_t result; 2423 ulong* t; 2424 2425 spec.client = w; 2426 spec.mask = XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID; 2427 c = xcb_res_query_client_ids(xcon, 1, &spec); 2428 e = NULL; 2429 r = xcb_res_query_client_ids_reply(xcon, c, &e); 2430 2431 if (!r) 2432 return 0; 2433 i = xcb_res_query_client_ids_ids_iterator(r); 2434 result = 0; 2435 for (; i.rem; xcb_res_client_id_value_next(&i)) { 2436 spec = i.data->spec; 2437 if (spec.mask & XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID) { 2438 t = (ulong*)xcb_res_client_id_value_value(i.data); 2439 result = *t; 2440 break; 2441 } 2442 } 2443 free(r); 2444 if (result == (pid_t) - 1) 2445 result = 0; 2446 return result; 2447 } 2448 2449 pid_t 2450 getparentprocess(pid_t p) 2451 { 2452 FILE* f; 2453 char buf[256]; 2454 uint v; 2455 2456 snprintf(buf, sizeof(buf) - 1, "/proc/%u/stat", (uint)p); 2457 if (!(f = fopen(buf, "r"))) 2458 return 0; 2459 v = 0; 2460 fscanf(f, "%*u %*s %*c %u", &v); 2461 fclose(f); 2462 return (pid_t)v; 2463 } 2464 2465 int 2466 isdescprocess(pid_t p, pid_t c) 2467 { 2468 for (;p != c && c; c = getparentprocess(c)); 2469 return (int)c; 2470 } 2471 2472 Client* 2473 termforwin(const Client* w) 2474 { 2475 Client* c; 2476 Monitor* m; 2477 2478 if (!w->pid || w->isterminal) 2479 return NULL; 2480 for (m = mons; m; m = m->next) 2481 for (c = m->clients; c; c = c->next) 2482 if (c->isterminal && !c->swallowing && c->pid 2483 && isdescprocess(c->pid, w->pid) 2484 ) 2485 return c; 2486 return NULL; 2487 } 2488 2489 Client* 2490 swallowingclient(Window w) 2491 { 2492 Client* c; 2493 Monitor* m; 2494 2495 for (m = mons; m; m = m->next) 2496 for (c = m->clients; c; c = c->next) 2497 if (c->swallowing && c->swallowing->win == w) 2498 return c; 2499 return NULL; 2500 } 2501 2502 Client* 2503 wintoclient(Window w) { 2504 Client* c; 2505 Monitor* m; 2506 2507 for (m = mons; m; m = m->next) 2508 for (c = m->clients; c; c = c->next) 2509 if (c->win == w) 2510 return c; 2511 return NULL; 2512 } 2513 2514 Monitor* wintomon(Window w) { 2515 int x, y; 2516 Client* c; 2517 Monitor* m; 2518 2519 if (w == root && getrootptr(&x, &y)) 2520 return recttomon(x, y, 1, 1); 2521 for (m = mons; m; m = m->next) 2522 if (w == m->barwin) 2523 return m; 2524 if ((c = wintoclient(w))) 2525 return c->mon; 2526 return selmon; 2527 } 2528 2529 /* There's no way to check accesses to destroyed windows, thus those cases are 2530 * ignored (especially on UnmapNotify's). Other types of errors call Xlibs 2531 * default error handler, which may call exit. */ 2532 int 2533 xerror(Display* dpy, XErrorEvent* error_event) 2534 { 2535 if (error_event->error_code == BadWindow 2536 || (error_event->request_code == X_SetInputFocus 2537 && error_event->error_code == BadMatch) 2538 || (error_event->request_code == X_PolyText8 2539 && error_event->error_code == BadDrawable) 2540 || (error_event->request_code == X_PolyFillRectangle 2541 && error_event->error_code == BadDrawable) 2542 || (error_event->request_code == X_PolySegment 2543 && error_event->error_code == BadDrawable) 2544 || (error_event->request_code == X_ConfigureWindow 2545 && error_event->error_code == BadMatch) 2546 || (error_event->request_code == X_GrabButton 2547 && error_event->error_code == BadAccess) 2548 || (error_event->request_code == X_GrabKey 2549 && error_event->error_code == BadAccess) 2550 || (error_event->request_code == X_CopyArea 2551 && error_event->error_code == BadDrawable) 2552 ) 2553 return 0; 2554 fprintf( 2555 stderr, "dwm: fatal error: request code=%d, error code=%d\n", 2556 error_event->request_code, error_event->error_code 2557 ); 2558 return xerrorxlib(dpy, error_event); /* may call exit */ 2559 } 2560 2561 int 2562 xerrordummy(Display* dpy, XErrorEvent* error_event) { 2563 return 0; 2564 } 2565 2566 /* Startup Error handler to check if another window manager 2567 * is already running. */ 2568 int 2569 xerrorstart(Display* dpy, XErrorEvent* error_event) 2570 { 2571 die("dwm: another window manager is already running"); 2572 return -1; 2573 } 2574 2575 void 2576 xinitvisual() 2577 { 2578 XVisualInfo* info, v; 2579 XRenderPictFormat* fmt; 2580 long masks; 2581 int i, n; 2582 2583 masks = VisualScreenMask | VisualDepthMask | VisualClassMask; 2584 v.screen = screen; 2585 v.depth = 32; 2586 v.class = TrueColor; 2587 info = XGetVisualInfo(dpy, masks, &v, &n); 2588 visual = NULL; 2589 for (i = 0; i < n; ++i) { 2590 fmt = XRenderFindVisualFormat(dpy, info[i].visual); 2591 if (fmt->type == PictTypeDirect && fmt->direct.alphaMask) { 2592 visual = info[i].visual; 2593 depth = info[i].depth; 2594 cmap = XCreateColormap(dpy, root, visual, AllocNone); 2595 break; 2596 } 2597 } 2598 XFree(info); 2599 if (visual == NULL) { 2600 visual = DefaultVisual(dpy, screen); 2601 depth = DefaultDepth(dpy, screen); 2602 cmap = DefaultColormap(dpy, screen); 2603 } 2604 } 2605 2606 void 2607 zoom(const Arg* arg) 2608 { 2609 Client* c = selmon->sel; 2610 2611 if (!selmon->lt[selmon->sellt]->arrange || !c || c->isfloating) 2612 return; 2613 if (c == nexttiled(selmon->clients) && !(c = nexttiled(c->next))) 2614 return; 2615 pop(c); 2616 } 2617 2618 int 2619 main(int argc, char* argv[]) 2620 { 2621 if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) 2622 fputs("warning: no locale support\n", stderr); 2623 if (!(dpy = XOpenDisplay(NULL))) 2624 die("dwm: cannot open dpy"); 2625 if (!(xcon = XGetXCBConnection(dpy))) 2626 die("dwm: cannot get xcb connection\n"); 2627 checkotherwm(); 2628 setup(); 2629 scan(); 2630 run(); 2631 cleanup(); 2632 XCloseDisplay(dpy); 2633 return EXIT_SUCCESS; 2634 }