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