560144a714cdaaa3839b819753ddbbb1b28664ef
[dwm.git] / dwm.c
1 /* See LICENSE file for copyright and license details.
2 *
3 * dynamic window manager is designed like any other X client as well. It is
4 * driven through handling X events. In contrast to other X clients, a window
5 * manager selects for SubstructureRedirectMask on the root window, to receive
6 * events about window (dis-)appearance. Only one X connection at a time is
7 * allowed to select for this event mask.
8 *
9 * The event handlers of dwm are organized in an array which is accessed
10 * whenever a new event has been fetched. This allows event dispatching
11 * in O(1) time.
12 *
13 * Each child of the root window is called a client, except windows which have
14 * set the override_redirect flag. Clients are organized in a linked client
15 * list on each monitor, the focus history is remembered through a stack list
16 * on each monitor. Each client contains a bit array to indicate the tags of a
17 * client.
18 *
19 * Keys and tagging rules are organized as arrays and defined in config.h.
20 *
21 * To understand everything else, start reading main().
22 */
23 #include <errno.h>
24 #include <locale.h>
25 #include <stdarg.h>
26 #include <signal.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include <sys/types.h>
32 #include <sys/wait.h>
33 #include <X11/cursorfont.h>
34 #include <X11/keysym.h>
35 #include <X11/Xatom.h>
36 #include <X11/Xlib.h>
37 #include <X11/Xproto.h>
38 #include <X11/Xutil.h>
39 #include <X11/Xft/Xft.h>
40 #include <pango/pango.h>
41 #include <pango/pangoxft.h>
42 #ifdef XINERAMA
43 #include <X11/extensions/Xinerama.h>
44 #endif /* XINERAMA */
45
46 /* macros */
47 #define BUTTONMASK (ButtonPressMask|ButtonReleaseMask)
48 #define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask))
49 #define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \
50 * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy)))
51 #define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags]))
52 #define LENGTH(X) (sizeof X / sizeof X[0])
53 #ifndef MAX
54 #define MAX(A, B) ((A) > (B) ? (A) : (B))
55 #endif
56 #ifndef MIN
57 #define MIN(A, B) ((A) < (B) ? (A) : (B))
58 #endif
59 #define MOUSEMASK (BUTTONMASK|PointerMotionMask)
60 #define WIDTH(X) ((X)->w + 2 * (X)->bw)
61 #define HEIGHT(X) ((X)->h + 2 * (X)->bw)
62 #define TAGMASK ((1 << LENGTH(tags)) - 1)
63 #define TEXTW(X) (textnw(X, strlen(X)) + dc.font.height)
64
65 /* enums */
66 enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */
67 enum { ColBorder, ColFG, ColBG, ColLast }; /* color */
68 enum { NetSupported, NetWMName, NetWMState,
69 NetWMFullscreen, NetActiveWindow, NetWMWindowType,
70 NetWMWindowTypeDialog, NetLast }; /* EWMH atoms */
71 enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */
72 enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle,
73 ClkClientWin, ClkRootWin, ClkLast }; /* clicks */
74
75 typedef union {
76 int i;
77 unsigned int ui;
78 float f;
79 const void *v;
80 } Arg;
81
82 typedef struct {
83 unsigned int click;
84 unsigned int mask;
85 unsigned int button;
86 void (*func)(const Arg *arg);
87 const Arg arg;
88 } Button;
89
90 typedef struct Monitor Monitor;
91 typedef struct Client Client;
92 struct Client {
93 char name[256];
94 float mina, maxa;
95 int x, y, w, h;
96 int oldx, oldy, oldw, oldh;
97 int basew, baseh, incw, inch, maxw, maxh, minw, minh;
98 int bw, oldbw;
99 unsigned int tags;
100 Bool isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen;
101 Client *next;
102 Client *snext;
103 Monitor *mon;
104 Window win;
105 };
106
107 typedef struct {
108 int x, y, w, h;
109 unsigned long norm[ColLast];
110 unsigned long sel[ColLast];
111 Drawable drawable;
112 GC gc;
113 struct {
114 XftColor norm[ColLast];
115 XftColor sel[ColLast];
116 XftDraw *drawable;
117 } xft;
118 struct {
119 int ascent;
120 int descent;
121 int height;
122 PangoLayout *layout;
123 } font;
124 } DC; /* draw context */
125
126 typedef struct {
127 unsigned int mod;
128 KeySym keysym;
129 void (*func)(const Arg *);
130 const Arg arg;
131 } Key;
132
133 typedef struct {
134 const char *symbol;
135 void (*arrange)(Monitor *);
136 } Layout;
137
138 struct Monitor {
139 char ltsymbol[16];
140 float mfact;
141 int nmaster;
142 int num;
143 int by; /* bar geometry */
144 int mx, my, mw, mh; /* screen size */
145 int wx, wy, ww, wh; /* window area */
146 unsigned int seltags;
147 unsigned int sellt;
148 unsigned int tagset[2];
149 Bool showbar;
150 Bool topbar;
151 Client *clients;
152 Client *sel;
153 Client *stack;
154 Monitor *next;
155 Window barwin;
156 const Layout *lt[2];
157 };
158
159 typedef struct {
160 const char *class;
161 const char *instance;
162 const char *title;
163 unsigned int tags;
164 Bool isfloating;
165 int monitor;
166 } Rule;
167
168 /* function declarations */
169 static void applyrules(Client *c);
170 static Bool applysizehints(Client *c, int *x, int *y, int *w, int *h, Bool interact);
171 static void arrange(Monitor *m);
172 static void arrangemon(Monitor *m);
173 static void attach(Client *c);
174 static void attachstack(Client *c);
175 static void buttonpress(XEvent *e);
176 static void checkotherwm(void);
177 static void cleanup(void);
178 static void cleanupmon(Monitor *mon);
179 static void clearurgent(Client *c);
180 static void clientmessage(XEvent *e);
181 static void configure(Client *c);
182 static void configurenotify(XEvent *e);
183 static void configurerequest(XEvent *e);
184 static Monitor *createmon(void);
185 static void destroynotify(XEvent *e);
186 static void detach(Client *c);
187 static void detachstack(Client *c);
188 static void die(const char *errstr, ...);
189 static Monitor *dirtomon(int dir);
190 static void drawbar(Monitor *m);
191 static void drawbars(void);
192 static void drawsquare(Bool filled, Bool empty, Bool invert, unsigned long col[ColLast]);
193 static void drawtext(const char *text, unsigned long col[ColLast], Bool invert);
194 static void enternotify(XEvent *e);
195 static void expose(XEvent *e);
196 static void findwindow(const Arg *arg);
197 static void focus(Client *c);
198 static void focusin(XEvent *e);
199 static void focusmon(const Arg *arg);
200 static void focusstack(const Arg *arg);
201 static unsigned long getcolor(const char *colstr, XftColor *color);
202 static Bool getrootptr(int *x, int *y);
203 static long getstate(Window w);
204 static Bool gettextprop(Window w, Atom atom, char *text, unsigned int size);
205 static void grabbuttons(Client *c, Bool focused);
206 static void grabkeys(void);
207 static void incnmaster(const Arg *arg);
208 static void initfont(const char *fontstr);
209 static void keypress(XEvent *e);
210 static void killclient(const Arg *arg);
211 static void list_clients(const Arg *arg);
212 static void listclients(void);
213 static void manage(Window w, XWindowAttributes *wa);
214 static void mappingnotify(XEvent *e);
215 static void maprequest(XEvent *e);
216 static void monocle(Monitor *m);
217 static void motionnotify(XEvent *e);
218 static void movemouse(const Arg *arg);
219 static Client *nexttiled(Client *c);
220 static void pop(Client *);
221 static void propertynotify(XEvent *e);
222 static void quit(const Arg *arg);
223 static Monitor *recttomon(int x, int y, int w, int h);
224 static void resize(Client *c, int x, int y, int w, int h, Bool interact);
225 static void resizeclient(Client *c, int x, int y, int w, int h);
226 static void resizemouse(const Arg *arg);
227 static void restack(Monitor *m);
228 static void run(void);
229 static void scan(void);
230 static Bool sendevent(Client *c, Atom proto);
231 static void sendmon(Client *c, Monitor *m);
232 static void setclientstate(Client *c, long state);
233 static void setfocus(Client *c);
234 static void setfullscreen(Client *c, Bool fullscreen);
235 static void setlayout(const Arg *arg);
236 static void setmfact(const Arg *arg);
237 static void setup(void);
238 static void showhide(Client *c);
239 static void sigchld(int unused);
240 static void spawn(const Arg *arg);
241 static void tag(const Arg *arg);
242 static void tagmon(const Arg *arg);
243 static int textnw(const char *text, unsigned int len);
244 static void tile(Monitor *);
245 static void togglebar(const Arg *arg);
246 static void togglefloating(const Arg *arg);
247 static void toggletag(const Arg *arg);
248 static void toggleview(const Arg *arg);
249 static void unfocus(Client *c, Bool setfocus);
250 static void unmanage(Client *c, Bool destroyed);
251 static void unmapnotify(XEvent *e);
252 static Bool updategeom(void);
253 static void updatebarpos(Monitor *m);
254 static void updatebars(void);
255 static void updatenumlockmask(void);
256 static void updatesizehints(Client *c);
257 static void updatestatus(void);
258 static void updatewindowtype(Client *c);
259 static void updatetitle(Client *c);
260 static void updatewmhints(Client *c);
261 static void view(const Arg *arg);
262 static Client *wintoclient(Window w);
263 static Monitor *wintomon(Window w);
264 static int xerror(Display *dpy, XErrorEvent *ee);
265 static int xerrordummy(Display *dpy, XErrorEvent *ee);
266 static int xerrorstart(Display *dpy, XErrorEvent *ee);
267 static void zoom(const Arg *arg);
268
269 /* variables */
270 static const char broken[] = "broken";
271 static char stext[512];
272 static int screen;
273 static int sw, sh; /* X display screen geometry width, height */
274 static int bh, blw = 0; /* bar geometry */
275 static int (*xerrorxlib)(Display *, XErrorEvent *);
276 static unsigned int numlockmask = 0;
277 static void (*handler[LASTEvent]) (XEvent *) = {
278 [ButtonPress] = buttonpress,
279 [ClientMessage] = clientmessage,
280 [ConfigureRequest] = configurerequest,
281 [ConfigureNotify] = configurenotify,
282 [DestroyNotify] = destroynotify,
283 [EnterNotify] = enternotify,
284 [Expose] = expose,
285 [FocusIn] = focusin,
286 [KeyPress] = keypress,
287 [MappingNotify] = mappingnotify,
288 [MapRequest] = maprequest,
289 [MotionNotify] = motionnotify,
290 [PropertyNotify] = propertynotify,
291 [UnmapNotify] = unmapnotify
292 };
293 static Atom wmatom[WMLast], netatom[NetLast];
294 static Bool running = True;
295 static Cursor cursor[CurLast];
296 static Display *dpy;
297 static DC dc;
298 static Monitor *mons = NULL, *selmon = NULL;
299 static Window root;
300
301 /* configuration, allows nested code to access above variables */
302 #include "config.h"
303
304 /* compile-time check if all tags fit into an unsigned int bit array. */
305 struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; };
306
307
308
309 /* function implementations */
310 void
311 applyrules(Client *c) {
312 const char *class, *instance;
313 unsigned int i;
314 const Rule *r;
315 Monitor *m;
316 XClassHint ch = { NULL, NULL };
317
318 /* rule matching */
319 c->isfloating = c->tags = 0;
320 XGetClassHint(dpy, c->win, &ch);
321 class = ch.res_class ? ch.res_class : broken;
322 instance = ch.res_name ? ch.res_name : broken;
323
324 for(i = 0; i < LENGTH(rules); i++) {
325 r = &rules[i];
326 if((!r->title || strstr(c->name, r->title))
327 && (!r->class || strstr(class, r->class))
328 && (!r->instance || strstr(instance, r->instance)))
329 {
330 c->isfloating = r->isfloating;
331 c->tags |= r->tags;
332 for(m = mons; m && m->num != r->monitor; m = m->next);
333 if(m)
334 c->mon = m;
335 }
336 }
337 if(ch.res_class)
338 XFree(ch.res_class);
339 if(ch.res_name)
340 XFree(ch.res_name);
341 c->tags = c->tags & TAGMASK ? c->tags & TAGMASK : c->mon->tagset[c->mon->seltags];
342 }
343
344 Bool
345 applysizehints(Client *c, int *x, int *y, int *w, int *h, Bool interact) {
346 Bool baseismin;
347 Monitor *m = c->mon;
348
349 /* set minimum possible */
350 *w = MAX(1, *w);
351 *h = MAX(1, *h);
352 if(interact) {
353 if(*x > sw)
354 *x = sw - WIDTH(c);
355 if(*y > sh)
356 *y = sh - HEIGHT(c);
357 if(*x + *w + 2 * c->bw < 0)
358 *x = 0;
359 if(*y + *h + 2 * c->bw < 0)
360 *y = 0;
361 }
362 else {
363 if(*x >= m->wx + m->ww)
364 *x = m->wx + m->ww - WIDTH(c);
365 if(*y >= m->wy + m->wh)
366 *y = m->wy + m->wh - HEIGHT(c);
367 if(*x + *w + 2 * c->bw <= m->wx)
368 *x = m->wx;
369 if(*y + *h + 2 * c->bw <= m->wy)
370 *y = m->wy;
371 }
372 if(*h < bh)
373 *h = bh;
374 if(*w < bh)
375 *w = bh;
376 if(resizehints || c->isfloating || !c->mon->lt[c->mon->sellt]->arrange) {
377 /* see last two sentences in ICCCM 4.1.2.3 */
378 baseismin = c->basew == c->minw && c->baseh == c->minh;
379 if(!baseismin) { /* temporarily remove base dimensions */
380 *w -= c->basew;
381 *h -= c->baseh;
382 }
383 /* adjust for aspect limits */
384 if(c->mina > 0 && c->maxa > 0) {
385 if(c->maxa < (float)*w / *h)
386 *w = *h * c->maxa + 0.5;
387 else if(c->mina < (float)*h / *w)
388 *h = *w * c->mina + 0.5;
389 }
390 if(baseismin) { /* increment calculation requires this */
391 *w -= c->basew;
392 *h -= c->baseh;
393 }
394 /* adjust for increment value */
395 if(c->incw)
396 *w -= *w % c->incw;
397 if(c->inch)
398 *h -= *h % c->inch;
399 /* restore base dimensions */
400 *w = MAX(*w + c->basew, c->minw);
401 *h = MAX(*h + c->baseh, c->minh);
402 if(c->maxw)
403 *w = MIN(*w, c->maxw);
404 if(c->maxh)
405 *h = MIN(*h, c->maxh);
406 }
407 return *x != c->x || *y != c->y || *w != c->w || *h != c->h;
408 }
409
410 void
411 arrange(Monitor *m) {
412 if(m)
413 showhide(m->stack);
414 else for(m = mons; m; m = m->next)
415 showhide(m->stack);
416 if(m)
417 arrangemon(m);
418 else for(m = mons; m; m = m->next)
419 arrangemon(m);
420 }
421
422 void
423 arrangemon(Monitor *m) {
424 strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol);
425 if(m->lt[m->sellt]->arrange)
426 m->lt[m->sellt]->arrange(m);
427 restack(m);
428 }
429
430 void
431 attach(Client *c) {
432 c->next = c->mon->clients;
433 c->mon->clients = c;
434 }
435
436 void
437 attachstack(Client *c) {
438 c->snext = c->mon->stack;
439 c->mon->stack = c;
440 }
441
442 void
443 buttonpress(XEvent *e) {
444 unsigned int i, x, click;
445 Arg arg = {0};
446 Client *c;
447 Monitor *m;
448 XButtonPressedEvent *ev = &e->xbutton;
449
450 click = ClkRootWin;
451 /* focus monitor if necessary */
452 if((m = wintomon(ev->window)) && m != selmon) {
453 unfocus(selmon->sel, True);
454 selmon = m;
455 focus(NULL);
456 }
457 if(ev->window == selmon->barwin) {
458 i = x = 0;
459 do
460 x += TEXTW(tags[i]);
461 while(ev->x >= x && ++i < LENGTH(tags));
462 if(i < LENGTH(tags)) {
463 click = ClkTagBar;
464 arg.ui = 1 << i;
465 }
466 else if(ev->x < x + blw)
467 click = ClkLtSymbol;
468 else if(ev->x > selmon->ww - TEXTW(stext))
469 click = ClkStatusText;
470 else
471 click = ClkWinTitle;
472 }
473 else if((c = wintoclient(ev->window))) {
474 focus(c);
475 click = ClkClientWin;
476 }
477 for(i = 0; i < LENGTH(buttons); i++)
478 if(click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button
479 && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state))
480 buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg);
481 }
482
483 void
484 checkotherwm(void) {
485 xerrorxlib = XSetErrorHandler(xerrorstart);
486 /* this causes an error if some other window manager is running */
487 XSelectInput(dpy, DefaultRootWindow(dpy), SubstructureRedirectMask);
488 XSync(dpy, False);
489 XSetErrorHandler(xerror);
490 XSync(dpy, False);
491 }
492
493 void
494 cleanup(void) {
495 Arg a = {.ui = ~0};
496 Layout foo = { "", NULL };
497 Monitor *m;
498 int i;
499
500 view(&a);
501 selmon->lt[selmon->sellt] = &foo;
502 for(m = mons; m; m = m->next)
503 while(m->stack)
504 unmanage(m->stack, False);
505 XUngrabKey(dpy, AnyKey, AnyModifier, root);
506 XFreePixmap(dpy, dc.drawable);
507 for(i = ColBorder; i < ColLast; i++) {
508 XftColorFree(dpy, DefaultVisual(dpy, screen), DefaultColormap(dpy, screen), dc.xft.norm + i);
509 XftColorFree(dpy, DefaultVisual(dpy, screen), DefaultColormap(dpy, screen), dc.xft.sel + i);
510 }
511 XftDrawDestroy(dc.xft.drawable);
512 g_object_unref(dc.font.layout);
513 XFreeGC(dpy, dc.gc);
514 XFreeCursor(dpy, cursor[CurNormal]);
515 XFreeCursor(dpy, cursor[CurResize]);
516 XFreeCursor(dpy, cursor[CurMove]);
517 while(mons)
518 cleanupmon(mons);
519 XSync(dpy, False);
520 XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
521 }
522
523 void
524 cleanupmon(Monitor *mon) {
525 Monitor *m;
526
527 if(mon == mons)
528 mons = mons->next;
529 else {
530 for(m = mons; m && m->next != mon; m = m->next);
531 m->next = mon->next;
532 }
533 XUnmapWindow(dpy, mon->barwin);
534 XDestroyWindow(dpy, mon->barwin);
535 free(mon);
536 }
537
538 void
539 clearurgent(Client *c) {
540 XWMHints *wmh;
541
542 c->isurgent = False;
543 if(!(wmh = XGetWMHints(dpy, c->win)))
544 return;
545 wmh->flags &= ~XUrgencyHint;
546 XSetWMHints(dpy, c->win, wmh);
547 XFree(wmh);
548 }
549
550 void
551 clientmessage(XEvent *e) {
552 XClientMessageEvent *cme = &e->xclient;
553 Client *c = wintoclient(cme->window);
554
555 if(!c)
556 return;
557 if(cme->message_type == netatom[NetWMState]) {
558 if(cme->data.l[1] == netatom[NetWMFullscreen] || cme->data.l[2] == netatom[NetWMFullscreen])
559 setfullscreen(c, (cme->data.l[0] == 1 /* _NET_WM_STATE_ADD */
560 || (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */ && !c->isfullscreen)));
561 }
562 else if(cme->message_type == netatom[NetActiveWindow]) {
563 if(!ISVISIBLE(c)) {
564 c->mon->seltags ^= 1;
565 c->mon->tagset[c->mon->seltags] = c->tags;
566 }
567 pop(c);
568 }
569 }
570
571 void
572 configure(Client *c) {
573 XConfigureEvent ce;
574
575 ce.type = ConfigureNotify;
576 ce.display = dpy;
577 ce.event = c->win;
578 ce.window = c->win;
579 ce.x = c->x;
580 ce.y = c->y;
581 ce.width = c->w;
582 ce.height = c->h;
583 ce.border_width = c->bw;
584 ce.above = None;
585 ce.override_redirect = False;
586 XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce);
587 }
588
589 void
590 configurenotify(XEvent *e) {
591 Monitor *m;
592 XConfigureEvent *ev = &e->xconfigure;
593 Bool dirty;
594
595 if(ev->window == root) {
596 dirty = (sw != ev->width);
597 sw = ev->width;
598 sh = ev->height;
599 if(updategeom() || dirty) {
600 if(dc.drawable != 0)
601 XFreePixmap(dpy, dc.drawable);
602 dc.drawable = XCreatePixmap(dpy, root, sw, bh, DefaultDepth(dpy, screen));
603 XftDrawChange(dc.xft.drawable, dc.drawable);
604 updatebars();
605 for(m = mons; m; m = m->next)
606 XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh);
607 focus(NULL);
608 arrange(NULL);
609 }
610 }
611 }
612
613 void
614 configurerequest(XEvent *e) {
615 Client *c;
616 Monitor *m;
617 XConfigureRequestEvent *ev = &e->xconfigurerequest;
618 XWindowChanges wc;
619
620 if((c = wintoclient(ev->window))) {
621 if(ev->value_mask & CWBorderWidth)
622 c->bw = ev->border_width;
623 else if(c->isfloating || !selmon->lt[selmon->sellt]->arrange) {
624 m = c->mon;
625 if(ev->value_mask & CWX) {
626 c->oldx = c->x;
627 c->x = m->mx + ev->x;
628 }
629 if(ev->value_mask & CWY) {
630 c->oldy = c->y;
631 c->y = m->my + ev->y;
632 }
633 if(ev->value_mask & CWWidth) {
634 c->oldw = c->w;
635 c->w = ev->width;
636 }
637 if(ev->value_mask & CWHeight) {
638 c->oldh = c->h;
639 c->h = ev->height;
640 }
641 if((c->x + c->w) > m->mx + m->mw && c->isfloating)
642 c->x = m->mx + (m->mw / 2 - WIDTH(c) / 2); /* center in x direction */
643 if((c->y + c->h) > m->my + m->mh && c->isfloating)
644 c->y = m->my + (m->mh / 2 - HEIGHT(c) / 2); /* center in y direction */
645 if((ev->value_mask & (CWX|CWY)) && !(ev->value_mask & (CWWidth|CWHeight)))
646 configure(c);
647 if(ISVISIBLE(c))
648 XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h);
649 }
650 else
651 configure(c);
652 }
653 else {
654 wc.x = ev->x;
655 wc.y = ev->y;
656 wc.width = ev->width;
657 wc.height = ev->height;
658 wc.border_width = ev->border_width;
659 wc.sibling = ev->above;
660 wc.stack_mode = ev->detail;
661 XConfigureWindow(dpy, ev->window, ev->value_mask, &wc);
662 }
663 XSync(dpy, False);
664 }
665
666 Monitor *
667 createmon(void) {
668 Monitor *m;
669
670 if(!(m = (Monitor *)calloc(1, sizeof(Monitor))))
671 die("fatal: could not malloc() %u bytes\n", sizeof(Monitor));
672 m->tagset[0] = m->tagset[1] = 1;
673 m->mfact = mfact;
674 m->nmaster = nmaster;
675 m->showbar = showbar;
676 m->topbar = topbar;
677 m->lt[0] = &layouts[0];
678 m->lt[1] = &layouts[1 % LENGTH(layouts)];
679 strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol);
680 return m;
681 }
682
683 void
684 destroynotify(XEvent *e) {
685 Client *c;
686 XDestroyWindowEvent *ev = &e->xdestroywindow;
687
688 if((c = wintoclient(ev->window)))
689 unmanage(c, True);
690 }
691
692 void
693 detach(Client *c) {
694 Client **tc;
695
696 for(tc = &c->mon->clients; *tc && *tc != c; tc = &(*tc)->next);
697 *tc = c->next;
698 }
699
700 void
701 detachstack(Client *c) {
702 Client **tc, *t;
703
704 for(tc = &c->mon->stack; *tc && *tc != c; tc = &(*tc)->snext);
705 *tc = c->snext;
706
707 if(c == c->mon->sel) {
708 for(t = c->mon->stack; t && !ISVISIBLE(t); t = t->snext);
709 c->mon->sel = t;
710 }
711 }
712
713 void
714 die(const char *errstr, ...) {
715 va_list ap;
716
717 va_start(ap, errstr);
718 vfprintf(stderr, errstr, ap);
719 va_end(ap);
720 exit(EXIT_FAILURE);
721 }
722
723 Monitor *
724 dirtomon(int dir) {
725 Monitor *m = NULL;
726
727 if(dir > 0) {
728 if(!(m = selmon->next))
729 m = mons;
730 }
731 else if(selmon == mons)
732 for(m = mons; m->next; m = m->next);
733 else
734 for(m = mons; m->next != selmon; m = m->next);
735 return m;
736 }
737
738 void
739 drawbar(Monitor *m) {
740 int x;
741 unsigned int i, occ = 0, urg = 0;
742 unsigned long *col;
743 Client *c;
744
745 for(c = m->clients; c; c = c->next) {
746 occ |= c->tags;
747 if(c->isurgent)
748 urg |= c->tags;
749 }
750 dc.x = 0;
751 for(i = 0; i < LENGTH(tags); i++) {
752 dc.w = TEXTW(tags[i]);
753 col = m->tagset[m->seltags] & 1 << i ? dc.sel : dc.norm;
754 drawtext(tags[i], col, urg & 1 << i);
755 drawsquare(m == selmon && selmon->sel && selmon->sel->tags & 1 << i,
756 occ & 1 << i, urg & 1 << i, col);
757 dc.x += dc.w;
758 }
759 dc.w = blw = TEXTW(m->ltsymbol);
760 drawtext(m->ltsymbol, dc.norm, False);
761 dc.x += dc.w;
762 x = dc.x;
763 if(m == selmon) { /* status is only drawn on selected monitor */
764 dc.w = TEXTW(stext);
765 dc.x = m->ww - dc.w;
766 if(dc.x < x) {
767 dc.x = x;
768 dc.w = m->ww - x;
769 }
770 drawtext(stext, dc.norm, False);
771 }
772 else
773 dc.x = m->ww;
774 if((dc.w = dc.x - x) > bh) {
775 dc.x = x;
776 if(m->sel) {
777 col = m == selmon ? dc.sel : dc.norm;
778 drawtext(m->sel->name, col, False);
779 drawsquare(m->sel->isfixed, m->sel->isfloating, False, col);
780 }
781 else
782 drawtext(NULL, dc.norm, False);
783 }
784 XCopyArea(dpy, dc.drawable, m->barwin, dc.gc, 0, 0, m->ww, bh, 0, 0);
785 XSync(dpy, False);
786 }
787
788 void
789 drawbars(void) {
790 Monitor *m;
791
792 for(m = mons; m; m = m->next)
793 drawbar(m);
794 }
795
796 void
797 drawsquare(Bool filled, Bool empty, Bool invert, unsigned long col[ColLast]) {
798 int x;
799
800 XSetForeground(dpy, dc.gc, col[invert ? ColBG : ColFG]);
801 x = (dc.font.ascent + dc.font.descent + 2) / 4;
802 if(filled)
803 XFillRectangle(dpy, dc.drawable, dc.gc, dc.x+1, dc.y+1, x+1, x+1);
804 else if(empty)
805 XDrawRectangle(dpy, dc.drawable, dc.gc, dc.x+1, dc.y+1, x, x);
806 }
807
808 void
809 drawtext(const char *text, unsigned long col[ColLast], Bool invert) {
810 char buf[512];
811 int i, x, y, h, len, olen;
812
813 XSetForeground(dpy, dc.gc, col[invert ? ColFG : ColBG]);
814 XFillRectangle(dpy, dc.drawable, dc.gc, dc.x, dc.y, dc.w, dc.h);
815 if(!text)
816 return;
817 olen = strlen(text);
818 h = dc.font.ascent + dc.font.descent;
819 y = dc.y + (dc.h / 2) - (h / 2);
820 x = dc.x + (h / 2);
821 /* shorten text if necessary (this could wreak havoc with pango markup but fortunately
822 dc.w is adjusted to the width of the status text and not the other way around) */
823 for(len = MIN(olen, sizeof buf); len && textnw(text, len) > dc.w - h; len--);
824 if(!len)
825 return;
826 memcpy(buf, text, len);
827 if(len < olen)
828 for(i = len; i && i > len - 3; buf[--i] = '.');
829 if(text == stext && statusmarkup)
830 pango_layout_set_markup(dc.font.layout, buf, len);
831 else
832 pango_layout_set_text(dc.font.layout, buf, len);
833 pango_xft_render_layout(dc.xft.drawable,
834 (col == dc.norm ? dc.xft.norm : dc.xft.sel) + (invert ? ColBG : ColFG),
835 dc.font.layout, x * PANGO_SCALE, y * PANGO_SCALE);
836 if(text == stext && statusmarkup) /* clear markup attributes */
837 pango_layout_set_attributes(dc.font.layout, NULL);
838 }
839
840 void
841 enternotify(XEvent *e) {
842 Client *c;
843 Monitor *m;
844 XCrossingEvent *ev = &e->xcrossing;
845
846 if((ev->mode != NotifyNormal || ev->detail == NotifyInferior) && ev->window != root)
847 return;
848 c = wintoclient(ev->window);
849 m = c ? c->mon : wintomon(ev->window);
850 if(m != selmon) {
851 unfocus(selmon->sel, True);
852 selmon = m;
853 }
854 else if(!c || c == selmon->sel)
855 return;
856 focus(c);
857 }
858
859 void
860 expose(XEvent *e) {
861 Monitor *m;
862 XExposeEvent *ev = &e->xexpose;
863
864 if(ev->count == 0 && (m = wintomon(ev->window)))
865 drawbar(m);
866 }
867
868 void
869 findwindow (const Arg *arg) {
870 /* Tries to find an emacs instance. If it finds one,
871 it displays it. */
872 Arg a;
873 Monitor *m;
874 Client *c;
875 XClassHint ch = { NULL, NULL};
876 const char *class ;
877 for (m=mons; m; m = m->next)
878 for (c=m->clients; c; c=c->next) {
879 XGetClassHint(dpy, c->win, &ch);
880 class = ch.res_class ? ch.res_class : broken;
881 if (strstr(class,(char*)arg->v)) {
882 a.ui = c->tags;
883 view(&a);
884 unfocus(selmon->sel, True); /* this should focus the right monitor (not tested)*/
885 selmon = m;
886 focus(c);
887 restack(selmon);
888 if(ch.res_class)
889 XFree(ch.res_class);
890 if(ch.res_name)
891 XFree(ch.res_name);
892 return;
893 }
894 }
895 }
896
897 void
898 focus(Client *c) {
899 if(!c || !ISVISIBLE(c))
900 for(c = selmon->stack; c && !ISVISIBLE(c); c = c->snext);
901 /* was if(selmon->sel) */
902 if(selmon->sel && selmon->sel != c)
903 unfocus(selmon->sel, False);
904 if(c) {
905 if(c->mon != selmon)
906 selmon = c->mon;
907 if(c->isurgent)
908 clearurgent(c);
909 detachstack(c);
910 attachstack(c);
911 grabbuttons(c, True);
912 XSetWindowBorder(dpy, c->win, dc.sel[ColBorder]);
913 setfocus(c);
914 }
915 else
916 XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
917 selmon->sel = c;
918 drawbars();
919 }
920
921 void
922 focusin(XEvent *e) { /* there are some broken focus acquiring clients */
923 XFocusChangeEvent *ev = &e->xfocus;
924
925 if(selmon->sel && ev->window != selmon->sel->win)
926 setfocus(selmon->sel);
927 }
928
929 void
930 focusmon(const Arg *arg) {
931 Monitor *m;
932
933 if(!mons->next)
934 return;
935 if((m = dirtomon(arg->i)) == selmon)
936 return;
937 unfocus(selmon->sel, True);
938 selmon = m;
939 focus(NULL);
940 }
941
942 void
943 focusstack(const Arg *arg) {
944 Client *c = NULL, *i;
945
946 if(!selmon->sel)
947 return;
948 if(arg->i > 0) {
949 for(c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next);
950 if(!c)
951 for(c = selmon->clients; c && !ISVISIBLE(c); c = c->next);
952 }
953 else {
954 for(i = selmon->clients; i != selmon->sel; i = i->next)
955 if(ISVISIBLE(i))
956 c = i;
957 if(!c)
958 for(; i; i = i->next)
959 if(ISVISIBLE(i))
960 c = i;
961 }
962 if(c) {
963 focus(c);
964 restack(selmon);
965 }
966 }
967
968 Atom
969 getatomprop(Client *c, Atom prop) {
970 int di;
971 unsigned long dl;
972 unsigned char *p = NULL;
973 Atom da, atom = None;
974
975 if(XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM,
976 &da, &di, &dl, &dl, &p) == Success && p) {
977 atom = *(Atom *)p;
978 XFree(p);
979 }
980 return atom;
981 }
982
983 unsigned long
984 getcolor(const char *colstr, XftColor *color) {
985 Colormap cmap = DefaultColormap(dpy, screen);
986 Visual *vis = DefaultVisual(dpy, screen);
987
988 if(!XftColorAllocName(dpy, vis, cmap, colstr, color))
989 die("error, cannot allocate color '%s'\n", colstr);
990 return color->pixel;
991 }
992
993 Bool
994 getrootptr(int *x, int *y) {
995 int di;
996 unsigned int dui;
997 Window dummy;
998
999 return XQueryPointer(dpy, root, &dummy, &dummy, x, y, &di, &di, &dui);
1000 }
1001
1002 long
1003 getstate(Window w) {
1004 int format;
1005 long result = -1;
1006 unsigned char *p = NULL;
1007 unsigned long n, extra;
1008 Atom real;
1009
1010 if(XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState],
1011 &real, &format, &n, &extra, (unsigned char **)&p) != Success)
1012 return -1;
1013 if(n != 0)
1014 result = *p;
1015 XFree(p);
1016 return result;
1017 }
1018
1019 Bool
1020 gettextprop(Window w, Atom atom, char *text, unsigned int size) {
1021 char **list = NULL;
1022 int n;
1023 XTextProperty name;
1024
1025 if(!text || size == 0)
1026 return False;
1027 text[0] = '\0';
1028 XGetTextProperty(dpy, w, &name, atom);
1029 if(!name.nitems)
1030 return False;
1031 if(name.encoding == XA_STRING)
1032 strncpy(text, (char *)name.value, size - 1);
1033 else {
1034 if(XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success && n > 0 && *list) {
1035 strncpy(text, *list, size - 1);
1036 XFreeStringList(list);
1037 }
1038 }
1039 text[size - 1] = '\0';
1040 XFree(name.value);
1041 return True;
1042 }
1043
1044 void
1045 grabbuttons(Client *c, Bool focused) {
1046 updatenumlockmask();
1047 {
1048 unsigned int i, j;
1049 unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask };
1050 XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
1051 if(focused) {
1052 for(i = 0; i < LENGTH(buttons); i++)
1053 if(buttons[i].click == ClkClientWin)
1054 for(j = 0; j < LENGTH(modifiers); j++)
1055 XGrabButton(dpy, buttons[i].button,
1056 buttons[i].mask | modifiers[j],
1057 c->win, False, BUTTONMASK,
1058 GrabModeAsync, GrabModeSync, None, None);
1059 }
1060 else
1061 XGrabButton(dpy, AnyButton, AnyModifier, c->win, False,
1062 BUTTONMASK, GrabModeAsync, GrabModeSync, None, None);
1063 }
1064 }
1065
1066 void
1067 grabkeys(void) {
1068 updatenumlockmask();
1069 {
1070 unsigned int i, j;
1071 unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask };
1072 KeyCode code;
1073
1074 XUngrabKey(dpy, AnyKey, AnyModifier, root);
1075 for(i = 0; i < LENGTH(keys); i++)
1076 if((code = XKeysymToKeycode(dpy, keys[i].keysym)))
1077 for(j = 0; j < LENGTH(modifiers); j++)
1078 XGrabKey(dpy, code, keys[i].mod | modifiers[j], root,
1079 True, GrabModeAsync, GrabModeAsync);
1080 }
1081 }
1082
1083 void
1084 incnmaster(const Arg *arg) {
1085 selmon->nmaster = MAX(selmon->nmaster + arg->i, 0);
1086 arrange(selmon);
1087 }
1088
1089 void
1090 initfont(const char *fontstr) {
1091 PangoFontMap *fontmap;
1092 PangoContext *context;
1093 PangoFontDescription *desc;
1094 PangoFontMetrics *metrics;
1095
1096 fontmap = pango_xft_get_font_map(dpy, screen);
1097 context = pango_font_map_create_context(fontmap);
1098 desc = pango_font_description_from_string(fontstr);
1099 dc.font.layout = pango_layout_new(context);
1100 pango_layout_set_font_description(dc.font.layout, desc);
1101
1102 metrics = pango_context_get_metrics(context, desc, NULL);
1103 dc.font.ascent = pango_font_metrics_get_ascent(metrics) / PANGO_SCALE;
1104 dc.font.descent = pango_font_metrics_get_descent(metrics) / PANGO_SCALE;
1105 dc.font.height = dc.font.ascent + dc.font.descent;
1106
1107 pango_font_metrics_unref(metrics);
1108 g_object_unref(context);
1109 }
1110
1111 #ifdef XINERAMA
1112 static Bool
1113 isuniquegeom(XineramaScreenInfo *unique, size_t n, XineramaScreenInfo *info) {
1114 while(n--)
1115 if(unique[n].x_org == info->x_org && unique[n].y_org == info->y_org
1116 && unique[n].width == info->width && unique[n].height == info->height)
1117 return False;
1118 return True;
1119 }
1120 #endif /* XINERAMA */
1121
1122 void
1123 keypress(XEvent *e) {
1124 unsigned int i;
1125 KeySym keysym;
1126 XKeyEvent *ev;
1127
1128 ev = &e->xkey;
1129 keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0);
1130 for(i = 0; i < LENGTH(keys); i++)
1131 if(keysym == keys[i].keysym
1132 && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state)
1133 && keys[i].func)
1134 keys[i].func(&(keys[i].arg));
1135 }
1136
1137 void
1138 killclient(const Arg *arg) {
1139 if(!selmon->sel)
1140 return;
1141 if(!sendevent(selmon->sel, wmatom[WMDelete])) {
1142 XGrabServer(dpy);
1143 XSetErrorHandler(xerrordummy);
1144 XSetCloseDownMode(dpy, DestroyAll);
1145 XKillClient(dpy, selmon->sel->win);
1146 XSync(dpy, False);
1147 XSetErrorHandler(xerror);
1148 XUngrabServer(dpy);
1149 }
1150 }
1151
1152 void
1153 list_clients(const Arg *arg) {
1154 /* wrapper function */
1155 listclients();
1156 }
1157 void
1158 listclients (void) {
1159 /* List the names of all clients */
1160 Monitor *m;
1161 FILE *clist;
1162 Client *c;
1163 clist = fopen("/tmp/clients", "w");
1164 for (m=mons; m; m = m->next)
1165 for (c=m->clients; c; c = c->next)
1166 fprintf(clist, "%s %o\n", c->name, c->tags);
1167 fclose(clist);
1168 }
1169
1170 void
1171 manage(Window w, XWindowAttributes *wa) {
1172 Client *c, *t = NULL;
1173 Window trans = None;
1174 XWindowChanges wc;
1175
1176 if(!(c = calloc(1, sizeof(Client))))
1177 die("fatal: could not malloc() %u bytes\n", sizeof(Client));
1178 c->win = w;
1179 updatetitle(c);
1180 if(XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) {
1181 c->mon = t->mon;
1182 c->tags = t->tags;
1183 }
1184 else {
1185 c->mon = selmon;
1186 applyrules(c);
1187 }
1188 /* geometry */
1189 c->x = c->oldx = wa->x;
1190 c->y = c->oldy = wa->y;
1191 c->w = c->oldw = wa->width;
1192 c->h = c->oldh = wa->height;
1193 c->oldbw = wa->border_width;
1194
1195 if(c->x + WIDTH(c) > c->mon->mx + c->mon->mw)
1196 c->x = c->mon->mx + c->mon->mw - WIDTH(c);
1197 if(c->y + HEIGHT(c) > c->mon->my + c->mon->mh)
1198 c->y = c->mon->my + c->mon->mh - HEIGHT(c);
1199 c->x = MAX(c->x, c->mon->mx);
1200 /* only fix client y-offset, if the client center might cover the bar */
1201 c->y = MAX(c->y, ((c->mon->by == c->mon->my) && (c->x + (c->w / 2) >= c->mon->wx)
1202 && (c->x + (c->w / 2) < c->mon->wx + c->mon->ww)) ? bh : c->mon->my);
1203 c->bw = borderpx;
1204
1205 wc.border_width = c->bw;
1206 XConfigureWindow(dpy, w, CWBorderWidth, &wc);
1207 XSetWindowBorder(dpy, w, dc.norm[ColBorder]);
1208 configure(c); /* propagates border_width, if size doesn't change */
1209 updatewindowtype(c);
1210 updatesizehints(c);
1211 updatewmhints(c);
1212 XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask);
1213 grabbuttons(c, False);
1214 if(!c->isfloating)
1215 c->isfloating = c->oldstate = trans != None || c->isfixed;
1216 if(c->isfloating)
1217 XRaiseWindow(dpy, c->win);
1218 attach(c);
1219 attachstack(c);
1220 XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */
1221 setclientstate(c, NormalState);
1222 if (c->mon == selmon)
1223 unfocus(selmon->sel, False);
1224 c->mon->sel = c;
1225 arrange(c->mon);
1226 XMapWindow(dpy, c->win);
1227 focus(NULL);
1228 }
1229
1230 void
1231 mappingnotify(XEvent *e) {
1232 XMappingEvent *ev = &e->xmapping;
1233
1234 XRefreshKeyboardMapping(ev);
1235 if(ev->request == MappingKeyboard)
1236 grabkeys();
1237 }
1238
1239 void
1240 maprequest(XEvent *e) {
1241 static XWindowAttributes wa;
1242 XMapRequestEvent *ev = &e->xmaprequest;
1243
1244 if(!XGetWindowAttributes(dpy, ev->window, &wa))
1245 return;
1246 if(wa.override_redirect)
1247 return;
1248 if(!wintoclient(ev->window))
1249 manage(ev->window, &wa);
1250 }
1251
1252 void
1253 monocle(Monitor *m) {
1254 unsigned int n = 0;
1255 Client *c;
1256
1257 for(c = m->clients; c; c = c->next)
1258 if(ISVISIBLE(c))
1259 n++;
1260 if(n > 0) /* override layout symbol */
1261 snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n);
1262 for(c = nexttiled(m->clients); c; c = nexttiled(c->next))
1263 resize(c, m->wx, m->wy, m->ww - 2 * c->bw, m->wh - 2 * c->bw, False);
1264 }
1265
1266 void
1267 motionnotify(XEvent *e) {
1268 static Monitor *mon = NULL;
1269 Monitor *m;
1270 XMotionEvent *ev = &e->xmotion;
1271
1272 if(ev->window != root)
1273 return;
1274 if((m = recttomon(ev->x_root, ev->y_root, 1, 1)) != mon && mon) {
1275 selmon = m;
1276 focus(NULL);
1277 }
1278 mon = m;
1279 }
1280
1281 void
1282 movemouse(const Arg *arg) {
1283 int x, y, ocx, ocy, nx, ny;
1284 Client *c;
1285 Monitor *m;
1286 XEvent ev;
1287
1288 if(!(c = selmon->sel))
1289 return;
1290 restack(selmon);
1291 ocx = c->x;
1292 ocy = c->y;
1293 if(XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync,
1294 None, cursor[CurMove], CurrentTime) != GrabSuccess)
1295 return;
1296 if(!getrootptr(&x, &y))
1297 return;
1298 do {
1299 XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev);
1300 switch(ev.type) {
1301 case ConfigureRequest:
1302 case Expose:
1303 case MapRequest:
1304 handler[ev.type](&ev);
1305 break;
1306 case MotionNotify:
1307 nx = ocx + (ev.xmotion.x - x);
1308 ny = ocy + (ev.xmotion.y - y);
1309 if(nx >= selmon->wx && nx <= selmon->wx + selmon->ww
1310 && ny >= selmon->wy && ny <= selmon->wy + selmon->wh) {
1311 if(abs(selmon->wx - nx) < snap)
1312 nx = selmon->wx;
1313 else if(abs((selmon->wx + selmon->ww) - (nx + WIDTH(c))) < snap)
1314 nx = selmon->wx + selmon->ww - WIDTH(c);
1315 if(abs(selmon->wy - ny) < snap)
1316 ny = selmon->wy;
1317 else if(abs((selmon->wy + selmon->wh) - (ny + HEIGHT(c))) < snap)
1318 ny = selmon->wy + selmon->wh - HEIGHT(c);
1319 if(!c->isfloating && selmon->lt[selmon->sellt]->arrange
1320 && (abs(nx - c->x) > snap || abs(ny - c->y) > snap))
1321 togglefloating(NULL);
1322 }
1323 if(!selmon->lt[selmon->sellt]->arrange || c->isfloating)
1324 resize(c, nx, ny, c->w, c->h, True);
1325 break;
1326 }
1327 } while(ev.type != ButtonRelease);
1328 XUngrabPointer(dpy, CurrentTime);
1329 if((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) {
1330 sendmon(c, m);
1331 selmon = m;
1332 focus(NULL);
1333 }
1334 }
1335
1336 Client *
1337 nexttiled(Client *c) {
1338 for(; c && (c->isfloating || !ISVISIBLE(c)); c = c->next);
1339 return c;
1340 }
1341
1342 void
1343 pop(Client *c) {
1344 detach(c);
1345 attach(c);
1346 focus(c);
1347 arrange(c->mon);
1348 }
1349
1350 void
1351 propertynotify(XEvent *e) {
1352 Client *c;
1353 Window trans;
1354 XPropertyEvent *ev = &e->xproperty;
1355
1356 if((ev->window == root) && (ev->atom == XA_WM_NAME))
1357 updatestatus();
1358 else if(ev->state == PropertyDelete)
1359 return; /* ignore */
1360 else if((c = wintoclient(ev->window))) {
1361 switch(ev->atom) {
1362 default: break;
1363 case XA_WM_TRANSIENT_FOR:
1364 if(!c->isfloating && (XGetTransientForHint(dpy, c->win, &trans)) &&
1365 (c->isfloating = (wintoclient(trans)) != NULL))
1366 arrange(c->mon);
1367 break;
1368 case XA_WM_NORMAL_HINTS:
1369 updatesizehints(c);
1370 break;
1371 case XA_WM_HINTS:
1372 updatewmhints(c);
1373 drawbars();
1374 break;
1375 }
1376 if(ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) {
1377 updatetitle(c);
1378 if(c == c->mon->sel)
1379 drawbar(c->mon);
1380 }
1381 if(ev->atom == netatom[NetWMWindowType])
1382 updatewindowtype(c);
1383 }
1384 }
1385
1386 void
1387 quit(const Arg *arg) {
1388 running = False;
1389 }
1390
1391 Monitor *
1392 recttomon(int x, int y, int w, int h) {
1393 Monitor *m, *r = selmon;
1394 int a, area = 0;
1395
1396 for(m = mons; m; m = m->next)
1397 if((a = INTERSECT(x, y, w, h, m)) > area) {
1398 area = a;
1399 r = m;
1400 }
1401 return r;
1402 }
1403
1404 void
1405 resize(Client *c, int x, int y, int w, int h, Bool interact) {
1406 if(applysizehints(c, &x, &y, &w, &h, interact))
1407 resizeclient(c, x, y, w, h);
1408 }
1409
1410 void
1411 resizeclient(Client *c, int x, int y, int w, int h) {
1412 XWindowChanges wc;
1413
1414 c->oldx = c->x; c->x = wc.x = x;
1415 c->oldy = c->y; c->y = wc.y = y;
1416 c->oldw = c->w; c->w = wc.width = w;
1417 c->oldh = c->h; c->h = wc.height = h;
1418 wc.border_width = c->bw;
1419 XConfigureWindow(dpy, c->win, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc);
1420 configure(c);
1421 XSync(dpy, False);
1422 }
1423
1424 void
1425 resizemouse(const Arg *arg) {
1426 int ocx, ocy;
1427 int nw, nh;
1428 Client *c;
1429 Monitor *m;
1430 XEvent ev;
1431
1432 if(!(c = selmon->sel))
1433 return;
1434 restack(selmon);
1435 ocx = c->x;
1436 ocy = c->y;
1437 if(XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync,
1438 None, cursor[CurResize], CurrentTime) != GrabSuccess)
1439 return;
1440 XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1);
1441 do {
1442 XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev);
1443 switch(ev.type) {
1444 case ConfigureRequest:
1445 case Expose:
1446 case MapRequest:
1447 handler[ev.type](&ev);
1448 break;
1449 case MotionNotify:
1450 nw = MAX(ev.xmotion.x - ocx - 2 * c->bw + 1, 1);
1451 nh = MAX(ev.xmotion.y - ocy - 2 * c->bw + 1, 1);
1452 if(c->mon->wx + nw >= selmon->wx && c->mon->wx + nw <= selmon->wx + selmon->ww
1453 && c->mon->wy + nh >= selmon->wy && c->mon->wy + nh <= selmon->wy + selmon->wh)
1454 {
1455 if(!c->isfloating && selmon->lt[selmon->sellt]->arrange
1456 && (abs(nw - c->w) > snap || abs(nh - c->h) > snap))
1457 togglefloating(NULL);
1458 }
1459 if(!selmon->lt[selmon->sellt]->arrange || c->isfloating)
1460 resize(c, c->x, c->y, nw, nh, True);
1461 break;
1462 }
1463 } while(ev.type != ButtonRelease);
1464 XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1);
1465 XUngrabPointer(dpy, CurrentTime);
1466 while(XCheckMaskEvent(dpy, EnterWindowMask, &ev));
1467 if((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) {
1468 sendmon(c, m);
1469 selmon = m;
1470 focus(NULL);
1471 }
1472 }
1473
1474 void
1475 restack(Monitor *m) {
1476 Client *c;
1477 XEvent ev;
1478 XWindowChanges wc;
1479
1480 drawbar(m);
1481 if(!m->sel)
1482 return;
1483 if(m->sel->isfloating || !m->lt[m->sellt]->arrange)
1484 XRaiseWindow(dpy, m->sel->win);
1485 if(m->lt[m->sellt]->arrange) {
1486 wc.stack_mode = Below;
1487 wc.sibling = m->barwin;
1488 for(c = m->stack; c; c = c->snext)
1489 if(!c->isfloating && ISVISIBLE(c)) {
1490 XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc);
1491 wc.sibling = c->win;
1492 }
1493 }
1494 XSync(dpy, False);
1495 while(XCheckMaskEvent(dpy, EnterWindowMask, &ev));
1496 }
1497
1498 void
1499 run(void) {
1500 XEvent ev;
1501 /* main event loop */
1502 XSync(dpy, False);
1503 while(running && !XNextEvent(dpy, &ev))
1504 if(handler[ev.type])
1505 handler[ev.type](&ev); /* call handler */
1506 }
1507
1508 void
1509 scan(void) {
1510 unsigned int i, num;
1511 Window d1, d2, *wins = NULL;
1512 XWindowAttributes wa;
1513
1514 if(XQueryTree(dpy, root, &d1, &d2, &wins, &num)) {
1515 for(i = 0; i < num; i++) {
1516 if(!XGetWindowAttributes(dpy, wins[i], &wa)
1517 || wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1))
1518 continue;
1519 if(wa.map_state == IsViewable || getstate(wins[i]) == IconicState)
1520 manage(wins[i], &wa);
1521 }
1522 for(i = 0; i < num; i++) { /* now the transients */
1523 if(!XGetWindowAttributes(dpy, wins[i], &wa))
1524 continue;
1525 if(XGetTransientForHint(dpy, wins[i], &d1)
1526 && (wa.map_state == IsViewable || getstate(wins[i]) == IconicState))
1527 manage(wins[i], &wa);
1528 }
1529 if(wins)
1530 XFree(wins);
1531 }
1532 }
1533
1534 void
1535 sendmon(Client *c, Monitor *m) {
1536 if(c->mon == m)
1537 return;
1538 unfocus(c, True);
1539 detach(c);
1540 detachstack(c);
1541 c->mon = m;
1542 c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */
1543 attach(c);
1544 attachstack(c);
1545 focus(NULL);
1546 arrange(NULL);
1547 }
1548
1549 void
1550 setclientstate(Client *c, long state) {
1551 long data[] = { state, None };
1552
1553 XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32,
1554 PropModeReplace, (unsigned char *)data, 2);
1555 }
1556
1557 Bool
1558 sendevent(Client *c, Atom proto) {
1559 int n;
1560 Atom *protocols;
1561 Bool exists = False;
1562 XEvent ev;
1563
1564 if(XGetWMProtocols(dpy, c->win, &protocols, &n)) {
1565 while(!exists && n--)
1566 exists = protocols[n] == proto;
1567 XFree(protocols);
1568 }
1569 if(exists) {
1570 ev.type = ClientMessage;
1571 ev.xclient.window = c->win;
1572 ev.xclient.message_type = wmatom[WMProtocols];
1573 ev.xclient.format = 32;
1574 ev.xclient.data.l[0] = proto;
1575 ev.xclient.data.l[1] = CurrentTime;
1576 XSendEvent(dpy, c->win, False, NoEventMask, &ev);
1577 }
1578 return exists;
1579 }
1580
1581 void
1582 setfocus(Client *c) {
1583 if(!c->neverfocus)
1584 XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime);
1585 sendevent(c, wmatom[WMTakeFocus]);
1586 }
1587
1588 void
1589 setfullscreen(Client *c, Bool fullscreen) {
1590 if(fullscreen) {
1591 XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32,
1592 PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1);
1593 c->isfullscreen = True;
1594 c->oldstate = c->isfloating;
1595 c->oldbw = c->bw;
1596 c->bw = 0;
1597 c->isfloating = True;
1598 resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh);
1599 XRaiseWindow(dpy, c->win);
1600 }
1601 else {
1602 XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32,
1603 PropModeReplace, (unsigned char*)0, 0);
1604 c->isfullscreen = False;
1605 c->isfloating = c->oldstate;
1606 c->bw = c->oldbw;
1607 c->x = c->oldx;
1608 c->y = c->oldy;
1609 c->w = c->oldw;
1610 c->h = c->oldh;
1611 resizeclient(c, c->x, c->y, c->w, c->h);
1612 arrange(c->mon);
1613 }
1614 }
1615
1616 void
1617 setlayout(const Arg *arg) {
1618 if(!arg || !arg->v || arg->v != selmon->lt[selmon->sellt])
1619 selmon->sellt ^= 1;
1620 if(arg && arg->v)
1621 selmon->lt[selmon->sellt] = (Layout *)arg->v;
1622 strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol);
1623 if(selmon->sel)
1624 arrange(selmon);
1625 else
1626 drawbar(selmon);
1627 }
1628
1629 /* arg > 1.0 will set mfact absolutly */
1630 void
1631 setmfact(const Arg *arg) {
1632 float f;
1633
1634 if(!arg || !selmon->lt[selmon->sellt]->arrange)
1635 return;
1636 f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0;
1637 if(f < 0.1 || f > 0.9)
1638 return;
1639 selmon->mfact = f;
1640 arrange(selmon);
1641 }
1642
1643 void
1644 setup(void) {
1645 XSetWindowAttributes wa;
1646
1647 /* clean up any zombies immediately */
1648 sigchld(0);
1649
1650 /* init screen */
1651 screen = DefaultScreen(dpy);
1652 root = RootWindow(dpy, screen);
1653 initfont(font);
1654 sw = DisplayWidth(dpy, screen);
1655 sh = DisplayHeight(dpy, screen);
1656 bh = dc.h = dc.font.height + 2;
1657 updategeom();
1658 /* init atoms */
1659 wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False);
1660 wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
1661 wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False);
1662 wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False);
1663 netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False);
1664 netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False);
1665 netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False);
1666 netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False);
1667 netatom[NetWMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
1668 netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);
1669 netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False);
1670 /* init cursors */
1671 cursor[CurNormal] = XCreateFontCursor(dpy, XC_left_ptr);
1672 cursor[CurResize] = XCreateFontCursor(dpy, XC_sizing);
1673 cursor[CurMove] = XCreateFontCursor(dpy, XC_fleur);
1674 /* init appearance */
1675 dc.norm[ColBorder] = getcolor(normbordercolor, dc.xft.norm + ColBorder);
1676 dc.norm[ColBG] = getcolor(normbgcolor, dc.xft.norm + ColBG);
1677 dc.norm[ColFG] = getcolor(normfgcolor, dc.xft.norm + ColFG);
1678 dc.sel[ColBorder] = getcolor(selbordercolor, dc.xft.sel + ColBorder);
1679 dc.sel[ColBG] = getcolor(selbgcolor, dc.xft.sel + ColBG);
1680 dc.sel[ColFG] = getcolor(selfgcolor, dc.xft.sel + ColFG);
1681 dc.drawable = XCreatePixmap(dpy, root, DisplayWidth(dpy, screen), bh, DefaultDepth(dpy, screen));
1682 dc.xft.drawable = XftDrawCreate(dpy, dc.drawable, DefaultVisual(dpy, screen), DefaultColormap(dpy, screen));
1683 dc.gc = XCreateGC(dpy, root, 0, NULL);
1684 XSetLineAttributes(dpy, dc.gc, 1, LineSolid, CapButt, JoinMiter);
1685 /* init bars */
1686 updatebars();
1687 updatestatus();
1688 /* EWMH support per view */
1689 XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32,
1690 PropModeReplace, (unsigned char *) netatom, NetLast);
1691 /* select for events */
1692 wa.cursor = cursor[CurNormal];
1693 wa.event_mask = SubstructureRedirectMask|SubstructureNotifyMask|ButtonPressMask|PointerMotionMask
1694 |EnterWindowMask|LeaveWindowMask|StructureNotifyMask|PropertyChangeMask;
1695 XChangeWindowAttributes(dpy, root, CWEventMask|CWCursor, &wa);
1696 XSelectInput(dpy, root, wa.event_mask);
1697 grabkeys();
1698 }
1699
1700 void
1701 showhide(Client *c) {
1702 if(!c)
1703 return;
1704 if(ISVISIBLE(c)) { /* show clients top down */
1705 XMoveWindow(dpy, c->win, c->x, c->y);
1706 if((!c->mon->lt[c->mon->sellt]->arrange || c->isfloating) && !c->isfullscreen)
1707 resize(c, c->x, c->y, c->w, c->h, False);
1708 showhide(c->snext);
1709 }
1710 else { /* hide clients bottom up */
1711 showhide(c->snext);
1712 XMoveWindow(dpy, c->win, WIDTH(c) * -2, c->y);
1713 }
1714 }
1715
1716 void
1717 sigchld(int unused) {
1718 if(signal(SIGCHLD, sigchld) == SIG_ERR)
1719 die("Can't install SIGCHLD handler");
1720 while(0 < waitpid(-1, NULL, WNOHANG));
1721 }
1722
1723 void
1724 spawn(const Arg *arg) {
1725 if(fork() == 0) {
1726 if(dpy)
1727 close(ConnectionNumber(dpy));
1728 setsid();
1729 execvp(((char **)arg->v)[0], (char **)arg->v);
1730 fprintf(stderr, "dwm: execvp %s", ((char **)arg->v)[0]);
1731 perror(" failed");
1732 exit(EXIT_SUCCESS);
1733 }
1734 }
1735
1736 void
1737 tag(const Arg *arg) {
1738 if(selmon->sel && arg->ui & TAGMASK) {
1739 selmon->sel->tags = arg->ui & TAGMASK;
1740 focus(NULL);
1741 arrange(selmon);
1742 }
1743 }
1744
1745 void
1746 tagmon(const Arg *arg) {
1747 if(!selmon->sel || !mons->next)
1748 return;
1749 sendmon(selmon->sel, dirtomon(arg->i));
1750 }
1751
1752 int
1753 textnw(const char *text, unsigned int len) {
1754 PangoRectangle r;
1755 if(text == stext && statusmarkup)
1756 pango_layout_set_markup(dc.font.layout, text, len);
1757 else
1758 pango_layout_set_text(dc.font.layout, text, len);
1759 pango_layout_get_extents(dc.font.layout, 0, &r);
1760 if(text == stext && statusmarkup) /* clear markup attributes */
1761 pango_layout_set_attributes(dc.font.layout, NULL);
1762 return r.width / PANGO_SCALE;
1763 }
1764
1765 void
1766 tile(Monitor *m) {
1767 unsigned int i, n, h, mw, my, ty;
1768 Client *c;
1769
1770 for(n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++);
1771 if(n == 0)
1772 return;
1773
1774 if(n > m->nmaster)
1775 mw = m->nmaster ? m->ww * m->mfact : 0;
1776 else
1777 mw = m->ww;
1778 for(i = my = ty = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++)
1779 if(i < m->nmaster) {
1780 h = (m->wh - my) / (MIN(n, m->nmaster) - i);
1781 resize(c, m->wx, m->wy + my, mw - (2*c->bw), h - (2*c->bw), False);
1782 my += HEIGHT(c);
1783 }
1784 else {
1785 h = (m->wh - ty) / (n - i);
1786 resize(c, m->wx + mw, m->wy + ty, m->ww - mw - (2*c->bw), h - (2*c->bw), False);
1787 ty += HEIGHT(c);
1788 }
1789 }
1790
1791 void
1792 togglebar(const Arg *arg) {
1793 selmon->showbar = !selmon->showbar;
1794 updatebarpos(selmon);
1795 XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh);
1796 arrange(selmon);
1797 }
1798
1799 void
1800 togglefloating(const Arg *arg) {
1801 if(!selmon->sel)
1802 return;
1803 selmon->sel->isfloating = !selmon->sel->isfloating || selmon->sel->isfixed;
1804 if(selmon->sel->isfloating)
1805 resize(selmon->sel, selmon->sel->x, selmon->sel->y,
1806 selmon->sel->w, selmon->sel->h, False);
1807 arrange(selmon);
1808 }
1809
1810 void
1811 toggletag(const Arg *arg) {
1812 unsigned int newtags;
1813
1814 if(!selmon->sel)
1815 return;
1816 newtags = selmon->sel->tags ^ (arg->ui & TAGMASK);
1817 if(newtags) {
1818 selmon->sel->tags = newtags;
1819 focus(NULL);
1820 arrange(selmon);
1821 }
1822 }
1823
1824 void
1825 toggleview(const Arg *arg) {
1826 unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK);
1827
1828 if(newtagset) {
1829 selmon->tagset[selmon->seltags] = newtagset;
1830 focus(NULL);
1831 arrange(selmon);
1832 }
1833 }
1834
1835 void
1836 unfocus(Client *c, Bool setfocus) {
1837 if(!c)
1838 return;
1839 grabbuttons(c, False);
1840 XSetWindowBorder(dpy, c->win, dc.norm[ColBorder]);
1841 if(setfocus)
1842 XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
1843 }
1844
1845 void
1846 unmanage(Client *c, Bool destroyed) {
1847 Monitor *m = c->mon;
1848 XWindowChanges wc;
1849
1850 /* The server grab construct avoids race conditions. */
1851 detach(c);
1852 detachstack(c);
1853 if(!destroyed) {
1854 wc.border_width = c->oldbw;
1855 XGrabServer(dpy);
1856 XSetErrorHandler(xerrordummy);
1857 XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */
1858 XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
1859 setclientstate(c, WithdrawnState);
1860 XSync(dpy, False);
1861 XSetErrorHandler(xerror);
1862 XUngrabServer(dpy);
1863 }
1864 free(c);
1865 focus(NULL);
1866 arrange(m);
1867 }
1868
1869 void
1870 unmapnotify(XEvent *e) {
1871 Client *c;
1872 XUnmapEvent *ev = &e->xunmap;
1873
1874 if((c = wintoclient(ev->window))) {
1875 if(ev->send_event)
1876 setclientstate(c, WithdrawnState);
1877 else
1878 unmanage(c, False);
1879 }
1880 }
1881
1882 void
1883 updatebars(void) {
1884 Monitor *m;
1885 XSetWindowAttributes wa = {
1886 .override_redirect = True,
1887 .background_pixmap = ParentRelative,
1888 .event_mask = ButtonPressMask|ExposureMask
1889 };
1890 for(m = mons; m; m = m->next) {
1891 m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen),
1892 CopyFromParent, DefaultVisual(dpy, screen),
1893 CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa);
1894 XDefineCursor(dpy, m->barwin, cursor[CurNormal]);
1895 XMapRaised(dpy, m->barwin);
1896 }
1897 }
1898
1899 void
1900 updatebarpos(Monitor *m) {
1901 m->wy = m->my;
1902 m->wh = m->mh;
1903 if(m->showbar) {
1904 m->wh -= bh;
1905 m->by = m->topbar ? m->wy : m->wy + m->wh;
1906 m->wy = m->topbar ? m->wy + bh : m->wy;
1907 }
1908 else
1909 m->by = -bh;
1910 }
1911
1912 Bool
1913 updategeom(void) {
1914 Bool dirty = False;
1915
1916 #ifdef XINERAMA
1917 if(XineramaIsActive(dpy)) {
1918 int i, j, n, nn;
1919 Client *c;
1920 Monitor *m;
1921 XineramaScreenInfo *info = XineramaQueryScreens(dpy, &nn);
1922 XineramaScreenInfo *unique = NULL;
1923
1924 for(n = 0, m = mons; m; m = m->next, n++);
1925 /* only consider unique geometries as separate screens */
1926 if(!(unique = (XineramaScreenInfo *)malloc(sizeof(XineramaScreenInfo) * nn)))
1927 die("fatal: could not malloc() %u bytes\n", sizeof(XineramaScreenInfo) * nn);
1928 for(i = 0, j = 0; i < nn; i++)
1929 if(isuniquegeom(unique, j, &info[i]))
1930 memcpy(&unique[j++], &info[i], sizeof(XineramaScreenInfo));
1931 XFree(info);
1932 nn = j;
1933 if(n <= nn) {
1934 for(i = 0; i < (nn - n); i++) { /* new monitors available */
1935 for(m = mons; m && m->next; m = m->next);
1936 if(m)
1937 m->next = createmon();
1938 else
1939 mons = createmon();
1940 }
1941 for(i = 0, m = mons; i < nn && m; m = m->next, i++)
1942 if(i >= n
1943 || (unique[i].x_org != m->mx || unique[i].y_org != m->my
1944 || unique[i].width != m->mw || unique[i].height != m->mh))
1945 {
1946 dirty = True;
1947 m->num = i;
1948 m->mx = m->wx = unique[i].x_org;
1949 m->my = m->wy = unique[i].y_org;
1950 m->mw = m->ww = unique[i].width;
1951 m->mh = m->wh = unique[i].height;
1952 updatebarpos(m);
1953 }
1954 }
1955 else { /* less monitors available nn < n */
1956 for(i = nn; i < n; i++) {
1957 for(m = mons; m && m->next; m = m->next);
1958 while(m->clients) {
1959 dirty = True;
1960 c = m->clients;
1961 m->clients = c->next;
1962 detachstack(c);
1963 c->mon = mons;
1964 attach(c);
1965 attachstack(c);
1966 }
1967 if(m == selmon)
1968 selmon = mons;
1969 cleanupmon(m);
1970 }
1971 }
1972 free(unique);
1973 }
1974 else
1975 #endif /* XINERAMA */
1976 /* default monitor setup */
1977 {
1978 if(!mons)
1979 mons = createmon();
1980 if(mons->mw != sw || mons->mh != sh) {
1981 dirty = True;
1982 mons->mw = mons->ww = sw;
1983 mons->mh = mons->wh = sh;
1984 updatebarpos(mons);
1985 }
1986 }
1987 if(dirty) {
1988 selmon = mons;
1989 selmon = wintomon(root);
1990 }
1991 return dirty;
1992 }
1993
1994 void
1995 updatenumlockmask(void) {
1996 unsigned int i, j;
1997 XModifierKeymap *modmap;
1998
1999 numlockmask = 0;
2000 modmap = XGetModifierMapping(dpy);
2001 for(i = 0; i < 8; i++)
2002 for(j = 0; j < modmap->max_keypermod; j++)
2003 if(modmap->modifiermap[i * modmap->max_keypermod + j]
2004 == XKeysymToKeycode(dpy, XK_Num_Lock))
2005 numlockmask = (1 << i);
2006 XFreeModifiermap(modmap);
2007 }
2008
2009 void
2010 updatesizehints(Client *c) {
2011 long msize;
2012 XSizeHints size;
2013
2014 if(!XGetWMNormalHints(dpy, c->win, &size, &msize))
2015 /* size is uninitialized, ensure that size.flags aren't used */
2016 size.flags = PSize;
2017 if(size.flags & PBaseSize) {
2018 c->basew = size.base_width;
2019 c->baseh = size.base_height;
2020 }
2021 else if(size.flags & PMinSize) {
2022 c->basew = size.min_width;
2023 c->baseh = size.min_height;
2024 }
2025 else
2026 c->basew = c->baseh = 0;
2027 if(size.flags & PResizeInc) {
2028 c->incw = size.width_inc;
2029 c->inch = size.height_inc;
2030 }
2031 else
2032 c->incw = c->inch = 0;
2033 if(size.flags & PMaxSize) {
2034 c->maxw = size.max_width;
2035 c->maxh = size.max_height;
2036 }
2037 else
2038 c->maxw = c->maxh = 0;
2039 if(size.flags & PMinSize) {
2040 c->minw = size.min_width;
2041 c->minh = size.min_height;
2042 }
2043 else if(size.flags & PBaseSize) {
2044 c->minw = size.base_width;
2045 c->minh = size.base_height;
2046 }
2047 else
2048 c->minw = c->minh = 0;
2049 if(size.flags & PAspect) {
2050 c->mina = (float)size.min_aspect.y / size.min_aspect.x;
2051 c->maxa = (float)size.max_aspect.x / size.max_aspect.y;
2052 }
2053 else
2054 c->maxa = c->mina = 0.0;
2055 c->isfixed = (c->maxw && c->minw && c->maxh && c->minh
2056 && c->maxw == c->minw && c->maxh == c->minh);
2057 }
2058
2059 void
2060 updatetitle(Client *c) {
2061 if(!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name))
2062 gettextprop(c->win, XA_WM_NAME, c->name, sizeof c->name);
2063 if(c->name[0] == '\0') /* hack to mark broken clients */
2064 strcpy(c->name, broken);
2065 }
2066
2067 void
2068 updatestatus(void) {
2069 if(!gettextprop(root, XA_WM_NAME, stext, sizeof(stext)))
2070 strcpy(stext, "dwm-"VERSION);
2071 drawbar(selmon);
2072 }
2073
2074 void
2075 updatewindowtype(Client *c) {
2076 Atom state = getatomprop(c, netatom[NetWMState]);
2077 Atom wtype = getatomprop(c, netatom[NetWMWindowType]);
2078
2079 if(state == netatom[NetWMFullscreen])
2080 setfullscreen(c, True);
2081
2082 if(wtype == netatom[NetWMWindowTypeDialog])
2083 c->isfloating = True;
2084 }
2085
2086 void
2087 updatewmhints(Client *c) {
2088 XWMHints *wmh;
2089
2090 if((wmh = XGetWMHints(dpy, c->win))) {
2091 if(c == selmon->sel && wmh->flags & XUrgencyHint) {
2092 wmh->flags &= ~XUrgencyHint;
2093 XSetWMHints(dpy, c->win, wmh);
2094 }
2095 else
2096 c->isurgent = (wmh->flags & XUrgencyHint) ? True : False;
2097 if(wmh->flags & InputHint)
2098 c->neverfocus = !wmh->input;
2099 else
2100 c->neverfocus = False;
2101 XFree(wmh);
2102 }
2103 }
2104
2105 void
2106 view(const Arg *arg) {
2107 if((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags])
2108 return;
2109 selmon->seltags ^= 1; /* toggle sel tagset */
2110 if(arg->ui & TAGMASK)
2111 selmon->tagset[selmon->seltags] = arg->ui & TAGMASK;
2112 focus(NULL);
2113 arrange(selmon);
2114 }
2115
2116 Client *
2117 wintoclient(Window w) {
2118 Client *c;
2119 Monitor *m;
2120
2121 for(m = mons; m; m = m->next)
2122 for(c = m->clients; c; c = c->next)
2123 if(c->win == w)
2124 return c;
2125 return NULL;
2126 }
2127
2128 Monitor *
2129 wintomon(Window w) {
2130 int x, y;
2131 Client *c;
2132 Monitor *m;
2133
2134 if(w == root && getrootptr(&x, &y))
2135 return recttomon(x, y, 1, 1);
2136 for(m = mons; m; m = m->next)
2137 if(w == m->barwin)
2138 return m;
2139 if((c = wintoclient(w)))
2140 return c->mon;
2141 return selmon;
2142 }
2143
2144 /* There's no way to check accesses to destroyed windows, thus those cases are
2145 * ignored (especially on UnmapNotify's). Other types of errors call Xlibs
2146 * default error handler, which may call exit. */
2147 int
2148 xerror(Display *dpy, XErrorEvent *ee) {
2149 if(ee->error_code == BadWindow
2150 || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch)
2151 || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable)
2152 || (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable)
2153 || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable)
2154 || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch)
2155 || (ee->request_code == X_GrabButton && ee->error_code == BadAccess)
2156 || (ee->request_code == X_GrabKey && ee->error_code == BadAccess)
2157 || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable))
2158 return 0;
2159 fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n",
2160 ee->request_code, ee->error_code);
2161 return xerrorxlib(dpy, ee); /* may call exit */
2162 }
2163
2164 int
2165 xerrordummy(Display *dpy, XErrorEvent *ee) {
2166 return 0;
2167 }
2168
2169 /* Startup Error handler to check if another window manager
2170 * is already running. */
2171 int
2172 xerrorstart(Display *dpy, XErrorEvent *ee) {
2173 die("dwm: another window manager is already running\n");
2174 return -1;
2175 }
2176
2177 void
2178 zoom(const Arg *arg) {
2179 Client *c = selmon->sel;
2180
2181 if(!selmon->lt[selmon->sellt]->arrange
2182 || (selmon->sel && selmon->sel->isfloating))
2183 return;
2184 if(c == nexttiled(selmon->clients))
2185 if(!c || !(c = nexttiled(c->next)))
2186 return;
2187 pop(c);
2188 }
2189
2190 int
2191 main(int argc, char *argv[]) {
2192 if(argc == 2 && !strcmp("-v", argv[1]))
2193 die("dwm-"VERSION", © 2006-2011 dwm engineers, see LICENSE for details\n");
2194 else if(argc != 1)
2195 die("usage: dwm [-v]\n");
2196 if(!setlocale(LC_CTYPE, "") || !XSupportsLocale())
2197 fputs("warning: no locale support\n", stderr);
2198 if(!(dpy = XOpenDisplay(NULL)))
2199 die("dwm: cannot open display\n");
2200 checkotherwm();
2201 setup();
2202 scan();
2203 run();
2204 cleanup();
2205 XCloseDisplay(dpy);
2206 return EXIT_SUCCESS;
2207 }