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