JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
changes monitor structure to be a list
[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 global
15  * linked client list, the focus history is remembered through a global
16  * stack list. 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 <stdarg.h>
26 #include <signal.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
43 /* macros */
44 #define BUTTONMASK              (ButtonPressMask|ButtonReleaseMask)
45 #define CLEANMASK(mask)         (mask & ~(numlockmask|LockMask))
46 #define INRECT(X,Y,RX,RY,RW,RH) ((X) >= (RX) && (X) < (RX) + (RW) && (Y) >= (RY) && (Y) < (RY) + (RH))
47 #define ISVISIBLE(M, C)         ((M) == (C->m) && (C->tags & M->tagset[M->seltags]))
48 #define LENGTH(X)               (sizeof X / sizeof X[0])
49 #define MAX(A, B)               ((A) > (B) ? (A) : (B))
50 #define MIN(A, B)               ((A) < (B) ? (A) : (B))
51 #define MOUSEMASK               (BUTTONMASK|PointerMotionMask)
52 #define WIDTH(X)                ((X)->w + 2 * (X)->bw)
53 #define HEIGHT(X)               ((X)->h + 2 * (X)->bw)
54 #define TAGMASK                 ((int)((1LL << LENGTH(tags)) - 1))
55 #define TEXTW(X)                (textnw(X, strlen(X)) + dc.font.height)
56
57 /* enums */
58 enum { CurNormal, CurResize, CurMove, CurLast };        /* cursor */
59 enum { ColBorder, ColFG, ColBG, ColLast };              /* color */
60 enum { NetSupported, NetWMName, NetLast };              /* EWMH atoms */
61 enum { WMProtocols, WMDelete, WMState, WMLast };        /* default atoms */
62 enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle,
63        ClkClientWin, ClkRootWin, ClkLast };             /* clicks */
64
65 typedef union {
66         int i;
67         unsigned int ui;
68         float f;
69         void *v;
70 } Arg;
71
72 typedef struct {
73         unsigned int click;
74         unsigned int mask;
75         unsigned int button;
76         void (*func)(const Arg *arg);
77         const Arg arg;
78 } Button;
79
80 typedef struct Monitor Monitor;
81 typedef struct Client Client;
82 struct Client {
83         char name[256];
84         float mina, maxa;
85         int x, y, w, h;
86         int basew, baseh, incw, inch, maxw, maxh, minw, minh;
87         int bw, oldbw;
88         unsigned int tags;
89         Bool isfixed, isfloating, isurgent;
90         Client *next;
91         Client *snext;
92         Monitor *m;
93         Window win;
94 };
95
96 typedef struct {
97         int x, y, w, h;
98         unsigned long norm[ColLast];
99         unsigned long sel[ColLast];
100         Drawable drawable;
101         GC gc;
102         struct {
103                 int ascent;
104                 int descent;
105                 int height;
106                 XFontSet set;
107                 XFontStruct *xfont;
108         } font;
109 } DC; /* draw context */
110
111 typedef struct {
112         unsigned int mod;
113         KeySym keysym;
114         void (*func)(const Arg *);
115         const Arg arg;
116 } Key;
117
118 typedef struct {
119         const char *symbol;
120         void (*arrange)(Monitor *);
121 } Layout;
122
123 struct Monitor {
124         int screen_number;
125         float mfact;
126         int by, btx;          /* bar geometry */
127         int wx, wy, ww, wh;   /* window area  */
128         unsigned int seltags;
129         unsigned int sellt;
130         unsigned int tagset[2];
131         Bool showbar;
132         Bool topbar;
133         Window barwin;
134         Monitor *next;
135 };
136
137 typedef struct {
138         const char *class;
139         const char *instance;
140         const char *title;
141         unsigned int tags;
142         Bool isfloating;
143 } Rule;
144
145 /* function declarations */
146 static void applyrules(Client *c);
147 static Bool applysizehints(Client *c, int *x, int *y, int *w, int *h);
148 static void arrange(void);
149 static void attach(Client *c);
150 static void attachstack(Client *c);
151 static void buttonpress(XEvent *e);
152 static void checkotherwm(void);
153 static void cleanup(void);
154 static void cleanupmons(void);
155 static void clearurgent(Client *c);
156 static void configure(Client *c);
157 static void configurenotify(XEvent *e);
158 static void configurerequest(XEvent *e);
159 static void destroynotify(XEvent *e);
160 static void detach(Client *c);
161 static void detachstack(Client *c);
162 static void die(const char *errstr, ...);
163 static void drawbar(Monitor *m);
164 static void drawbars();
165 static void drawsquare(Bool filled, Bool empty, Bool invert, unsigned long col[ColLast]);
166 static void drawtext(const char *text, unsigned long col[ColLast], Bool invert);
167 static void enternotify(XEvent *e);
168 static void expose(XEvent *e);
169 static void focus(Client *c);
170 static void focusin(XEvent *e);
171 static void focusstack(const Arg *arg);
172 static Client *getclient(Window w);
173 static unsigned long getcolor(const char *colstr);
174 static long getstate(Window w);
175 static Bool gettextprop(Window w, Atom atom, char *text, unsigned int size);
176 static void grabbuttons(Client *c, Bool focused);
177 static void grabkeys(void);
178 static void initfont(const char *fontstr);
179 static Bool isprotodel(Client *c);
180 static void keypress(XEvent *e);
181 static void killclient(const Arg *arg);
182 static void manage(Window w, XWindowAttributes *wa);
183 static void mappingnotify(XEvent *e);
184 static void maprequest(XEvent *e);
185 static void monocle(Monitor *m);
186 static void movemouse(const Arg *arg);
187 static Client *nexttiled(Monitor *m, Client *c);
188 static void propertynotify(XEvent *e);
189 static void quit(const Arg *arg);
190 static void resize(Client *c, int x, int y, int w, int h);
191 static void resizemouse(const Arg *arg);
192 static void restack(Monitor *m);
193 static void run(void);
194 static void scan(void);
195 static void setclientstate(Client *c, long state);
196 static void setlayout(const Arg *arg);
197 static void setmfact(const Arg *arg);
198 static void setup(void);
199 static void showhide(Client *c);
200 static void sigchld(int signal);
201 static void spawn(const Arg *arg);
202 static void tag(const Arg *arg);
203 static int textnw(const char *text, unsigned int len);
204 static void tile(Monitor *);
205 static void togglebar(const Arg *arg);
206 static void togglefloating(const Arg *arg);
207 static void toggletag(const Arg *arg);
208 static void toggleview(const Arg *arg);
209 static void unmanage(Client *c);
210 static void unmapnotify(XEvent *e);
211 static void updategeom(void);
212 static void updatebars(void);
213 static void updatenumlockmask(void);
214 static void updatesizehints(Client *c);
215 static void updatestatus(void);
216 static void updatetitle(Client *c);
217 static void updatewmhints(Client *c);
218 static void view(const Arg *arg);
219 static int xerror(Display *dpy, XErrorEvent *ee);
220 static int xerrordummy(Display *dpy, XErrorEvent *ee);
221 static int xerrorstart(Display *dpy, XErrorEvent *ee);
222 static void zoom(const Arg *arg);
223 #ifdef XINERAMA
224 static void focusmon(const Arg *arg);
225 static void tagmon(const Arg *arg);
226 #endif /* XINERAMA */
227
228 /* variables */
229 static char stext[256];
230 static int screen;
231 static int sx, sy, sw, sh;   /* X display screen geometry x, y, width, height */
232 static int bh, blw = 0;      /* bar geometry */
233 static int (*xerrorxlib)(Display *, XErrorEvent *);
234 static unsigned int numlockmask = 0;
235 static void (*handler[LASTEvent]) (XEvent *) = {
236         [ButtonPress] = buttonpress,
237         [ConfigureRequest] = configurerequest,
238         [ConfigureNotify] = configurenotify,
239         [DestroyNotify] = destroynotify,
240         [EnterNotify] = enternotify,
241         [Expose] = expose,
242         [FocusIn] = focusin,
243         [KeyPress] = keypress,
244         [MappingNotify] = mappingnotify,
245         [MapRequest] = maprequest,
246         [PropertyNotify] = propertynotify,
247         [UnmapNotify] = unmapnotify
248 };
249 static Atom wmatom[WMLast], netatom[NetLast];
250 static Bool otherwm;
251 static Bool running = True;
252 static Client *clients = NULL;
253 static Client *sel = NULL;
254 static Client *stack = NULL;
255 static Cursor cursor[CurLast];
256 static Display *dpy;
257 static DC dc;
258 static Layout *lt[] = { NULL, NULL };
259 static Monitor *mons = NULL, *selmon = NULL;
260 static Window root;
261 /* configuration, allows nested code to access above variables */
262 #include "config.h"
263
264 /* compile-time check if all tags fit into an unsigned int bit array. */
265 struct NumTags { char limitexceeded[sizeof(unsigned int) * 8 < LENGTH(tags) ? -1 : 1]; };
266
267 /* function implementations */
268 void
269 applyrules(Client *c) {
270         unsigned int i;
271         Rule *r;
272         XClassHint ch = { 0 };
273
274         /* rule matching */
275         c->isfloating = c->tags = 0;
276         if(XGetClassHint(dpy, c->win, &ch)) {
277                 for(i = 0; i < LENGTH(rules); i++) {
278                         r = &rules[i];
279                         if((!r->title || strstr(c->name, r->title))
280                         && (!r->class || (ch.res_class && strstr(ch.res_class, r->class)))
281                         && (!r->instance || (ch.res_name && strstr(ch.res_name, r->instance)))) {
282                                 c->isfloating = r->isfloating;
283                                 c->tags |= r->tags;
284                         }
285                 }
286                 if(ch.res_class)
287                         XFree(ch.res_class);
288                 if(ch.res_name)
289                         XFree(ch.res_name);
290         }
291         c->tags = c->tags & TAGMASK ? c->tags & TAGMASK : c->m->tagset[c->m->seltags];
292 }
293
294 Bool
295 applysizehints(Client *c, int *x, int *y, int *w, int *h) {
296         Bool baseismin;
297
298         /* set minimum possible */
299         *w = MAX(1, *w);
300         *h = MAX(1, *h);
301
302         if(*x > sx + sw)
303                 *x = sw - WIDTH(c);
304         if(*y > sy + sh)
305                 *y = sh - HEIGHT(c);
306         if(*x + *w + 2 * c->bw < sx)
307                 *x = sx;
308         if(*y + *h + 2 * c->bw < sy)
309                 *y = sy;
310         if(*h < bh)
311                 *h = bh;
312         if(*w < bh)
313                 *w = bh;
314
315         if(resizehints || c->isfloating) {
316                 /* see last two sentences in ICCCM 4.1.2.3 */
317                 baseismin = c->basew == c->minw && c->baseh == c->minh;
318
319                 if(!baseismin) { /* temporarily remove base dimensions */
320                         *w -= c->basew;
321                         *h -= c->baseh;
322                 }
323
324                 /* adjust for aspect limits */
325                 if(c->mina > 0 && c->maxa > 0) {
326                         if(c->maxa < (float)*w / *h)
327                                 *w = *h * c->maxa;
328                         else if(c->mina < (float)*h / *w)
329                                 *h = *w * c->mina;
330                 }
331
332                 if(baseismin) { /* increment calculation requires this */
333                         *w -= c->basew;
334                         *h -= c->baseh;
335                 }
336
337                 /* adjust for increment value */
338                 if(c->incw)
339                         *w -= *w % c->incw;
340                 if(c->inch)
341                         *h -= *h % c->inch;
342
343                 /* restore base dimensions */
344                 *w += c->basew;
345                 *h += c->baseh;
346
347                 *w = MAX(*w, c->minw);
348                 *h = MAX(*h, c->minh);
349
350                 if(c->maxw)
351                         *w = MIN(*w, c->maxw);
352
353                 if(c->maxh)
354                         *h = MIN(*h, c->maxh);
355         }
356         return *x != c->x || *y != c->y || *w != c->w || *h != c->h;
357 }
358
359 void
360 arrange(void) {
361         Monitor *m;
362
363         showhide(stack);
364         focus(NULL);
365         for(m = mons; m; m = m->next) {
366                 if(lt[m->sellt]->arrange)
367                         lt[m->sellt]->arrange(m);
368                 restack(m);
369         }
370 }
371
372 void
373 attach(Client *c) {
374         c->next = clients;
375         clients = c;
376 }
377
378 void
379 attachstack(Client *c) {
380         c->snext = stack;
381         stack = c;
382 }
383
384 void
385 buttonpress(XEvent *e) {
386         unsigned int i, x, click;
387         Arg arg = {0};
388         Client *c;
389         XButtonPressedEvent *ev = &e->xbutton;
390
391         click = ClkRootWin;
392         if(ev->window == selmon->barwin) {
393                 i = 0;
394                 x = selmon->btx;
395                 do
396                         x += TEXTW(tags[i]);
397                 while(ev->x >= x && ++i < LENGTH(tags));
398                 if(i < LENGTH(tags)) {
399                         click = ClkTagBar;
400                         arg.ui = 1 << i;
401                 }
402                 else if(ev->x < x + blw)
403                         click = ClkLtSymbol;
404                 else if(ev->x > selmon->wx + selmon->ww - TEXTW(stext))
405                         click = ClkStatusText;
406                 else
407                         click = ClkWinTitle;
408         }
409         else if((c = getclient(ev->window))) {
410                 focus(c);
411                 click = ClkClientWin;
412         }
413
414         for(i = 0; i < LENGTH(buttons); i++)
415                 if(click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button
416                    && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state))
417                         buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg);
418 }
419
420 void
421 checkotherwm(void) {
422         otherwm = False;
423         xerrorxlib = XSetErrorHandler(xerrorstart);
424
425         /* this causes an error if some other window manager is running */
426         XSelectInput(dpy, DefaultRootWindow(dpy), SubstructureRedirectMask);
427         XSync(dpy, False);
428         if(otherwm)
429                 die("dwm: another window manager is already running\n");
430         XSetErrorHandler(xerror);
431         XSync(dpy, False);
432 }
433
434 void
435 cleanup(void) {
436         Arg a = {.ui = ~0};
437         Layout foo = { "", NULL };
438
439         view(&a);
440         lt[selmon->sellt] = &foo;
441         while(stack)
442                 unmanage(stack);
443         if(dc.font.set)
444                 XFreeFontSet(dpy, dc.font.set);
445         else
446                 XFreeFont(dpy, dc.font.xfont);
447         XUngrabKey(dpy, AnyKey, AnyModifier, root);
448         XFreePixmap(dpy, dc.drawable);
449         XFreeGC(dpy, dc.gc);
450         XFreeCursor(dpy, cursor[CurNormal]);
451         XFreeCursor(dpy, cursor[CurResize]);
452         XFreeCursor(dpy, cursor[CurMove]);
453         cleanupmons();
454         XSync(dpy, False);
455         XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
456 }
457
458 void
459 cleanupmons(void) {
460         Monitor *m;
461
462         while(mons) {
463                 m = mons->next;
464                 XUnmapWindow(dpy, mons->barwin);
465                 XDestroyWindow(dpy, mons->barwin);
466                 free(mons);
467                 mons = m;
468         }
469 }
470
471 void
472 clearurgent(Client *c) {
473         XWMHints *wmh;
474
475         c->isurgent = False;
476         if(!(wmh = XGetWMHints(dpy, c->win)))
477                 return;
478         wmh->flags &= ~XUrgencyHint;
479         XSetWMHints(dpy, c->win, wmh);
480         XFree(wmh);
481 }
482
483 void
484 configure(Client *c) {
485         XConfigureEvent ce;
486
487         ce.type = ConfigureNotify;
488         ce.display = dpy;
489         ce.event = c->win;
490         ce.window = c->win;
491         ce.x = c->x;
492         ce.y = c->y;
493         ce.width = c->w;
494         ce.height = c->h;
495         ce.border_width = c->bw;
496         ce.above = None;
497         ce.override_redirect = False;
498         XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce);
499 }
500
501 void
502 configurenotify(XEvent *e) {
503         Monitor *m;
504         XConfigureEvent *ev = &e->xconfigure;
505
506         if(ev->window == root && (ev->width != sw || ev->height != sh)) {
507                 sw = ev->width;
508                 sh = ev->height;
509                 updategeom();
510                 if(dc.drawable != 0)
511                         XFreePixmap(dpy, dc.drawable);
512                 dc.drawable = XCreatePixmap(dpy, root, sw, bh, DefaultDepth(dpy, screen));
513                 updatebars();
514                 for(m = mons; m; m = m->next)
515                         XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh);
516                 arrange();
517         }
518 }
519
520 void
521 configurerequest(XEvent *e) {
522         Client *c;
523         XConfigureRequestEvent *ev = &e->xconfigurerequest;
524         XWindowChanges wc;
525
526         if((c = getclient(ev->window))) {
527                 if(ev->value_mask & CWBorderWidth)
528                         c->bw = ev->border_width;
529                 else if(c->isfloating || !lt[selmon->sellt]->arrange) {
530                         if(ev->value_mask & CWX)
531                                 c->x = sx + ev->x;
532                         if(ev->value_mask & CWY)
533                                 c->y = sy + ev->y;
534                         if(ev->value_mask & CWWidth)
535                                 c->w = ev->width;
536                         if(ev->value_mask & CWHeight)
537                                 c->h = ev->height;
538                         if((c->x - sx + c->w) > sw && c->isfloating)
539                                 c->x = sx + (sw / 2 - c->w / 2); /* center in x direction */
540                         if((c->y - sy + c->h) > sh && c->isfloating)
541                                 c->y = sy + (sh / 2 - c->h / 2); /* center in y direction */
542                         if((ev->value_mask & (CWX|CWY)) && !(ev->value_mask & (CWWidth|CWHeight)))
543                                 configure(c);
544                         if(ISVISIBLE((c->m), c))
545                                 XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h);
546                 }
547                 else
548                         configure(c);
549         }
550         else {
551                 wc.x = ev->x;
552                 wc.y = ev->y;
553                 wc.width = ev->width;
554                 wc.height = ev->height;
555                 wc.border_width = ev->border_width;
556                 wc.sibling = ev->above;
557                 wc.stack_mode = ev->detail;
558                 XConfigureWindow(dpy, ev->window, ev->value_mask, &wc);
559         }
560         XSync(dpy, False);
561 }
562
563 void
564 destroynotify(XEvent *e) {
565         Client *c;
566         XDestroyWindowEvent *ev = &e->xdestroywindow;
567
568         if((c = getclient(ev->window)))
569                 unmanage(c);
570 }
571
572 void
573 detach(Client *c) {
574         Client **tc;
575
576         for(tc = &clients; *tc && *tc != c; tc = &(*tc)->next);
577         *tc = c->next;
578 }
579
580 void
581 detachstack(Client *c) {
582         Client **tc;
583
584         for(tc = &stack; *tc && *tc != c; tc = &(*tc)->snext);
585         *tc = c->snext;
586 }
587
588 void
589 die(const char *errstr, ...) {
590         va_list ap;
591
592         va_start(ap, errstr);
593         vfprintf(stderr, errstr, ap);
594         va_end(ap);
595         exit(EXIT_FAILURE);
596 }
597
598 void
599 drawbar(Monitor *m) {
600         int x;
601         unsigned int i, occ = 0, urg = 0;
602         unsigned long *col;
603         Client *c;
604
605         for(c = clients; c; c = c->next) {
606                 if(m == c->m) {
607                         occ |= c->tags;
608                         if(c->isurgent)
609                                 urg |= c->tags;
610                 }
611         }
612
613         dc.x = 0;
614 #ifdef XINERAMA
615         {
616                 /*
617                 dc.w = TEXTW(m->symbol);
618                 drawtext(NULL, selmon == m ? dc.sel : dc.norm, False);
619                 dc.x += dc.w;
620                 */
621         }
622 #endif /* XINERAMA */
623         m->btx = dc.x;
624         for(i = 0; i < LENGTH(tags); i++) {
625                 dc.w = TEXTW(tags[i]);
626                 col = m->tagset[m->seltags] & 1 << i ? dc.sel : dc.norm;
627                 drawtext(tags[i], col, urg & 1 << i);
628                 drawsquare(m == selmon && sel && sel->tags & 1 << i,
629                            occ & 1 << i, urg & 1 << i, col);
630                 dc.x += dc.w;
631         }
632         if(blw > 0) {
633                 dc.w = blw;
634                 drawtext(lt[m->sellt]->symbol, dc.norm, False);
635                 x = dc.x + dc.w;
636         }
637         else
638                 x = dc.x;
639         if(m == selmon) {
640                 dc.w = TEXTW(stext);
641                 dc.x = m->ww - dc.w;
642                 if(dc.x < x) {
643                         dc.x = x;
644                         dc.w = m->ww - x;
645                 }
646                 drawtext(stext, dc.norm, False);
647                 if((dc.w = dc.x - x) > bh) {
648                         dc.x = x;
649                         if(sel) {
650                                 drawtext(sel->name, dc.sel, False);
651                                 drawsquare(sel->isfixed, sel->isfloating, False, dc.sel);
652                         }
653                         else
654                                 drawtext(NULL, dc.norm, False);
655                 }
656         }
657         else {
658                 dc.x = x;
659                 dc.w = m->ww - x;
660                 drawtext(NULL, dc.norm, False);
661         }
662         XCopyArea(dpy, dc.drawable, m->barwin, dc.gc, 0, 0, m->ww, bh, 0, 0);
663         XSync(dpy, False);
664 }
665
666 void
667 drawbars() {
668         Monitor *m;
669
670         for(m = mons; m; m = m->next)
671                 drawbar(m);
672 }
673
674 void
675 drawsquare(Bool filled, Bool empty, Bool invert, unsigned long col[ColLast]) {
676         int x;
677         XGCValues gcv;
678         XRectangle r = { dc.x, dc.y, dc.w, dc.h };
679
680         gcv.foreground = col[invert ? ColBG : ColFG];
681         XChangeGC(dpy, dc.gc, GCForeground, &gcv);
682         x = (dc.font.ascent + dc.font.descent + 2) / 4;
683         r.x = dc.x + 1;
684         r.y = dc.y + 1;
685         if(filled) {
686                 r.width = r.height = x + 1;
687                 XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1);
688         }
689         else if(empty) {
690                 r.width = r.height = x;
691                 XDrawRectangles(dpy, dc.drawable, dc.gc, &r, 1);
692         }
693 }
694
695 void
696 drawtext(const char *text, unsigned long col[ColLast], Bool invert) {
697         char buf[256];
698         int i, x, y, h, len, olen;
699         XRectangle r = { dc.x, dc.y, dc.w, dc.h };
700
701         XSetForeground(dpy, dc.gc, col[invert ? ColFG : ColBG]);
702         XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1);
703         if(!text)
704                 return;
705         olen = strlen(text);
706         h = dc.font.ascent + dc.font.descent;
707         y = dc.y + (dc.h / 2) - (h / 2) + dc.font.ascent;
708         x = dc.x + (h / 2);
709         /* shorten text if necessary */
710         for(len = MIN(olen, sizeof buf); len && textnw(text, len) > dc.w - h; len--);
711         if(!len)
712                 return;
713         memcpy(buf, text, len);
714         if(len < olen)
715                 for(i = len; i && i > len - 3; buf[--i] = '.');
716         XSetForeground(dpy, dc.gc, col[invert ? ColBG : ColFG]);
717         if(dc.font.set)
718                 XmbDrawString(dpy, dc.drawable, dc.font.set, dc.gc, x, y, buf, len);
719         else
720                 XDrawString(dpy, dc.drawable, dc.gc, x, y, buf, len);
721 }
722
723 void
724 enternotify(XEvent *e) {
725         Client *c;
726         XCrossingEvent *ev = &e->xcrossing;
727
728         if((ev->mode != NotifyNormal || ev->detail == NotifyInferior) && ev->window != root)
729                 return;
730         if((c = getclient(ev->window)))
731                 focus(c);
732         else
733                 focus(NULL);
734 }
735
736 void
737 expose(XEvent *e) {
738         Monitor *m;
739         XExposeEvent *ev = &e->xexpose;
740
741         if(ev->count == 0)
742                 for(m = mons; m; m = m->next)
743                         if(ev->window == m->barwin) {
744                                 drawbar(m);
745                                 break;
746                         }
747 }
748
749 void
750 focus(Client *c) {
751         if(!c || !ISVISIBLE((c->m), c))
752                 for(c = stack; c && !ISVISIBLE(selmon, c); c = c->snext);
753         if(sel && sel != c) {
754                 grabbuttons(sel, False);
755                 XSetWindowBorder(dpy, sel->win, dc.norm[ColBorder]);
756         }
757         if(c) {
758                 if(c->isurgent)
759                         clearurgent(c);
760                 detachstack(c);
761                 attachstack(c);
762                 grabbuttons(c, True);
763                 XSetWindowBorder(dpy, c->win, dc.sel[ColBorder]);
764                 XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime);
765         }
766         else
767                 XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
768         sel = c;
769         drawbars();
770 }
771
772 void
773 focusin(XEvent *e) { /* there are some broken focus acquiring clients */
774         XFocusChangeEvent *ev = &e->xfocus;
775
776         if(sel && ev->window != sel->win)
777                 XSetInputFocus(dpy, sel->win, RevertToPointerRoot, CurrentTime);
778 }
779
780 #ifdef XINERAMA
781 void
782 focusmon(const Arg *arg) {
783         unsigned int i;
784         Monitor *m; 
785
786         for(i = 0, m = mons; m; m = m->next, i++)
787                 if(i == arg->ui) {
788                         selmon = m;
789                         focus(NULL);
790                         drawbars();
791                         break;
792                 }
793 }
794 #endif /* XINERAMA */
795
796 void
797 focusstack(const Arg *arg) {
798         Client *c = NULL, *i;
799
800         if(!sel)
801                 return;
802         if(arg->i > 0) {
803                 for(c = sel->next; c && !ISVISIBLE(selmon, c); c = c->next);
804                 if(!c)
805                         for(c = clients; c && !ISVISIBLE(selmon, c); c = c->next);
806         }
807         else {
808                 for(i = clients; i != sel; i = i->next)
809                         if(ISVISIBLE(selmon, i))
810                                 c = i;
811                 if(!c)
812                         for(; i; i = i->next)
813                                 if(ISVISIBLE(selmon, i))
814                                         c = i;
815         }
816         if(c) {
817                 focus(c);
818                 restack(selmon);
819         }
820 }
821
822 Client *
823 getclient(Window w) {
824         Client *c;
825
826         for(c = clients; c && c->win != w; c = c->next);
827         return c;
828 }
829
830 unsigned long
831 getcolor(const char *colstr) {
832         Colormap cmap = DefaultColormap(dpy, screen);
833         XColor color;
834
835         if(!XAllocNamedColor(dpy, cmap, colstr, &color, &color))
836                 die("error, cannot allocate color '%s'\n", colstr);
837         return color.pixel;
838 }
839
840 long
841 getstate(Window w) {
842         int format, status;
843         long result = -1;
844         unsigned char *p = NULL;
845         unsigned long n, extra;
846         Atom real;
847
848         status = XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState],
849                         &real, &format, &n, &extra, (unsigned char **)&p);
850         if(status != Success)
851                 return -1;
852         if(n != 0)
853                 result = *p;
854         XFree(p);
855         return result;
856 }
857
858 Bool
859 gettextprop(Window w, Atom atom, char *text, unsigned int size) {
860         char **list = NULL;
861         int n;
862         XTextProperty name;
863
864         if(!text || size == 0)
865                 return False;
866         text[0] = '\0';
867         XGetTextProperty(dpy, w, &name, atom);
868         if(!name.nitems)
869                 return False;
870         if(name.encoding == XA_STRING)
871                 strncpy(text, (char *)name.value, size - 1);
872         else {
873                 if(XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success
874                 && n > 0 && *list) {
875                         strncpy(text, *list, size - 1);
876                         XFreeStringList(list);
877                 }
878         }
879         text[size - 1] = '\0';
880         XFree(name.value);
881         return True;
882 }
883
884 void
885 grabbuttons(Client *c, Bool focused) {
886         updatenumlockmask();
887         {
888                 unsigned int i, j;
889                 unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask };
890                 XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
891                 if(focused) {
892                         for(i = 0; i < LENGTH(buttons); i++)
893                                 if(buttons[i].click == ClkClientWin)
894                                         for(j = 0; j < LENGTH(modifiers); j++)
895                                                 XGrabButton(dpy, buttons[i].button,
896                                                             buttons[i].mask | modifiers[j],
897                                                             c->win, False, BUTTONMASK,
898                                                             GrabModeAsync, GrabModeSync, None, None);
899                 } else
900                         XGrabButton(dpy, AnyButton, AnyModifier, c->win, False,
901                                     BUTTONMASK, GrabModeAsync, GrabModeSync, None, None);
902         }
903 }
904
905 void
906 grabkeys(void) {
907         updatenumlockmask();
908         { /* grab keys */
909                 unsigned int i, j;
910                 unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask };
911                 KeyCode code;
912
913                 XUngrabKey(dpy, AnyKey, AnyModifier, root);
914                 for(i = 0; i < LENGTH(keys); i++) {
915                         if((code = XKeysymToKeycode(dpy, keys[i].keysym)))
916                                 for(j = 0; j < LENGTH(modifiers); j++)
917                                         XGrabKey(dpy, code, keys[i].mod | modifiers[j], root,
918                                                  True, GrabModeAsync, GrabModeAsync);
919                 }
920         }
921 }
922
923 void
924 initfont(const char *fontstr) {
925         char *def, **missing;
926         int i, n;
927
928         missing = NULL;
929         dc.font.set = XCreateFontSet(dpy, fontstr, &missing, &n, &def);
930         if(missing) {
931                 while(n--)
932                         fprintf(stderr, "dwm: missing fontset: %s\n", missing[n]);
933                 XFreeStringList(missing);
934         }
935         if(dc.font.set) {
936                 XFontSetExtents *font_extents;
937                 XFontStruct **xfonts;
938                 char **font_names;
939                 dc.font.ascent = dc.font.descent = 0;
940                 font_extents = XExtentsOfFontSet(dc.font.set);
941                 n = XFontsOfFontSet(dc.font.set, &xfonts, &font_names);
942                 for(i = 0, dc.font.ascent = 0, dc.font.descent = 0; i < n; i++) {
943                         dc.font.ascent = MAX(dc.font.ascent, (*xfonts)->ascent);
944                         dc.font.descent = MAX(dc.font.descent,(*xfonts)->descent);
945                         xfonts++;
946                 }
947         }
948         else {
949                 if(!(dc.font.xfont = XLoadQueryFont(dpy, fontstr))
950                 && !(dc.font.xfont = XLoadQueryFont(dpy, "fixed")))
951                         die("error, cannot load font: '%s'\n", fontstr);
952                 dc.font.ascent = dc.font.xfont->ascent;
953                 dc.font.descent = dc.font.xfont->descent;
954         }
955         dc.font.height = dc.font.ascent + dc.font.descent;
956 }
957
958 Bool
959 isprotodel(Client *c) {
960         int i, n;
961         Atom *protocols;
962         Bool ret = False;
963
964         if(XGetWMProtocols(dpy, c->win, &protocols, &n)) {
965                 for(i = 0; !ret && i < n; i++)
966                         if(protocols[i] == wmatom[WMDelete])
967                                 ret = True;
968                 XFree(protocols);
969         }
970         return ret;
971 }
972
973 void
974 keypress(XEvent *e) {
975         unsigned int i;
976         KeySym keysym;
977         XKeyEvent *ev;
978
979         ev = &e->xkey;
980         keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0);
981         for(i = 0; i < LENGTH(keys); i++)
982                 if(keysym == keys[i].keysym
983                    && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state)
984                    && keys[i].func)
985                         keys[i].func(&(keys[i].arg));
986 }
987
988 void
989 killclient(const Arg *arg) {
990         XEvent ev;
991
992         if(!sel)
993                 return;
994         if(isprotodel(sel)) {
995                 ev.type = ClientMessage;
996                 ev.xclient.window = sel->win;
997                 ev.xclient.message_type = wmatom[WMProtocols];
998                 ev.xclient.format = 32;
999                 ev.xclient.data.l[0] = wmatom[WMDelete];
1000                 ev.xclient.data.l[1] = CurrentTime;
1001                 XSendEvent(dpy, sel->win, False, NoEventMask, &ev);
1002         }
1003         else
1004                 XKillClient(dpy, sel->win);
1005 }
1006
1007 void
1008 manage(Window w, XWindowAttributes *wa) {
1009         static Client cz;
1010         Client *c, *t = NULL;
1011         Window trans = None;
1012         XWindowChanges wc;
1013
1014         if(!(c = malloc(sizeof(Client))))
1015                 die("fatal: could not malloc() %u bytes\n", sizeof(Client));
1016         *c = cz;
1017         c->win = w;
1018         c->m = selmon;
1019
1020         /* geometry */
1021         c->x = wa->x;
1022         c->y = wa->y;
1023         c->w = wa->width;
1024         c->h = wa->height;
1025         c->oldbw = wa->border_width;
1026         if(c->w == sw && c->h == sh) {
1027                 c->x = sx;
1028                 c->y = sy;
1029                 c->bw = 0;
1030         }
1031         else {
1032                 if(c->x + WIDTH(c) > sx + sw)
1033                         c->x = sx + sw - WIDTH(c);
1034                 if(c->y + HEIGHT(c) > sy + sh)
1035                         c->y = sy + sh - HEIGHT(c);
1036                 c->x = MAX(c->x, sx);
1037                 /* only fix client y-offset, if the client center might cover the bar */
1038                 c->y = MAX(c->y, ((selmon->by == 0) && (c->x + (c->w / 2) >= selmon->wx)
1039                            && (c->x + (c->w / 2) < selmon->wx + selmon->ww)) ? bh : sy);
1040                 c->bw = borderpx;
1041         }
1042
1043         wc.border_width = c->bw;
1044         XConfigureWindow(dpy, w, CWBorderWidth, &wc);
1045         XSetWindowBorder(dpy, w, dc.norm[ColBorder]);
1046         configure(c); /* propagates border_width, if size doesn't change */
1047         updatesizehints(c);
1048         XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask);
1049         grabbuttons(c, False);
1050         updatetitle(c);
1051         if(XGetTransientForHint(dpy, w, &trans))
1052                 t = getclient(trans);
1053         if(t)
1054                 c->tags = t->tags;
1055         else
1056                 applyrules(c);
1057         if(!c->isfloating)
1058                 c->isfloating = trans != None || c->isfixed;
1059         if(c->isfloating)
1060                 XRaiseWindow(dpy, c->win);
1061         attach(c);
1062         attachstack(c);
1063         XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */
1064         XMapWindow(dpy, c->win);
1065         setclientstate(c, NormalState);
1066         arrange();
1067 }
1068
1069 void
1070 mappingnotify(XEvent *e) {
1071         XMappingEvent *ev = &e->xmapping;
1072
1073         XRefreshKeyboardMapping(ev);
1074         if(ev->request == MappingKeyboard)
1075                 grabkeys();
1076 }
1077
1078 void
1079 maprequest(XEvent *e) {
1080         static XWindowAttributes wa;
1081         XMapRequestEvent *ev = &e->xmaprequest;
1082
1083         if(!XGetWindowAttributes(dpy, ev->window, &wa))
1084                 return;
1085         if(wa.override_redirect)
1086                 return;
1087         if(!getclient(ev->window))
1088                 manage(ev->window, &wa);
1089 }
1090
1091 void
1092 monocle(Monitor *m) {
1093         Client *c;
1094
1095         for(c = nexttiled(m, clients); c; c = nexttiled(m, c->next))
1096                 resize(c, m->wx, m->wy, m->ww - 2 * c->bw, m->wh - 2 * c->bw);
1097 }
1098
1099 void
1100 movemouse(const Arg *arg) {
1101         int x, y, ocx, ocy, di, nx, ny;
1102         unsigned int dui;
1103         Client *c;
1104         Window dummy;
1105         XEvent ev;
1106
1107         if(!(c = sel))
1108                 return;
1109         restack(selmon);
1110         ocx = c->x;
1111         ocy = c->y;
1112         if(XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync,
1113         None, cursor[CurMove], CurrentTime) != GrabSuccess)
1114                 return;
1115         XQueryPointer(dpy, root, &dummy, &dummy, &x, &y, &di, &di, &dui);
1116         do {
1117                 XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev);
1118                 switch (ev.type) {
1119                 case ConfigureRequest:
1120                 case Expose:
1121                 case MapRequest:
1122                         handler[ev.type](&ev);
1123                         break;
1124                 case MotionNotify:
1125                         nx = ocx + (ev.xmotion.x - x);
1126                         ny = ocy + (ev.xmotion.y - y);
1127                         if(snap && nx >= selmon->wx && nx <= selmon->wx + selmon->ww
1128                                 && ny >= selmon->wy && ny <= selmon->wy + selmon->wh) {
1129                                 if(abs(selmon->wx - nx) < snap)
1130                                         nx = selmon->wx;
1131                                 else if(abs((selmon->wx + selmon->ww) - (nx + WIDTH(c))) < snap)
1132                                         nx = selmon->wx + selmon->ww - WIDTH(c);
1133                                 if(abs(selmon->wy - ny) < snap)
1134                                         ny = selmon->wy;
1135                                 else if(abs((selmon->wy + selmon->wh) - (ny + HEIGHT(c))) < snap)
1136                                         ny = selmon->wy + selmon->wh - HEIGHT(c);
1137                                 if(!c->isfloating && lt[selmon->sellt]->arrange
1138                                                   && (abs(nx - c->x) > snap || abs(ny - c->y) > snap))
1139                                         togglefloating(NULL);
1140                         }
1141                         if(!lt[selmon->sellt]->arrange || c->isfloating)
1142                                 resize(c, nx, ny, c->w, c->h);
1143                         break;
1144                 }
1145         }
1146         while(ev.type != ButtonRelease);
1147         XUngrabPointer(dpy, CurrentTime);
1148 }
1149
1150 Client *
1151 nexttiled(Monitor *m, Client *c) {
1152         // TODO: m handling
1153         for(; c && (c->isfloating || !ISVISIBLE(m, c)); c = c->next);
1154         return c;
1155 }
1156
1157 void
1158 propertynotify(XEvent *e) {
1159         Client *c;
1160         Window trans;
1161         XPropertyEvent *ev = &e->xproperty;
1162
1163         if((ev->window == root) && (ev->atom == XA_WM_NAME))
1164                 updatestatus();
1165         else if(ev->state == PropertyDelete)
1166                 return; /* ignore */
1167         else if((c = getclient(ev->window))) {
1168                 switch (ev->atom) {
1169                 default: break;
1170                 case XA_WM_TRANSIENT_FOR:
1171                         XGetTransientForHint(dpy, c->win, &trans);
1172                         if(!c->isfloating && (c->isfloating = (getclient(trans) != NULL)))
1173                                 arrange();
1174                         break;
1175                 case XA_WM_NORMAL_HINTS:
1176                         updatesizehints(c);
1177                         break;
1178                 case XA_WM_HINTS:
1179                         updatewmhints(c);
1180                         drawbars();
1181                         break;
1182                 }
1183                 if(ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) {
1184                         updatetitle(c);
1185                         if(c == sel)
1186                                 drawbars();
1187                 }
1188         }
1189 }
1190
1191 void
1192 quit(const Arg *arg) {
1193         running = False;
1194 }
1195
1196 void
1197 resize(Client *c, int x, int y, int w, int h) {
1198         XWindowChanges wc;
1199
1200         if(applysizehints(c, &x, &y, &w, &h)) {
1201                 c->x = wc.x = x;
1202                 c->y = wc.y = y;
1203                 c->w = wc.width = w;
1204                 c->h = wc.height = h;
1205                 wc.border_width = c->bw;
1206                 XConfigureWindow(dpy, c->win,
1207                                 CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc);
1208                 configure(c);
1209                 XSync(dpy, False);
1210         }
1211 }
1212
1213 void
1214 resizemouse(const Arg *arg) {
1215         int ocx, ocy;
1216         int nw, nh;
1217         Client *c;
1218         XEvent ev;
1219
1220         if(!(c = sel))
1221                 return;
1222         restack(selmon);
1223         ocx = c->x;
1224         ocy = c->y;
1225         if(XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync,
1226         None, cursor[CurResize], CurrentTime) != GrabSuccess)
1227                 return;
1228         XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1);
1229         do {
1230                 XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev);
1231                 switch(ev.type) {
1232                 case ConfigureRequest:
1233                 case Expose:
1234                 case MapRequest:
1235                         handler[ev.type](&ev);
1236                         break;
1237                 case MotionNotify:
1238                         nw = MAX(ev.xmotion.x - ocx - 2 * c->bw + 1, 1);
1239                         nh = MAX(ev.xmotion.y - ocy - 2 * c->bw + 1, 1);
1240
1241                         if(snap && nw >= selmon->wx && nw <= selmon->wx + selmon->ww
1242                                 && nh >= selmon->wy && nh <= selmon->wy + selmon->wh) {
1243                                 if(!c->isfloating && lt[selmon->sellt]->arrange
1244                                    && (abs(nw - c->w) > snap || abs(nh - c->h) > snap))
1245                                         togglefloating(NULL);
1246                         }
1247                         if(!lt[selmon->sellt]->arrange || c->isfloating)
1248                                 resize(c, c->x, c->y, nw, nh);
1249                         break;
1250                 }
1251         }
1252         while(ev.type != ButtonRelease);
1253         XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1);
1254         XUngrabPointer(dpy, CurrentTime);
1255         while(XCheckMaskEvent(dpy, EnterWindowMask, &ev));
1256 }
1257
1258 void
1259 restack(Monitor *m) {
1260         Client *c;
1261         XEvent ev;
1262         XWindowChanges wc;
1263
1264         drawbars();
1265         if(!sel)
1266                 return;
1267         if(m == selmon && (sel->isfloating || !lt[m->sellt]->arrange))
1268                 XRaiseWindow(dpy, sel->win);
1269         if(lt[m->sellt]->arrange) {
1270                 wc.stack_mode = Below;
1271                 wc.sibling = m->barwin;
1272                 for(c = stack; c; c = c->snext)
1273                         if(!c->isfloating && ISVISIBLE(m, c)) {
1274                                 XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc);
1275                                 wc.sibling = c->win;
1276                         }
1277         }
1278         XSync(dpy, False);
1279         while(XCheckMaskEvent(dpy, EnterWindowMask, &ev));
1280 }
1281
1282 void
1283 run(void) {
1284         XEvent ev;
1285
1286         /* main event loop */
1287         XSync(dpy, False);
1288         while(running && !XNextEvent(dpy, &ev)) {
1289                 if(handler[ev.type])
1290                         (handler[ev.type])(&ev); /* call handler */
1291         }
1292 }
1293
1294 void
1295 scan(void) {
1296         unsigned int i, num;
1297         Window d1, d2, *wins = NULL;
1298         XWindowAttributes wa;
1299
1300         if(XQueryTree(dpy, root, &d1, &d2, &wins, &num)) {
1301                 for(i = 0; i < num; i++) {
1302                         if(!XGetWindowAttributes(dpy, wins[i], &wa)
1303                         || wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1))
1304                                 continue;
1305                         if(wa.map_state == IsViewable || getstate(wins[i]) == IconicState)
1306                                 manage(wins[i], &wa);
1307                 }
1308                 for(i = 0; i < num; i++) { /* now the transients */
1309                         if(!XGetWindowAttributes(dpy, wins[i], &wa))
1310                                 continue;
1311                         if(XGetTransientForHint(dpy, wins[i], &d1)
1312                         && (wa.map_state == IsViewable || getstate(wins[i]) == IconicState))
1313                                 manage(wins[i], &wa);
1314                 }
1315                 if(wins)
1316                         XFree(wins);
1317         }
1318 }
1319
1320 void
1321 setclientstate(Client *c, long state) {
1322         long data[] = {state, None};
1323
1324         XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32,
1325                         PropModeReplace, (unsigned char *)data, 2);
1326 }
1327
1328 void
1329 setlayout(const Arg *arg) {
1330         if(!arg || !arg->v || arg->v != lt[selmon->sellt])
1331                 selmon->sellt ^= 1;
1332         if(arg && arg->v)
1333                 lt[selmon->sellt] = (Layout *)arg->v;
1334         if(sel)
1335                 arrange();
1336         else
1337                 drawbars();
1338 }
1339
1340 /* arg > 1.0 will set mfact absolutly */
1341 void
1342 setmfact(const Arg *arg) {
1343         float f;
1344
1345         if(!arg || !lt[selmon->sellt]->arrange)
1346                 return;
1347         f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0;
1348         if(f < 0.1 || f > 0.9)
1349                 return;
1350         selmon->mfact = f;
1351         arrange();
1352 }
1353
1354 void
1355 setup(void) {
1356         unsigned int i;
1357         int w;
1358         XSetWindowAttributes wa;
1359
1360         /* init screen */
1361         screen = DefaultScreen(dpy);
1362         root = RootWindow(dpy, screen);
1363         initfont(font);
1364         sx = 0;
1365         sy = 0;
1366         sw = DisplayWidth(dpy, screen);
1367         sh = DisplayHeight(dpy, screen);
1368         bh = dc.h = dc.font.height + 2;
1369         lt[0] = &layouts[0];
1370         lt[1] = &layouts[1 % LENGTH(layouts)];
1371         updategeom();
1372
1373         /* init atoms */
1374         wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False);
1375         wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
1376         wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False);
1377         netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False);
1378         netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False);
1379
1380         /* init cursors */
1381         cursor[CurNormal] = XCreateFontCursor(dpy, XC_left_ptr);
1382         cursor[CurResize] = XCreateFontCursor(dpy, XC_sizing);
1383         cursor[CurMove] = XCreateFontCursor(dpy, XC_fleur);
1384
1385         /* init appearance */
1386         dc.norm[ColBorder] = getcolor(normbordercolor);
1387         dc.norm[ColBG] = getcolor(normbgcolor);
1388         dc.norm[ColFG] = getcolor(normfgcolor);
1389         dc.sel[ColBorder] = getcolor(selbordercolor);
1390         dc.sel[ColBG] = getcolor(selbgcolor);
1391         dc.sel[ColFG] = getcolor(selfgcolor);
1392         dc.drawable = XCreatePixmap(dpy, root, DisplayWidth(dpy, screen), bh, DefaultDepth(dpy, screen));
1393         dc.gc = XCreateGC(dpy, root, 0, NULL);
1394         XSetLineAttributes(dpy, dc.gc, 1, LineSolid, CapButt, JoinMiter);
1395         if(!dc.font.set)
1396                 XSetFont(dpy, dc.gc, dc.font.xfont->fid);
1397
1398         /* init bars */
1399         for(blw = i = 0; LENGTH(layouts) > 1 && i < LENGTH(layouts); i++) {
1400                 w = TEXTW(layouts[i].symbol);
1401                 blw = MAX(blw, w);
1402         }
1403         updatebars();
1404         updatestatus();
1405
1406         /* EWMH support per view */
1407         XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32,
1408                         PropModeReplace, (unsigned char *) netatom, NetLast);
1409
1410         /* select for events */
1411         wa.event_mask = SubstructureRedirectMask|SubstructureNotifyMask|ButtonPressMask
1412                         |EnterWindowMask|LeaveWindowMask|StructureNotifyMask
1413                         |PropertyChangeMask;
1414         XChangeWindowAttributes(dpy, root, CWEventMask|CWCursor, &wa);
1415         XSelectInput(dpy, root, wa.event_mask);
1416
1417         grabkeys();
1418 }
1419
1420 void
1421 showhide(Client *c) {
1422         if(!c)
1423                 return;
1424         if(ISVISIBLE((c->m), c)) { /* show clients top down */
1425                 XMoveWindow(dpy, c->win, c->x, c->y);
1426                 if(!lt[c->m->sellt]->arrange || c->isfloating)
1427                         resize(c, c->x, c->y, c->w, c->h);
1428                 showhide(c->snext);
1429         }
1430         else { /* hide clients bottom up */
1431                 showhide(c->snext);
1432                 XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y);
1433         }
1434 }
1435
1436
1437 void
1438 sigchld(int signal) {
1439         while(0 < waitpid(-1, NULL, WNOHANG));
1440 }
1441
1442 void
1443 spawn(const Arg *arg) {
1444         signal(SIGCHLD, sigchld);
1445         if(fork() == 0) {
1446                 if(dpy)
1447                         close(ConnectionNumber(dpy));
1448                 setsid();
1449                 execvp(((char **)arg->v)[0], (char **)arg->v);
1450                 fprintf(stderr, "dwm: execvp %s", ((char **)arg->v)[0]);
1451                 perror(" failed");
1452                 exit(0);
1453         }
1454 }
1455
1456 void
1457 tag(const Arg *arg) {
1458         if(sel && arg->ui & TAGMASK) {
1459                 sel->tags = arg->ui & TAGMASK;
1460                 arrange();
1461         }
1462 }
1463
1464 #ifdef XINERAMA
1465 void
1466 tagmon(const Arg *arg) {
1467         unsigned int i;
1468         Monitor *m;
1469
1470         for(i = 0, m = mons; m; m = m->next, i++)
1471                 if(i == arg->ui) {
1472                         sel->m = m;
1473                         arrange();
1474                         break;
1475                 }
1476 }
1477 #endif /* XINERAMA */
1478
1479 int
1480 textnw(const char *text, unsigned int len) {
1481         XRectangle r;
1482
1483         if(dc.font.set) {
1484                 XmbTextExtents(dc.font.set, text, len, NULL, &r);
1485                 return r.width;
1486         }
1487         return XTextWidth(dc.font.xfont, text, len);
1488 }
1489
1490 void
1491 tile(Monitor *m) {
1492         int x, y, h, w, mw;
1493         unsigned int i, n;
1494         Client *c;
1495
1496         for(n = 0, c = nexttiled(m, clients); c; c = nexttiled(m, c->next), n++);
1497         if(n == 0)
1498                 return;
1499
1500         /* master */
1501         c = nexttiled(m, clients);
1502         mw = m->mfact * m->ww;
1503         resize(c, m->wx, m->wy, (n == 1 ? m->ww : mw) - 2 * c->bw, m->wh - 2 * c->bw);
1504
1505         if(--n == 0)
1506                 return;
1507
1508         /* tile stack */
1509         x = (m->wx + mw > c->x + c->w) ? c->x + c->w + 2 * c->bw : m->wx + mw;
1510         y = m->wy;
1511         w = (m->wx + mw > c->x + c->w) ? m->wx + m->ww - x : m->ww - mw;
1512         h = m->wh / n;
1513         if(h < bh)
1514                 h = m->wh;
1515
1516         for(i = 0, c = nexttiled(m, c->next); c; c = nexttiled(m, c->next), i++) {
1517                 resize(c, x, y, w - 2 * c->bw, /* remainder */ ((i + 1 == n)
1518                        ? m->wy + m->wh - y - 2 * c->bw : h - 2 * c->bw));
1519                 if(h != m->wh)
1520                         y = c->y + HEIGHT(c);
1521         }
1522 }
1523
1524 void
1525 togglebar(const Arg *arg) {
1526         selmon->showbar = !selmon->showbar;
1527         updategeom();
1528         XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh);
1529         arrange();
1530 }
1531
1532 void
1533 togglefloating(const Arg *arg) {
1534         if(!sel)
1535                 return;
1536         sel->isfloating = !sel->isfloating || sel->isfixed;
1537         if(sel->isfloating)
1538                 resize(sel, sel->x, sel->y, sel->w, sel->h);
1539         arrange();
1540 }
1541
1542 void
1543 toggletag(const Arg *arg) {
1544         unsigned int mask;
1545
1546         if(!sel)
1547                 return;
1548         
1549         mask = sel->tags ^ (arg->ui & TAGMASK);
1550         if(mask) {
1551                 sel->tags = mask;
1552                 arrange();
1553         }
1554 }
1555
1556 void
1557 toggleview(const Arg *arg) {
1558         unsigned int mask = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK);
1559
1560         if(mask) {
1561                 selmon->tagset[selmon->seltags] = mask;
1562                 arrange();
1563         }
1564 }
1565
1566 void
1567 unmanage(Client *c) {
1568         XWindowChanges wc;
1569
1570         wc.border_width = c->oldbw;
1571         /* The server grab construct avoids race conditions. */
1572         XGrabServer(dpy);
1573         XSetErrorHandler(xerrordummy);
1574         XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */
1575         detach(c);
1576         detachstack(c);
1577         if(sel == c)
1578                 focus(NULL);
1579         XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
1580         setclientstate(c, WithdrawnState);
1581         free(c);
1582         XSync(dpy, False);
1583         XSetErrorHandler(xerror);
1584         XUngrabServer(dpy);
1585         arrange();
1586 }
1587
1588 void
1589 unmapnotify(XEvent *e) {
1590         Client *c;
1591         XUnmapEvent *ev = &e->xunmap;
1592
1593         if((c = getclient(ev->window)))
1594                 unmanage(c);
1595 }
1596
1597 void
1598 updatebars(void) {
1599         Monitor *m;
1600         XSetWindowAttributes wa;
1601
1602         wa.cursor = cursor[CurNormal];
1603         wa.override_redirect = True;
1604         wa.background_pixmap = ParentRelative;
1605         wa.event_mask = ButtonPressMask|ExposureMask;
1606
1607         for(m = mons; m; m = m->next) {
1608                 m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen),
1609
1610                                           CopyFromParent, DefaultVisual(dpy, screen),
1611                                           CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa);
1612                 XDefineCursor(dpy, m->barwin, cursor[CurNormal]);
1613                 XMapRaised(dpy, m->barwin);
1614         }
1615 }
1616
1617 void
1618 updategeom(void) {
1619         int i, n;
1620         Client *c;
1621         Monitor *newmons = NULL, *m;
1622
1623 #ifdef XINERAMA
1624         XineramaScreenInfo *info = NULL;
1625
1626         if(XineramaIsActive(dpy))
1627                 info = XineramaQueryScreens(dpy, &n);
1628 #endif
1629         /* allocate monitor(s) for the new geometry setup */
1630         for(i = 0; i < n; i++) {
1631                 m = (Monitor *)malloc(sizeof(Monitor));
1632                 m->next = newmons;
1633                 newmons = m;
1634         }
1635
1636         /* initialise monitor(s) */
1637 #ifdef XINERAMA
1638         if(XineramaIsActive(dpy)) {
1639                 for(i = 0, m = newmons; m; m = m->next, i++) {
1640                         m->screen_number = info[i].screen_number;
1641                         m->wx = info[i].x_org;
1642                         m->wy = info[i].y_org;
1643                         m->ww = info[i].width;
1644                         m->wh = info[i].height;
1645                 }
1646                 XFree(info);
1647         }
1648         else
1649 #endif
1650         /* default monitor setup */
1651         {
1652                 m->screen_number = 0;
1653                 m->wx = sx;
1654                 m->wy = sy;
1655                 m->ww = sw;
1656                 m->wh = sh;
1657         }
1658
1659         /* bar geometry setup */
1660         for(m = newmons; m; m = m->next) {
1661                 /* TODO: consider removing the following values from config.h */
1662                 m->seltags = 0;
1663                 m->sellt = 0;
1664                 m->tagset[0] = m->tagset[1] = 1;
1665                 m->mfact = mfact;
1666                 m->showbar = showbar;
1667                 m->topbar = topbar;
1668                 if(m->showbar) {
1669                         m->wh -= bh;
1670                         m->by = m->topbar ? m->wy : m->wy + m->wh;
1671                         m->wy = m->topbar ? m->wy + bh : m->wy;
1672                 }
1673                 else
1674                         m->by = -bh;
1675                 /* reassign all clients with same screen number */
1676                 for(c = clients; c; c = c->next)
1677                         if(c->m->screen_number == m->screen_number)
1678                                 c->m = m;
1679         }
1680
1681         /* reassign left over clients with disappeared screen number */
1682         for(c = clients; c; c = c->next)
1683                 if(c->m->screen_number >= n)
1684                         c->m = newmons;
1685
1686         /* select focused monitor */
1687         if(!selmon) {
1688                 selmon = newmons;
1689                 int di, x, y;
1690                 unsigned int dui;
1691                 Window dummy;
1692                 if(XQueryPointer(dpy, root, &dummy, &dummy, &x, &y, &di, &di, &dui)) 
1693                         for(m = newmons; m; m = m->next)
1694                                 if(INRECT(x, y, m->wx, m->wy, m->ww, m->wh)) {
1695                                         selmon = m;
1696                                         break;
1697                                 }
1698         }
1699
1700         /* final assignment of new monitors */
1701         cleanupmons();
1702         mons = newmons;
1703 }
1704
1705 void
1706 updatenumlockmask(void) {
1707         unsigned int i, j;
1708         XModifierKeymap *modmap;
1709
1710         numlockmask = 0;
1711         modmap = XGetModifierMapping(dpy);
1712         for(i = 0; i < 8; i++)
1713                 for(j = 0; j < modmap->max_keypermod; j++)
1714                         if(modmap->modifiermap[i * modmap->max_keypermod + j]
1715                            == XKeysymToKeycode(dpy, XK_Num_Lock))
1716                                 numlockmask = (1 << i);
1717         XFreeModifiermap(modmap);
1718 }
1719
1720 void
1721 updatesizehints(Client *c) {
1722         long msize;
1723         XSizeHints size;
1724
1725         if(!XGetWMNormalHints(dpy, c->win, &size, &msize))
1726                 /* size is uninitialized, ensure that size.flags aren't used */
1727                 size.flags = PSize;
1728         if(size.flags & PBaseSize) {
1729                 c->basew = size.base_width;
1730                 c->baseh = size.base_height;
1731         }
1732         else if(size.flags & PMinSize) {
1733                 c->basew = size.min_width;
1734                 c->baseh = size.min_height;
1735         }
1736         else
1737                 c->basew = c->baseh = 0;
1738         if(size.flags & PResizeInc) {
1739                 c->incw = size.width_inc;
1740                 c->inch = size.height_inc;
1741         }
1742         else
1743                 c->incw = c->inch = 0;
1744         if(size.flags & PMaxSize) {
1745                 c->maxw = size.max_width;
1746                 c->maxh = size.max_height;
1747         }
1748         else
1749                 c->maxw = c->maxh = 0;
1750         if(size.flags & PMinSize) {
1751                 c->minw = size.min_width;
1752                 c->minh = size.min_height;
1753         }
1754         else if(size.flags & PBaseSize) {
1755                 c->minw = size.base_width;
1756                 c->minh = size.base_height;
1757         }
1758         else
1759                 c->minw = c->minh = 0;
1760         if(size.flags & PAspect) {
1761                 c->mina = (float)size.min_aspect.y / (float)size.min_aspect.x;
1762                 c->maxa = (float)size.max_aspect.x / (float)size.max_aspect.y;
1763         }
1764         else
1765                 c->maxa = c->mina = 0.0;
1766         c->isfixed = (c->maxw && c->minw && c->maxh && c->minh
1767                      && c->maxw == c->minw && c->maxh == c->minh);
1768 }
1769
1770 void
1771 updatetitle(Client *c) {
1772         if(!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name))
1773                 gettextprop(c->win, XA_WM_NAME, c->name, sizeof c->name);
1774 }
1775
1776 void
1777 updatestatus() {
1778         if(!gettextprop(root, XA_WM_NAME, stext, sizeof(stext)))
1779                 strcpy(stext, "dwm-"VERSION);
1780         drawbar(selmon);
1781 }
1782
1783 void
1784 updatewmhints(Client *c) {
1785         XWMHints *wmh;
1786
1787         if((wmh = XGetWMHints(dpy, c->win))) {
1788                 if(c == sel && wmh->flags & XUrgencyHint) {
1789                         wmh->flags &= ~XUrgencyHint;
1790                         XSetWMHints(dpy, c->win, wmh);
1791                 }
1792                 else
1793                         c->isurgent = (wmh->flags & XUrgencyHint) ? True : False;
1794
1795                 XFree(wmh);
1796         }
1797 }
1798
1799 void
1800 view(const Arg *arg) {
1801         if((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags])
1802                 return;
1803         selmon->seltags ^= 1; /* toggle sel tagset */
1804         if(arg->ui & TAGMASK)
1805                 selmon->tagset[selmon->seltags] = arg->ui & TAGMASK;
1806         arrange();
1807 }
1808
1809 /* There's no way to check accesses to destroyed windows, thus those cases are
1810  * ignored (especially on UnmapNotify's).  Other types of errors call Xlibs
1811  * default error handler, which may call exit.  */
1812 int
1813 xerror(Display *dpy, XErrorEvent *ee) {
1814         if(ee->error_code == BadWindow
1815         || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch)
1816         || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable)
1817         || (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable)
1818         || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable)
1819         || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch)
1820         || (ee->request_code == X_GrabButton && ee->error_code == BadAccess)
1821         || (ee->request_code == X_GrabKey && ee->error_code == BadAccess)
1822         || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable))
1823                 return 0;
1824         fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n",
1825                         ee->request_code, ee->error_code);
1826         return xerrorxlib(dpy, ee); /* may call exit */
1827 }
1828
1829 int
1830 xerrordummy(Display *dpy, XErrorEvent *ee) {
1831         return 0;
1832 }
1833
1834 /* Startup Error handler to check if another window manager
1835  * is already running. */
1836 int
1837 xerrorstart(Display *dpy, XErrorEvent *ee) {
1838         otherwm = True;
1839         return -1;
1840 }
1841
1842 void
1843 zoom(const Arg *arg) {
1844         Client *c = sel;
1845
1846         if(!lt[selmon->sellt]->arrange || lt[selmon->sellt]->arrange == monocle || (sel && sel->isfloating))
1847                 return;
1848         if(c == nexttiled(selmon, clients))
1849                 if(!c || !(c = nexttiled(selmon, c->next)))
1850                         return;
1851         detach(c);
1852         attach(c);
1853         focus(c);
1854         arrange();
1855 }
1856
1857 int
1858 main(int argc, char *argv[]) {
1859         if(argc == 2 && !strcmp("-v", argv[1]))
1860                 die("dwm-"VERSION", © 2006-2009 dwm engineers, see LICENSE for details\n");
1861         else if(argc != 1)
1862                 die("usage: dwm [-v]\n");
1863
1864         if(!setlocale(LC_CTYPE, "") || !XSupportsLocale())
1865                 fputs("warning: no locale support\n", stderr);
1866
1867         if(!(dpy = XOpenDisplay(NULL)))
1868                 die("dwm: cannot open display\n");
1869
1870         checkotherwm();
1871         setup();
1872         scan();
1873         run();
1874         cleanup();
1875
1876         XCloseDisplay(dpy);
1877         return 0;
1878 }