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