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