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