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