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