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