Xinqi Bao's Git

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