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