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