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