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