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