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