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