dwm.c (55068B)
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 drw_scm_free(drw, scheme[i], 3); 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 if (dl > 0) 894 atom = *(Atom *)p; 895 XFree(p); 896 } 897 return atom; 898 } 899 900 int 901 getrootptr(int *x, int *y) 902 { 903 int di; 904 unsigned int dui; 905 Window dummy; 906 907 return XQueryPointer(dpy, root, &dummy, &dummy, x, y, &di, &di, &dui); 908 } 909 910 long 911 getstate(Window w) 912 { 913 int format; 914 long result = -1; 915 unsigned char *p = NULL; 916 unsigned long n, extra; 917 Atom real; 918 919 if (XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState], 920 &real, &format, &n, &extra, (unsigned char **)&p) != Success) 921 return -1; 922 if (n != 0) 923 result = *p; 924 XFree(p); 925 return result; 926 } 927 928 int 929 gettextprop(Window w, Atom atom, char *text, unsigned int size) 930 { 931 char **list = NULL; 932 int n; 933 XTextProperty name; 934 935 if (!text || size == 0) 936 return 0; 937 text[0] = '\0'; 938 if (!XGetTextProperty(dpy, w, &name, atom) || !name.nitems) 939 return 0; 940 if (name.encoding == XA_STRING) { 941 strncpy(text, (char *)name.value, size - 1); 942 } else if (XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success && n > 0 && *list) { 943 strncpy(text, *list, size - 1); 944 XFreeStringList(list); 945 } 946 text[size - 1] = '\0'; 947 XFree(name.value); 948 return 1; 949 } 950 951 void 952 grabbuttons(Client *c, int focused) 953 { 954 updatenumlockmask(); 955 { 956 unsigned int i, j; 957 unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; 958 XUngrabButton(dpy, AnyButton, AnyModifier, c->win); 959 if (!focused) 960 XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, 961 BUTTONMASK, GrabModeSync, GrabModeSync, None, None); 962 for (i = 0; i < LENGTH(buttons); i++) 963 if (buttons[i].click == ClkClientWin) 964 for (j = 0; j < LENGTH(modifiers); j++) 965 XGrabButton(dpy, buttons[i].button, 966 buttons[i].mask | modifiers[j], 967 c->win, False, BUTTONMASK, 968 GrabModeAsync, GrabModeSync, None, None); 969 } 970 } 971 972 void 973 grabkeys(void) 974 { 975 updatenumlockmask(); 976 { 977 unsigned int i, j, k; 978 unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; 979 int start, end, skip; 980 KeySym *syms; 981 982 XUngrabKey(dpy, AnyKey, AnyModifier, root); 983 XDisplayKeycodes(dpy, &start, &end); 984 syms = XGetKeyboardMapping(dpy, start, end - start + 1, &skip); 985 if (!syms) 986 return; 987 for (k = start; k <= end; k++) 988 for (i = 0; i < LENGTH(keys); i++) 989 /* skip modifier codes, we do that ourselves */ 990 if (keys[i].keysym == syms[(k - start) * skip]) 991 for (j = 0; j < LENGTH(modifiers); j++) 992 XGrabKey(dpy, k, 993 keys[i].mod | modifiers[j], 994 root, True, 995 GrabModeAsync, GrabModeAsync); 996 XFree(syms); 997 } 998 } 999 1000 void 1001 incnmaster(const Arg *arg) 1002 { 1003 selmon->nmaster = MAX(selmon->nmaster + arg->i, 0); 1004 arrange(selmon); 1005 } 1006 1007 #ifdef XINERAMA 1008 static int 1009 isuniquegeom(XineramaScreenInfo *unique, size_t n, XineramaScreenInfo *info) 1010 { 1011 while (n--) 1012 if (unique[n].x_org == info->x_org && unique[n].y_org == info->y_org 1013 && unique[n].width == info->width && unique[n].height == info->height) 1014 return 0; 1015 return 1; 1016 } 1017 #endif /* XINERAMA */ 1018 1019 void 1020 keypress(XEvent *e) 1021 { 1022 unsigned int i; 1023 KeySym keysym; 1024 XKeyEvent *ev; 1025 1026 ev = &e->xkey; 1027 keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0); 1028 for (i = 0; i < LENGTH(keys); i++) 1029 if (keysym == keys[i].keysym 1030 && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state) 1031 && keys[i].func) 1032 keys[i].func(&(keys[i].arg)); 1033 } 1034 1035 void 1036 killclient(const Arg *arg) 1037 { 1038 if (!selmon->sel) 1039 return; 1040 if (!sendevent(selmon->sel, wmatom[WMDelete])) { 1041 XGrabServer(dpy); 1042 XSetErrorHandler(xerrordummy); 1043 XSetCloseDownMode(dpy, DestroyAll); 1044 XKillClient(dpy, selmon->sel->win); 1045 XSync(dpy, False); 1046 XSetErrorHandler(xerror); 1047 XUngrabServer(dpy); 1048 } 1049 } 1050 1051 void 1052 manage(Window w, XWindowAttributes *wa) 1053 { 1054 Client *c, *t = NULL; 1055 Window trans = None; 1056 XWindowChanges wc; 1057 1058 c = ecalloc(1, sizeof(Client)); 1059 c->win = w; 1060 /* geometry */ 1061 c->x = c->oldx = wa->x; 1062 c->y = c->oldy = wa->y; 1063 c->w = c->oldw = wa->width; 1064 c->h = c->oldh = wa->height; 1065 c->oldbw = wa->border_width; 1066 1067 updatetitle(c); 1068 if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) { 1069 c->mon = t->mon; 1070 c->tags = t->tags; 1071 } else { 1072 c->mon = selmon; 1073 applyrules(c); 1074 } 1075 1076 if (c->x + WIDTH(c) > c->mon->wx + c->mon->ww) 1077 c->x = c->mon->wx + c->mon->ww - WIDTH(c); 1078 if (c->y + HEIGHT(c) > c->mon->wy + c->mon->wh) 1079 c->y = c->mon->wy + c->mon->wh - HEIGHT(c); 1080 c->x = MAX(c->x, c->mon->wx); 1081 c->y = MAX(c->y, c->mon->wy); 1082 c->bw = borderpx; 1083 1084 wc.border_width = c->bw; 1085 XConfigureWindow(dpy, w, CWBorderWidth, &wc); 1086 XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel); 1087 configure(c); /* propagates border_width, if size doesn't change */ 1088 updatewindowtype(c); 1089 updatesizehints(c); 1090 updatewmhints(c); 1091 XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask); 1092 grabbuttons(c, 0); 1093 if (!c->isfloating) 1094 c->isfloating = c->oldstate = trans != None || c->isfixed; 1095 if (c->isfloating) 1096 XRaiseWindow(dpy, c->win); 1097 attach(c); 1098 attachstack(c); 1099 XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, 1100 (unsigned char *) &(c->win), 1); 1101 XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */ 1102 setclientstate(c, NormalState); 1103 if (c->mon == selmon) 1104 unfocus(selmon->sel, 0); 1105 c->mon->sel = c; 1106 arrange(c->mon); 1107 XMapWindow(dpy, c->win); 1108 focus(NULL); 1109 } 1110 1111 void 1112 mappingnotify(XEvent *e) 1113 { 1114 XMappingEvent *ev = &e->xmapping; 1115 1116 XRefreshKeyboardMapping(ev); 1117 if (ev->request == MappingKeyboard) 1118 grabkeys(); 1119 } 1120 1121 void 1122 maprequest(XEvent *e) 1123 { 1124 static XWindowAttributes wa; 1125 XMapRequestEvent *ev = &e->xmaprequest; 1126 1127 if (!XGetWindowAttributes(dpy, ev->window, &wa) || wa.override_redirect) 1128 return; 1129 if (!wintoclient(ev->window)) 1130 manage(ev->window, &wa); 1131 } 1132 1133 void 1134 monocle(Monitor *m) 1135 { 1136 unsigned int n = 0; 1137 Client *c; 1138 1139 for (c = m->clients; c; c = c->next) 1140 if (ISVISIBLE(c)) 1141 n++; 1142 if (n > 0) /* override layout symbol */ 1143 snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n); 1144 for (c = nexttiled(m->clients); c; c = nexttiled(c->next)) 1145 resize(c, m->wx, m->wy, m->ww - 2 * c->bw, m->wh - 2 * c->bw, 0); 1146 } 1147 1148 void 1149 motionnotify(XEvent *e) 1150 { 1151 static Monitor *mon = NULL; 1152 Monitor *m; 1153 XMotionEvent *ev = &e->xmotion; 1154 1155 if (ev->window != root) 1156 return; 1157 if ((m = recttomon(ev->x_root, ev->y_root, 1, 1)) != mon && mon) { 1158 unfocus(selmon->sel, 1); 1159 selmon = m; 1160 focus(NULL); 1161 } 1162 mon = m; 1163 } 1164 1165 void 1166 movemouse(const Arg *arg) 1167 { 1168 int x, y, ocx, ocy, nx, ny; 1169 Client *c; 1170 Monitor *m; 1171 XEvent ev; 1172 Time lasttime = 0; 1173 1174 if (!(c = selmon->sel)) 1175 return; 1176 if (c->isfullscreen) /* no support moving fullscreen windows by mouse */ 1177 return; 1178 restack(selmon); 1179 ocx = c->x; 1180 ocy = c->y; 1181 if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, 1182 None, cursor[CurMove]->cursor, CurrentTime) != GrabSuccess) 1183 return; 1184 if (!getrootptr(&x, &y)) 1185 return; 1186 do { 1187 XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); 1188 switch(ev.type) { 1189 case ConfigureRequest: 1190 case Expose: 1191 case MapRequest: 1192 handler[ev.type](&ev); 1193 break; 1194 case MotionNotify: 1195 if ((ev.xmotion.time - lasttime) <= (1000 / refreshrate)) 1196 continue; 1197 lasttime = ev.xmotion.time; 1198 1199 nx = ocx + (ev.xmotion.x - x); 1200 ny = ocy + (ev.xmotion.y - y); 1201 if (abs(selmon->wx - nx) < snap) 1202 nx = selmon->wx; 1203 else if (abs((selmon->wx + selmon->ww) - (nx + WIDTH(c))) < snap) 1204 nx = selmon->wx + selmon->ww - WIDTH(c); 1205 if (abs(selmon->wy - ny) < snap) 1206 ny = selmon->wy; 1207 else if (abs((selmon->wy + selmon->wh) - (ny + HEIGHT(c))) < snap) 1208 ny = selmon->wy + selmon->wh - HEIGHT(c); 1209 if (!c->isfloating && selmon->lt[selmon->sellt]->arrange 1210 && (abs(nx - c->x) > snap || abs(ny - c->y) > snap)) 1211 togglefloating(NULL); 1212 if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) 1213 resize(c, nx, ny, c->w, c->h, 1); 1214 break; 1215 } 1216 } while (ev.type != ButtonRelease); 1217 XUngrabPointer(dpy, CurrentTime); 1218 if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { 1219 sendmon(c, m); 1220 selmon = m; 1221 focus(NULL); 1222 } 1223 } 1224 1225 Client * 1226 nexttiled(Client *c) 1227 { 1228 for (; c && (c->isfloating || !ISVISIBLE(c)); c = c->next); 1229 return c; 1230 } 1231 1232 void 1233 pop(Client *c) 1234 { 1235 detach(c); 1236 attach(c); 1237 focus(c); 1238 arrange(c->mon); 1239 } 1240 1241 void 1242 propertynotify(XEvent *e) 1243 { 1244 Client *c; 1245 Window trans; 1246 XPropertyEvent *ev = &e->xproperty; 1247 1248 if ((ev->window == root) && (ev->atom == XA_WM_NAME)) 1249 updatestatus(); 1250 else if (ev->state == PropertyDelete) 1251 return; /* ignore */ 1252 else if ((c = wintoclient(ev->window))) { 1253 switch(ev->atom) { 1254 default: break; 1255 case XA_WM_TRANSIENT_FOR: 1256 if (!c->isfloating && (XGetTransientForHint(dpy, c->win, &trans)) && 1257 (c->isfloating = (wintoclient(trans)) != NULL)) 1258 arrange(c->mon); 1259 break; 1260 case XA_WM_NORMAL_HINTS: 1261 c->hintsvalid = 0; 1262 break; 1263 case XA_WM_HINTS: 1264 updatewmhints(c); 1265 drawbars(); 1266 break; 1267 } 1268 if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { 1269 updatetitle(c); 1270 if (c == c->mon->sel) 1271 drawbar(c->mon); 1272 } 1273 if (ev->atom == netatom[NetWMWindowType]) 1274 updatewindowtype(c); 1275 } 1276 } 1277 1278 void 1279 quit(const Arg *arg) 1280 { 1281 running = 0; 1282 } 1283 1284 Monitor * 1285 recttomon(int x, int y, int w, int h) 1286 { 1287 Monitor *m, *r = selmon; 1288 int a, area = 0; 1289 1290 for (m = mons; m; m = m->next) 1291 if ((a = INTERSECT(x, y, w, h, m)) > area) { 1292 area = a; 1293 r = m; 1294 } 1295 return r; 1296 } 1297 1298 void 1299 resize(Client *c, int x, int y, int w, int h, int interact) 1300 { 1301 if (applysizehints(c, &x, &y, &w, &h, interact)) 1302 resizeclient(c, x, y, w, h); 1303 } 1304 1305 void 1306 resizeclient(Client *c, int x, int y, int w, int h) 1307 { 1308 XWindowChanges wc; 1309 1310 c->oldx = c->x; c->x = wc.x = x; 1311 c->oldy = c->y; c->y = wc.y = y; 1312 c->oldw = c->w; c->w = wc.width = w; 1313 c->oldh = c->h; c->h = wc.height = h; 1314 wc.border_width = c->bw; 1315 XConfigureWindow(dpy, c->win, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc); 1316 configure(c); 1317 XSync(dpy, False); 1318 } 1319 1320 void 1321 resizemouse(const Arg *arg) 1322 { 1323 int ocx, ocy, nw, nh; 1324 Client *c; 1325 Monitor *m; 1326 XEvent ev; 1327 Time lasttime = 0; 1328 1329 if (!(c = selmon->sel)) 1330 return; 1331 if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */ 1332 return; 1333 restack(selmon); 1334 ocx = c->x; 1335 ocy = c->y; 1336 if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, 1337 None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess) 1338 return; 1339 XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); 1340 do { 1341 XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); 1342 switch(ev.type) { 1343 case ConfigureRequest: 1344 case Expose: 1345 case MapRequest: 1346 handler[ev.type](&ev); 1347 break; 1348 case MotionNotify: 1349 if ((ev.xmotion.time - lasttime) <= (1000 / refreshrate)) 1350 continue; 1351 lasttime = ev.xmotion.time; 1352 1353 nw = MAX(ev.xmotion.x - ocx - 2 * c->bw + 1, 1); 1354 nh = MAX(ev.xmotion.y - ocy - 2 * c->bw + 1, 1); 1355 if (c->mon->wx + nw >= selmon->wx && c->mon->wx + nw <= selmon->wx + selmon->ww 1356 && c->mon->wy + nh >= selmon->wy && c->mon->wy + nh <= selmon->wy + selmon->wh) 1357 { 1358 if (!c->isfloating && selmon->lt[selmon->sellt]->arrange 1359 && (abs(nw - c->w) > snap || abs(nh - c->h) > snap)) 1360 togglefloating(NULL); 1361 } 1362 if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) 1363 resize(c, c->x, c->y, nw, nh, 1); 1364 break; 1365 } 1366 } while (ev.type != ButtonRelease); 1367 XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); 1368 XUngrabPointer(dpy, CurrentTime); 1369 while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); 1370 if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { 1371 sendmon(c, m); 1372 selmon = m; 1373 focus(NULL); 1374 } 1375 } 1376 1377 void 1378 restack(Monitor *m) 1379 { 1380 Client *c; 1381 XEvent ev; 1382 XWindowChanges wc; 1383 1384 drawbar(m); 1385 if (!m->sel) 1386 return; 1387 if (m->sel->isfloating || !m->lt[m->sellt]->arrange) 1388 XRaiseWindow(dpy, m->sel->win); 1389 if (m->lt[m->sellt]->arrange) { 1390 wc.stack_mode = Below; 1391 wc.sibling = m->barwin; 1392 for (c = m->stack; c; c = c->snext) 1393 if (!c->isfloating && ISVISIBLE(c)) { 1394 XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc); 1395 wc.sibling = c->win; 1396 } 1397 } 1398 XSync(dpy, False); 1399 while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); 1400 } 1401 1402 void 1403 run(void) 1404 { 1405 XEvent ev; 1406 /* main event loop */ 1407 XSync(dpy, False); 1408 while (running && !XNextEvent(dpy, &ev)) 1409 if (handler[ev.type]) 1410 handler[ev.type](&ev); /* call handler */ 1411 } 1412 1413 void 1414 scan(void) 1415 { 1416 unsigned int i, num; 1417 Window d1, d2, *wins = NULL; 1418 XWindowAttributes wa; 1419 1420 if (XQueryTree(dpy, root, &d1, &d2, &wins, &num)) { 1421 for (i = 0; i < num; i++) { 1422 if (!XGetWindowAttributes(dpy, wins[i], &wa) 1423 || wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1)) 1424 continue; 1425 if (wa.map_state == IsViewable || getstate(wins[i]) == IconicState) 1426 manage(wins[i], &wa); 1427 } 1428 for (i = 0; i < num; i++) { /* now the transients */ 1429 if (!XGetWindowAttributes(dpy, wins[i], &wa)) 1430 continue; 1431 if (XGetTransientForHint(dpy, wins[i], &d1) 1432 && (wa.map_state == IsViewable || getstate(wins[i]) == IconicState)) 1433 manage(wins[i], &wa); 1434 } 1435 if (wins) 1436 XFree(wins); 1437 } 1438 } 1439 1440 void 1441 sendmon(Client *c, Monitor *m) 1442 { 1443 if (c->mon == m) 1444 return; 1445 unfocus(c, 1); 1446 detach(c); 1447 detachstack(c); 1448 c->mon = m; 1449 c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ 1450 attach(c); 1451 attachstack(c); 1452 focus(NULL); 1453 arrange(NULL); 1454 } 1455 1456 void 1457 setclientstate(Client *c, long state) 1458 { 1459 long data[] = { state, None }; 1460 1461 XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32, 1462 PropModeReplace, (unsigned char *)data, 2); 1463 } 1464 1465 int 1466 sendevent(Client *c, Atom proto) 1467 { 1468 int n; 1469 Atom *protocols; 1470 int exists = 0; 1471 XEvent ev; 1472 1473 if (XGetWMProtocols(dpy, c->win, &protocols, &n)) { 1474 while (!exists && n--) 1475 exists = protocols[n] == proto; 1476 XFree(protocols); 1477 } 1478 if (exists) { 1479 ev.type = ClientMessage; 1480 ev.xclient.window = c->win; 1481 ev.xclient.message_type = wmatom[WMProtocols]; 1482 ev.xclient.format = 32; 1483 ev.xclient.data.l[0] = proto; 1484 ev.xclient.data.l[1] = CurrentTime; 1485 XSendEvent(dpy, c->win, False, NoEventMask, &ev); 1486 } 1487 return exists; 1488 } 1489 1490 void 1491 setfocus(Client *c) 1492 { 1493 if (!c->neverfocus) { 1494 XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); 1495 XChangeProperty(dpy, root, netatom[NetActiveWindow], 1496 XA_WINDOW, 32, PropModeReplace, 1497 (unsigned char *) &(c->win), 1); 1498 } 1499 sendevent(c, wmatom[WMTakeFocus]); 1500 } 1501 1502 void 1503 setfullscreen(Client *c, int fullscreen) 1504 { 1505 if (fullscreen && !c->isfullscreen) { 1506 XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, 1507 PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1); 1508 c->isfullscreen = 1; 1509 1510 if (fakefullscreen) 1511 return; 1512 1513 c->oldstate = c->isfloating; 1514 c->oldbw = c->bw; 1515 c->bw = 0; 1516 c->isfloating = 1; 1517 resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); 1518 XRaiseWindow(dpy, c->win); 1519 } else if (!fullscreen && c->isfullscreen){ 1520 XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, 1521 PropModeReplace, (unsigned char*)0, 0); 1522 c->isfullscreen = 0; 1523 c->isfloating = c->oldstate; 1524 c->bw = c->oldbw; 1525 c->x = c->oldx; 1526 c->y = c->oldy; 1527 c->w = c->oldw; 1528 c->h = c->oldh; 1529 resizeclient(c, c->x, c->y, c->w, c->h); 1530 arrange(c->mon); 1531 } 1532 } 1533 1534 void 1535 setlayout(const Arg *arg) 1536 { 1537 if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) 1538 selmon->sellt ^= 1; 1539 if (arg && arg->v) 1540 selmon->lt[selmon->sellt] = (Layout *)arg->v; 1541 strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof(selmon->ltsymbol) - 1); 1542 if (selmon->sel) 1543 arrange(selmon); 1544 else 1545 drawbar(selmon); 1546 } 1547 1548 void 1549 nextlayout(const Arg *arg) 1550 { 1551 const Layout *l; 1552 Arg a; 1553 1554 for (l = layouts; l != selmon->lt[selmon->sellt]; l++); 1555 1556 if (l->symbol && (l + 1)->symbol) 1557 a.v = (l + 1); 1558 else 1559 a.v = layouts; 1560 1561 setlayout(&a); 1562 } 1563 1564 void 1565 prevlayout(const Arg *arg) 1566 { 1567 const Layout *l; 1568 Arg a; 1569 1570 for (l = layouts; l != selmon->lt[selmon->sellt]; l++); 1571 1572 if (l != layouts && (l - 1)->symbol) 1573 a.v = (l - 1); 1574 else 1575 a.v = &layouts[LENGTH(layouts) - 2]; 1576 1577 setlayout(&a); 1578 } 1579 1580 /* arg > 1.0 will set mfact absolutely */ 1581 void 1582 setmfact(const Arg *arg) 1583 { 1584 float f; 1585 1586 if (!arg || !selmon->lt[selmon->sellt]->arrange) 1587 return; 1588 f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0; 1589 if (f < 0.05 || f > 0.95) 1590 return; 1591 selmon->mfact = f; 1592 arrange(selmon); 1593 } 1594 1595 void 1596 setup(void) 1597 { 1598 int i; 1599 XSetWindowAttributes wa; 1600 Atom utf8string; 1601 struct sigaction sa; 1602 1603 /* do not transform children into zombies when they terminate */ 1604 sigemptyset(&sa.sa_mask); 1605 sa.sa_flags = SA_NOCLDSTOP | SA_NOCLDWAIT | SA_RESTART; 1606 sa.sa_handler = SIG_IGN; 1607 sigaction(SIGCHLD, &sa, NULL); 1608 1609 /* clean up any zombies (inherited from .xinitrc etc) immediately */ 1610 while (waitpid(-1, NULL, WNOHANG) > 0); 1611 1612 /* init screen */ 1613 screen = DefaultScreen(dpy); 1614 sw = DisplayWidth(dpy, screen); 1615 sh = DisplayHeight(dpy, screen); 1616 root = RootWindow(dpy, screen); 1617 drw = drw_create(dpy, screen, root, sw, sh); 1618 if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) 1619 die("no fonts could be loaded."); 1620 lrpad = drw->fonts->h; 1621 bh = drw->fonts->h + padbar; 1622 updategeom(); 1623 /* init atoms */ 1624 utf8string = XInternAtom(dpy, "UTF8_STRING", False); 1625 wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); 1626 wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); 1627 wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False); 1628 wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False); 1629 netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); 1630 netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); 1631 netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); 1632 netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False); 1633 netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False); 1634 netatom[NetWMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); 1635 netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); 1636 netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); 1637 netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False); 1638 /* init cursors */ 1639 cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr); 1640 cursor[CurResize] = drw_cur_create(drw, XC_sizing); 1641 cursor[CurMove] = drw_cur_create(drw, XC_fleur); 1642 /* init appearance */ 1643 scheme = ecalloc(LENGTH(colors), sizeof(Clr *)); 1644 for (i = 0; i < LENGTH(colors); i++) 1645 scheme[i] = drw_scm_create(drw, colors[i], 3); 1646 /* init bars */ 1647 updatebars(); 1648 updatestatus(); 1649 /* supporting window for NetWMCheck */ 1650 wmcheckwin = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 0, 0, 0); 1651 XChangeProperty(dpy, wmcheckwin, netatom[NetWMCheck], XA_WINDOW, 32, 1652 PropModeReplace, (unsigned char *) &wmcheckwin, 1); 1653 XChangeProperty(dpy, wmcheckwin, netatom[NetWMName], utf8string, 8, 1654 PropModeReplace, (unsigned char *) "dwm", 3); 1655 XChangeProperty(dpy, root, netatom[NetWMCheck], XA_WINDOW, 32, 1656 PropModeReplace, (unsigned char *) &wmcheckwin, 1); 1657 /* EWMH support per view */ 1658 XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32, 1659 PropModeReplace, (unsigned char *) netatom, NetLast); 1660 XDeleteProperty(dpy, root, netatom[NetClientList]); 1661 /* select events */ 1662 wa.cursor = cursor[CurNormal]->cursor; 1663 wa.event_mask = SubstructureRedirectMask|SubstructureNotifyMask 1664 |ButtonPressMask|PointerMotionMask|EnterWindowMask 1665 |LeaveWindowMask|StructureNotifyMask|PropertyChangeMask; 1666 XChangeWindowAttributes(dpy, root, CWEventMask|CWCursor, &wa); 1667 XSelectInput(dpy, root, wa.event_mask); 1668 grabkeys(); 1669 focus(NULL); 1670 } 1671 1672 void 1673 seturgent(Client *c, int urg) 1674 { 1675 XWMHints *wmh; 1676 1677 c->isurgent = urg; 1678 if (!(wmh = XGetWMHints(dpy, c->win))) 1679 return; 1680 wmh->flags = urg ? (wmh->flags | XUrgencyHint) : (wmh->flags & ~XUrgencyHint); 1681 XSetWMHints(dpy, c->win, wmh); 1682 XFree(wmh); 1683 } 1684 1685 void 1686 showhide(Client *c) 1687 { 1688 if (!c) 1689 return; 1690 if (ISVISIBLE(c)) { 1691 if ((c->tags & SPTAGMASK) && c->isfloating) { 1692 c->x = c->mon->wx + (c->mon->ww / 2 - WIDTH(c) / 2); 1693 c->y = c->mon->wy + (c->mon->wh / 2 - HEIGHT(c) / 2); 1694 } 1695 /* show clients top down */ 1696 XMoveWindow(dpy, c->win, c->x, c->y); 1697 if ((!c->mon->lt[c->mon->sellt]->arrange || c->isfloating) && fakefullscreen && !c->isfullscreen) 1698 resize(c, c->x, c->y, c->w, c->h, 0); 1699 showhide(c->snext); 1700 } else { 1701 /* hide clients bottom up */ 1702 showhide(c->snext); 1703 XMoveWindow(dpy, c->win, WIDTH(c) * -2, c->y); 1704 } 1705 } 1706 1707 void 1708 spawn(const Arg *arg) 1709 { 1710 struct sigaction sa; 1711 1712 if (arg->v == dmenucmd) 1713 dmenumon[0] = '0' + selmon->num; 1714 if (fork() == 0) { 1715 if (dpy) 1716 close(ConnectionNumber(dpy)); 1717 setsid(); 1718 1719 sigemptyset(&sa.sa_mask); 1720 sa.sa_flags = 0; 1721 sa.sa_handler = SIG_DFL; 1722 sigaction(SIGCHLD, &sa, NULL); 1723 1724 execvp(((char **)arg->v)[0], (char **)arg->v); 1725 die("dwm: execvp '%s' failed:", ((char **)arg->v)[0]); 1726 } 1727 } 1728 1729 void 1730 tag(const Arg *arg) 1731 { 1732 if (selmon->sel && arg->ui & TAGMASK) { 1733 selmon->sel->tags = arg->ui & TAGMASK; 1734 focus(NULL); 1735 arrange(selmon); 1736 } 1737 } 1738 1739 void 1740 tagmon(const Arg *arg) 1741 { 1742 if (!selmon->sel || !mons->next) 1743 return; 1744 sendmon(selmon->sel, dirtomon(arg->i)); 1745 } 1746 1747 void 1748 togglebar(const Arg *arg) 1749 { 1750 selmon->showbar = !selmon->showbar; 1751 updatebarpos(selmon); 1752 XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); 1753 arrange(selmon); 1754 } 1755 1756 void 1757 togglefakefull(const Arg *arg) 1758 { 1759 fakefullscreen = !fakefullscreen; 1760 if (selmon->sel && selmon->sel->isfullscreen) 1761 setfullscreen(selmon->sel, 0); 1762 } 1763 1764 void 1765 togglefloating(const Arg *arg) 1766 { 1767 if (!selmon->sel) 1768 return; 1769 if (selmon->sel->isfullscreen) /* no support for fullscreen windows */ 1770 return; 1771 selmon->sel->isfloating = !selmon->sel->isfloating || selmon->sel->isfixed; 1772 if (selmon->sel->isfloating) 1773 resize(selmon->sel, selmon->sel->x, selmon->sel->y, 1774 selmon->sel->w, selmon->sel->h, 0); 1775 arrange(selmon); 1776 } 1777 1778 void 1779 togglefullscr(const Arg *arg) 1780 { 1781 if (!fakefullscreen && selmon->sel) 1782 setfullscreen(selmon->sel, !selmon->sel->isfullscreen); 1783 } 1784 1785 void 1786 togglescratch(const Arg *arg) 1787 { 1788 Client *c; 1789 unsigned int found = 0; 1790 unsigned int scratchtag = SPTAG(arg->ui); 1791 Arg sparg = {.v = scratchpads[arg->ui].cmd}; 1792 1793 for (c = selmon->clients; c && !(found = c->tags & scratchtag); c = c->next); 1794 if (found) { 1795 unsigned int newtagset = selmon->tagset[selmon->seltags] ^ scratchtag; 1796 if (newtagset) { 1797 selmon->tagset[selmon->seltags] = newtagset; 1798 focus(NULL); 1799 arrange(selmon); 1800 } 1801 if (ISVISIBLE(c)) { 1802 focus(c); 1803 restack(selmon); 1804 } 1805 } else { 1806 selmon->tagset[selmon->seltags] |= scratchtag; 1807 spawn(&sparg); 1808 } 1809 } 1810 1811 void 1812 toggletag(const Arg *arg) 1813 { 1814 unsigned int newtags; 1815 1816 if (!selmon->sel) 1817 return; 1818 newtags = selmon->sel->tags ^ (arg->ui & TAGMASK); 1819 if (newtags) { 1820 selmon->sel->tags = newtags; 1821 focus(NULL); 1822 arrange(selmon); 1823 } 1824 } 1825 1826 void 1827 toggleview(const Arg *arg) 1828 { 1829 unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK); 1830 1831 if (newtagset) { 1832 selmon->tagset[selmon->seltags] = newtagset; 1833 focus(NULL); 1834 arrange(selmon); 1835 } 1836 } 1837 1838 void 1839 unfocus(Client *c, int setfocus) 1840 { 1841 if (!c) 1842 return; 1843 grabbuttons(c, 0); 1844 XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel); 1845 if (setfocus) { 1846 XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); 1847 XDeleteProperty(dpy, root, netatom[NetActiveWindow]); 1848 } 1849 } 1850 1851 void 1852 unmanage(Client *c, int destroyed) 1853 { 1854 Monitor *m = c->mon; 1855 XWindowChanges wc; 1856 1857 detach(c); 1858 detachstack(c); 1859 if (!destroyed) { 1860 wc.border_width = c->oldbw; 1861 XGrabServer(dpy); /* avoid race conditions */ 1862 XSetErrorHandler(xerrordummy); 1863 XSelectInput(dpy, c->win, NoEventMask); 1864 XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */ 1865 XUngrabButton(dpy, AnyButton, AnyModifier, c->win); 1866 setclientstate(c, WithdrawnState); 1867 XSync(dpy, False); 1868 XSetErrorHandler(xerror); 1869 XUngrabServer(dpy); 1870 } 1871 free(c); 1872 focus(NULL); 1873 updateclientlist(); 1874 arrange(m); 1875 } 1876 1877 void 1878 unmapnotify(XEvent *e) 1879 { 1880 Client *c; 1881 XUnmapEvent *ev = &e->xunmap; 1882 1883 if ((c = wintoclient(ev->window))) { 1884 if (ev->send_event) 1885 setclientstate(c, WithdrawnState); 1886 else 1887 unmanage(c, 0); 1888 } 1889 } 1890 1891 void 1892 updatebars(void) 1893 { 1894 Monitor *m; 1895 XSetWindowAttributes wa = { 1896 .override_redirect = True, 1897 .background_pixmap = ParentRelative, 1898 .event_mask = ButtonPressMask|ExposureMask 1899 }; 1900 XClassHint ch = {"dwm", "dwm"}; 1901 for (m = mons; m; m = m->next) { 1902 if (m->barwin) 1903 continue; 1904 m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen), 1905 CopyFromParent, DefaultVisual(dpy, screen), 1906 CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); 1907 XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); 1908 XMapRaised(dpy, m->barwin); 1909 XSetClassHint(dpy, m->barwin, &ch); 1910 } 1911 } 1912 1913 void 1914 updatebarpos(Monitor *m) 1915 { 1916 m->wy = m->my; 1917 m->wh = m->mh; 1918 if (m->showbar) { 1919 m->wh -= bh; 1920 m->by = m->topbar ? m->wy : m->wy + m->wh; 1921 m->wy = m->topbar ? m->wy + bh : m->wy; 1922 } else 1923 m->by = -bh; 1924 } 1925 1926 void 1927 updateclientlist(void) 1928 { 1929 Client *c; 1930 Monitor *m; 1931 1932 XDeleteProperty(dpy, root, netatom[NetClientList]); 1933 for (m = mons; m; m = m->next) 1934 for (c = m->clients; c; c = c->next) 1935 XChangeProperty(dpy, root, netatom[NetClientList], 1936 XA_WINDOW, 32, PropModeAppend, 1937 (unsigned char *) &(c->win), 1); 1938 } 1939 1940 int 1941 updategeom(void) 1942 { 1943 int dirty = 0; 1944 1945 #ifdef XINERAMA 1946 if (XineramaIsActive(dpy)) { 1947 int i, j, n, nn; 1948 Client *c; 1949 Monitor *m; 1950 XineramaScreenInfo *info = XineramaQueryScreens(dpy, &nn); 1951 XineramaScreenInfo *unique = NULL; 1952 1953 for (n = 0, m = mons; m; m = m->next, n++); 1954 /* only consider unique geometries as separate screens */ 1955 unique = ecalloc(nn, sizeof(XineramaScreenInfo)); 1956 for (i = 0, j = 0; i < nn; i++) 1957 if (isuniquegeom(unique, j, &info[i])) 1958 memcpy(&unique[j++], &info[i], sizeof(XineramaScreenInfo)); 1959 XFree(info); 1960 nn = j; 1961 1962 /* new monitors if nn > n */ 1963 for (i = n; i < nn; i++) { 1964 for (m = mons; m && m->next; m = m->next); 1965 if (m) 1966 m->next = createmon(); 1967 else 1968 mons = createmon(); 1969 } 1970 for (i = 0, m = mons; i < nn && m; m = m->next, i++) 1971 if (i >= n 1972 || unique[i].x_org != m->mx || unique[i].y_org != m->my 1973 || unique[i].width != m->mw || unique[i].height != m->mh) 1974 { 1975 dirty = 1; 1976 m->num = i; 1977 m->mx = m->wx = unique[i].x_org; 1978 m->my = m->wy = unique[i].y_org; 1979 m->mw = m->ww = unique[i].width; 1980 m->mh = m->wh = unique[i].height; 1981 updatebarpos(m); 1982 } 1983 /* removed monitors if n > nn */ 1984 for (i = nn; i < n; i++) { 1985 for (m = mons; m && m->next; m = m->next); 1986 while ((c = m->clients)) { 1987 dirty = 1; 1988 m->clients = c->next; 1989 detachstack(c); 1990 c->mon = mons; 1991 attach(c); 1992 attachstack(c); 1993 } 1994 if (m == selmon) 1995 selmon = mons; 1996 cleanupmon(m); 1997 } 1998 free(unique); 1999 } else 2000 #endif /* XINERAMA */ 2001 { /* default monitor setup */ 2002 if (!mons) 2003 mons = createmon(); 2004 if (mons->mw != sw || mons->mh != sh) { 2005 dirty = 1; 2006 mons->mw = mons->ww = sw; 2007 mons->mh = mons->wh = sh; 2008 updatebarpos(mons); 2009 } 2010 } 2011 if (dirty) { 2012 selmon = mons; 2013 selmon = wintomon(root); 2014 } 2015 return dirty; 2016 } 2017 2018 void 2019 updatenumlockmask(void) 2020 { 2021 unsigned int i, j; 2022 XModifierKeymap *modmap; 2023 2024 numlockmask = 0; 2025 modmap = XGetModifierMapping(dpy); 2026 for (i = 0; i < 8; i++) 2027 for (j = 0; j < modmap->max_keypermod; j++) 2028 if (modmap->modifiermap[i * modmap->max_keypermod + j] 2029 == XKeysymToKeycode(dpy, XK_Num_Lock)) 2030 numlockmask = (1 << i); 2031 XFreeModifiermap(modmap); 2032 } 2033 2034 void 2035 updatesizehints(Client *c) 2036 { 2037 long msize; 2038 XSizeHints size; 2039 2040 if (!XGetWMNormalHints(dpy, c->win, &size, &msize)) 2041 /* size is uninitialized, ensure that size.flags aren't used */ 2042 size.flags = PSize; 2043 if (size.flags & PBaseSize) { 2044 c->basew = size.base_width; 2045 c->baseh = size.base_height; 2046 } else if (size.flags & PMinSize) { 2047 c->basew = size.min_width; 2048 c->baseh = size.min_height; 2049 } else 2050 c->basew = c->baseh = 0; 2051 if (size.flags & PResizeInc) { 2052 c->incw = size.width_inc; 2053 c->inch = size.height_inc; 2054 } else 2055 c->incw = c->inch = 0; 2056 if (size.flags & PMaxSize) { 2057 c->maxw = size.max_width; 2058 c->maxh = size.max_height; 2059 } else 2060 c->maxw = c->maxh = 0; 2061 if (size.flags & PMinSize) { 2062 c->minw = size.min_width; 2063 c->minh = size.min_height; 2064 } else if (size.flags & PBaseSize) { 2065 c->minw = size.base_width; 2066 c->minh = size.base_height; 2067 } else 2068 c->minw = c->minh = 0; 2069 if (size.flags & PAspect) { 2070 c->mina = (float)size.min_aspect.y / size.min_aspect.x; 2071 c->maxa = (float)size.max_aspect.x / size.max_aspect.y; 2072 } else 2073 c->maxa = c->mina = 0.0; 2074 c->isfixed = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh); 2075 c->hintsvalid = 1; 2076 } 2077 2078 void 2079 updatestatus(void) 2080 { 2081 if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) 2082 strcpy(stext, "dwm-"VERSION); 2083 drawbar(selmon); 2084 } 2085 2086 void 2087 updatetitle(Client *c) 2088 { 2089 if (!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name)) 2090 gettextprop(c->win, XA_WM_NAME, c->name, sizeof c->name); 2091 if (c->name[0] == '\0') /* hack to mark broken clients */ 2092 strcpy(c->name, broken); 2093 } 2094 2095 void 2096 updatewindowtype(Client *c) 2097 { 2098 Atom state = getatomprop(c, netatom[NetWMState]); 2099 Atom wtype = getatomprop(c, netatom[NetWMWindowType]); 2100 2101 if (state == netatom[NetWMFullscreen]) 2102 setfullscreen(c, 1); 2103 if (wtype == netatom[NetWMWindowTypeDialog]) 2104 c->isfloating = 1; 2105 } 2106 2107 void 2108 updatewmhints(Client *c) 2109 { 2110 XWMHints *wmh; 2111 2112 if ((wmh = XGetWMHints(dpy, c->win))) { 2113 if (c == selmon->sel && wmh->flags & XUrgencyHint) { 2114 wmh->flags &= ~XUrgencyHint; 2115 XSetWMHints(dpy, c->win, wmh); 2116 } else 2117 c->isurgent = (wmh->flags & XUrgencyHint) ? 1 : 0; 2118 if (wmh->flags & InputHint) 2119 c->neverfocus = !wmh->input; 2120 else 2121 c->neverfocus = 0; 2122 XFree(wmh); 2123 } 2124 } 2125 2126 void 2127 view(const Arg *arg) 2128 { 2129 if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) 2130 return; 2131 selmon->seltags ^= 1; /* toggle sel tagset */ 2132 if (arg->ui & TAGMASK) 2133 selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; 2134 focus(NULL); 2135 arrange(selmon); 2136 } 2137 2138 Client * 2139 wintoclient(Window w) 2140 { 2141 Client *c; 2142 Monitor *m; 2143 2144 for (m = mons; m; m = m->next) 2145 for (c = m->clients; c; c = c->next) 2146 if (c->win == w) 2147 return c; 2148 return NULL; 2149 } 2150 2151 Monitor * 2152 wintomon(Window w) 2153 { 2154 int x, y; 2155 Client *c; 2156 Monitor *m; 2157 2158 if (w == root && getrootptr(&x, &y)) 2159 return recttomon(x, y, 1, 1); 2160 for (m = mons; m; m = m->next) 2161 if (w == m->barwin) 2162 return m; 2163 if ((c = wintoclient(w))) 2164 return c->mon; 2165 return selmon; 2166 } 2167 2168 /* There's no way to check accesses to destroyed windows, thus those cases are 2169 * ignored (especially on UnmapNotify's). Other types of errors call Xlibs 2170 * default error handler, which may call exit. */ 2171 int 2172 xerror(Display *dpy, XErrorEvent *ee) 2173 { 2174 if (ee->error_code == BadWindow 2175 || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch) 2176 || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable) 2177 || (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable) 2178 || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable) 2179 || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch) 2180 || (ee->request_code == X_GrabButton && ee->error_code == BadAccess) 2181 || (ee->request_code == X_GrabKey && ee->error_code == BadAccess) 2182 || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable)) 2183 return 0; 2184 fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n", 2185 ee->request_code, ee->error_code); 2186 return xerrorxlib(dpy, ee); /* may call exit */ 2187 } 2188 2189 int 2190 xerrordummy(Display *dpy, XErrorEvent *ee) 2191 { 2192 return 0; 2193 } 2194 2195 /* Startup Error handler to check if another window manager 2196 * is already running. */ 2197 int 2198 xerrorstart(Display *dpy, XErrorEvent *ee) 2199 { 2200 die("dwm: another window manager is already running"); 2201 return -1; 2202 } 2203 2204 void 2205 zoom(const Arg *arg) 2206 { 2207 Client *c = selmon->sel; 2208 2209 if (!selmon->lt[selmon->sellt]->arrange || !c || c->isfloating) 2210 return; 2211 if (c == nexttiled(selmon->clients) && !(c = nexttiled(c->next))) 2212 return; 2213 pop(c); 2214 } 2215 2216 int 2217 main(int argc, char *argv[]) 2218 { 2219 if (argc == 2 && !strcmp("-v", argv[1])) 2220 die("dwm-"VERSION); 2221 else if (argc != 1) 2222 die("usage: dwm [-v]"); 2223 if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) 2224 fputs("warning: no locale support\n", stderr); 2225 if (!(dpy = XOpenDisplay(NULL))) 2226 die("dwm: cannot open display"); 2227 checkotherwm(); 2228 setup(); 2229 #ifdef __OpenBSD__ 2230 if (pledge("stdio rpath proc exec", NULL) == -1) 2231 die("pledge"); 2232 #endif /* __OpenBSD__ */ 2233 scan(); 2234 run(); 2235 cleanup(); 2236 XCloseDisplay(dpy); 2237 return EXIT_SUCCESS; 2238 }