aboutsummaryrefslogtreecommitdiffstats
path: root/dwm.c
diff options
context:
space:
mode:
authorphilw <dscr@duck.com>2025-04-15 01:41:05 +0200
committerphilw <dscr@duck.com>2025-04-15 01:41:05 +0200
commit8146021a2ea4130cfb0d2bf846b451ec538b1bb6 (patch)
tree3a946ef5ee4769bf4eae89b4e77858cc67b8060b /dwm.c
parent515b7f3b4f048b29325e3e38f0f4a2ef898e8daa (diff)
downloaddwm-8146021a2ea4130cfb0d2bf846b451ec538b1bb6.tar.gz
dwm-8146021a2ea4130cfb0d2bf846b451ec538b1bb6.zip
Refactor the project
Refactored core logic to improve readability and maintainability. Documented the 'transient.c' file to provide clear explanations of its purpose and functions. Updated the 'util.c' file with necessary comments and improvements to existing code. Documented the 'util.h' header file to clarify function prototypes and usage. This update should improve the overall code quality and make it easier for future development. Signed-off-by: philw <dscr@duck.com>
Diffstat (limited to '')
-rw-r--r--dwm.c2903
-rw-r--r--dwm.c~2884
2 files changed, 0 insertions, 5787 deletions
diff --git a/dwm.c b/dwm.c
deleted file mode 100644
index 22f228a..0000000
--- a/dwm.c
+++ /dev/null
@@ -1,2903 +0,0 @@
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 <signal.h>
26#include <stdarg.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <unistd.h>
31#include <sys/types.h>
32#include <sys/wait.h>
33#include <X11/cursorfont.h>
34#include <X11/keysym.h>
35#include <X11/Xatom.h>
36#include <X11/Xlib.h>
37#include <X11/Xproto.h>
38#include <X11/Xutil.h>
39#ifdef XINERAMA
40#include <X11/extensions/Xinerama.h>
41#endif /* XINERAMA */
42#include <X11/Xft/Xft.h>
43#include <X11/Xlib-xcb.h>
44#include <xcb/res.h>
45#ifdef __OpenBSD__
46#include <sys/sysctl.h>
47#include <kvm.h>
48#endif /* __OpenBSD */
49
50#include "drw.h"
51#include "util.h"
52#include <X11/XF86keysym.h>
53
54/* macros */
55#define BUTTONMASK (ButtonPressMask|ButtonReleaseMask)
56#define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask))
57#define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \
58 * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy)))
59#define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags]))
60#define LENGTH(X) (sizeof X / sizeof X[0])
61#define MOUSEMASK (BUTTONMASK|PointerMotionMask)
62#define WIDTH(X) ((X)->w + 2 * (X)->bw)
63#define HEIGHT(X) ((X)->h + 2 * (X)->bw)
64#define TAGMASK ((1 << LENGTH(tags)) - 1)
65#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad)
66
67#define SYSTEM_TRAY_REQUEST_DOCK 0
68/* XEMBED messages */
69#define XEMBED_EMBEDDED_NOTIFY 0
70#define XEMBED_WINDOW_ACTIVATE 1
71#define XEMBED_FOCUS_IN 4
72#define XEMBED_MODALITY_ON 10
73#define XEMBED_MAPPED (1 << 0)
74#define XEMBED_WINDOW_ACTIVATE 1
75#define XEMBED_WINDOW_DEACTIVATE 2
76#define VERSION_MAJOR 0
77#define VERSION_MINOR 0
78#define XEMBED_EMBEDDED_VERSION (VERSION_MAJOR << 16) | VERSION_MINOR
79
80/* enums */
81enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */
82enum { SchemeNorm, SchemeSel }; /* color schemes */
83enum { NetSupported, NetWMName, NetWMState, NetWMCheck,
84 NetSystemTray, NetSystemTrayOP, NetSystemTrayOrientation,
85 NetSystemTrayOrientationHorz,
86 NetWMFullscreen, NetActiveWindow, NetWMWindowType,
87 NetWMWindowTypeDialog, NetClientList, NetLast
88}; /* EWMH atoms */
89enum { Manager, Xembed, XembedInfo, XLast }; /* Xembed atoms */
90enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */
91enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle,
92 ClkClientWin, ClkRootWin, ClkLast
93}; /* clicks */
94
95typedef union {
96 int i;
97 unsigned int ui;
98 float f;
99 const void *v;
100} Arg;
101
102typedef struct {
103 unsigned int click;
104 unsigned int mask;
105 unsigned int button;
106 void (*func)(const Arg * arg);
107 const Arg arg;
108} Button;
109
110typedef struct Monitor Monitor;
111typedef struct Client Client;
112struct Client {
113 char name[256];
114 float mina, maxa;
115 int x, y, w, h;
116 int oldx, oldy, oldw, oldh;
117 int basew, baseh, incw, inch, maxw, maxh, minw, minh, hintsvalid;
118 int bw, oldbw;
119 unsigned int tags;
120 int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen,
121 isterminal, noswallow;
122 pid_t pid;
123 Client *next;
124 Client *snext;
125 Client *swallowing;
126 Monitor *mon;
127 Window win;
128};
129
130typedef struct {
131 unsigned int mod;
132 KeySym keysym;
133 void (*func)(const Arg *);
134 const Arg arg;
135} Key;
136
137typedef struct {
138 const char *symbol;
139 void (*arrange)(Monitor *);
140} Layout;
141
142struct Monitor {
143 char ltsymbol[16];
144 float mfact;
145 int nmaster;
146 int num;
147 int by; /* bar geometry */
148 int mx, my, mw, mh; /* screen size */
149 int wx, wy, ww, wh; /* window area */
150 int gappih; /* horizontal gap between windows */
151 int gappiv; /* vertical gap between windows */
152 int gappoh; /* horizontal outer gaps */
153 int gappov; /* vertical outer gaps */
154 unsigned int seltags;
155 unsigned int sellt;
156 unsigned int tagset[2];
157 int showbar;
158 int topbar;
159 Client *clients;
160 Client *sel;
161 Client *stack;
162 Monitor *next;
163 Window barwin;
164 const Layout *lt[2];
165};
166
167typedef struct {
168 const char *class;
169 const char *instance;
170 const char *title;
171 unsigned int tags;
172 int isfloating;
173 int isterminal;
174 int noswallow;
175 int monitor;
176} Rule;
177
178typedef struct Systray Systray;
179struct Systray {
180 Window win;
181 Client *icons;
182};
183
184/* function declarations */
185static void applyrules(Client * c);
186static int applysizehints(Client * c, int *x, int *y, int *w, int *h,
187 int interact);
188static void arrange(Monitor * m);
189static void arrangemon(Monitor * m);
190static void attach(Client * c);
191static void attachstack(Client * c);
192static void buttonpress(XEvent * e);
193static void checkotherwm(void);
194static void cleanup(void);
195static void cleanupmon(Monitor * mon);
196static void clientmessage(XEvent * e);
197static void configure(Client * c);
198static void configurenotify(XEvent * e);
199static void configurerequest(XEvent * e);
200static Monitor *createmon(void);
201static void destroynotify(XEvent * e);
202static void detach(Client * c);
203static void detachstack(Client * c);
204static Monitor *dirtomon(int dir);
205static void drawbar(Monitor * m);
206static void drawbars(void);
207static void enternotify(XEvent * e);
208static void expose(XEvent * e);
209static void focus(Client * c);
210static void focusin(XEvent * e);
211static void focusmon(const Arg * arg);
212static void focusstack(const Arg * arg);
213static Atom getatomprop(Client * c, Atom prop);
214static int getrootptr(int *x, int *y);
215static long getstate(Window w);
216static unsigned int getsystraywidth();
217static int gettextprop(Window w, Atom atom, char *text, unsigned int size);
218static void grabbuttons(Client * c, int focused);
219static void grabkeys(void);
220static void incnmaster(const Arg * arg);
221static void keypress(XEvent * e);
222static void killclient(const Arg * arg);
223static void manage(Window w, XWindowAttributes * wa);
224static void mappingnotify(XEvent * e);
225static void maprequest(XEvent * e);
226static void monocle(Monitor * m);
227static void motionnotify(XEvent * e);
228static void movemouse(const Arg * arg);
229static Client *nexttiled(Client * c);
230static void pop(Client * c);
231static void propertynotify(XEvent * e);
232static void quit(const Arg * arg);
233static Monitor *recttomon(int x, int y, int w, int h);
234static void removesystrayicon(Client * i);
235static void resize(Client * c, int x, int y, int w, int h, int interact);
236static void resizebarwin(Monitor * m);
237static void resizeclient(Client * c, int x, int y, int w, int h);
238static void resizemouse(const Arg * arg);
239static void resizerequest(XEvent * e);
240static void restack(Monitor * m);
241static void run(void);
242static void scan(void);
243static int sendevent(Window w, Atom proto, int m, long d0, long d1, long d2,
244 long d3, long d4);
245static void sendmon(Client * c, Monitor * m);
246static void setclientstate(Client * c, long state);
247static void setfocus(Client * c);
248static void setfullscreen(Client * c, int fullscreen);
249static void setgaps(int oh, int ov, int ih, int iv);
250static void incrgaps(const Arg * arg);
251static void incrigaps(const Arg * arg);
252static void incrogaps(const Arg * arg);
253static void incrohgaps(const Arg * arg);
254static void incrovgaps(const Arg * arg);
255static void incrihgaps(const Arg * arg);
256static void incrivgaps(const Arg * arg);
257static void togglegaps(const Arg * arg);
258static void defaultgaps(const Arg * arg);
259static void setlayout(const Arg * arg);
260static void setmfact(const Arg * arg);
261static void setup(void);
262static void seturgent(Client * c, int urg);
263static void showhide(Client * c);
264static void spawn(const Arg * arg);
265static Monitor *systraytomon(Monitor * m);
266static void tag(const Arg * arg);
267static void tagmon(const Arg * arg);
268static void tile(Monitor * m);
269static void togglebar(const Arg * arg);
270static void togglefloating(const Arg * arg);
271static void togglefullscr(const Arg * arg);
272static void toggletag(const Arg * arg);
273static void toggleview(const Arg * arg);
274static void unfocus(Client * c, int setfocus);
275static void unmanage(Client * c, int destroyed);
276static void unmapnotify(XEvent * e);
277static void updatebarpos(Monitor * m);
278static void updatebars(void);
279static void updateclientlist(void);
280static int updategeom(void);
281static void updatenumlockmask(void);
282static void updatesystray(void);
283static void updatesystrayicongeom(Client * i, int w, int h);
284static void updatesystrayiconstate(Client * i, XPropertyEvent * ev);
285static void updatesizehints(Client * c);
286static void updatestatus(void);
287static void updatetitle(Client * c);
288static void updatewindowtype(Client * c);
289static void updatewmhints(Client * c);
290static void view(const Arg * arg);
291static Client *wintoclient(Window w);
292static Monitor *wintomon(Window w);
293static Client *wintosystrayicon(Window w);
294static int xerror(Display * dpy, XErrorEvent * ee);
295static int xerrordummy(Display * dpy, XErrorEvent * ee);
296static int xerrorstart(Display * dpy, XErrorEvent * ee);
297static void zoom(const Arg * arg);
298
299static pid_t getparentprocess(pid_t p);
300static int isdescprocess(pid_t p, pid_t c);
301static Client *swallowingclient(Window w);
302static Client *termforwin(const Client * c);
303static pid_t winpid(Window w);
304
305/* variables */
306static Systray *systray = NULL;
307static const char broken[] = "broken";
308static char stext[256];
309static int screen;
310static int sw, sh; /* X display screen geometry width, height */
311static int bh; /* bar height */
312static int enablegaps = 1; /* enables gaps, used by togglegaps */
313static int lrpad; /* sum of left and right padding for text */
314static int (*xerrorxlib)(Display *, XErrorEvent *);
315static unsigned int numlockmask = 0;
316static void (*handler[LASTEvent])(XEvent *) = {
317 [ButtonPress] = buttonpress,
318 [ClientMessage] = clientmessage,
319 [ConfigureRequest] = configurerequest,
320 [ConfigureNotify] = configurenotify,
321 [DestroyNotify] = destroynotify,
322 [EnterNotify] = enternotify,
323 [Expose] = expose,
324 [FocusIn] = focusin,
325 [KeyPress] = keypress,
326 [MappingNotify] = mappingnotify,
327 [MapRequest] = maprequest,
328 [MotionNotify] = motionnotify,
329 [PropertyNotify] = propertynotify,
330 [ResizeRequest] = resizerequest,
331 [UnmapNotify] = unmapnotify
332};
333
334static Atom wmatom[WMLast], netatom[NetLast], xatom[XLast];
335static int running = 1;
336static Cur *cursor[CurLast];
337static Clr **scheme;
338static Display *dpy;
339static Drw *drw;
340static Monitor *mons, *selmon;
341static Window root, wmcheckwin;
342
343static xcb_connection_t *xcon;
344
345/* configuration, allows nested code to access above variables */
346#include "config.h"
347
348/* compile-time check if all tags fit into an unsigned int bit array. */
349struct NumTags {
350 char limitexceeded[LENGTH(tags) > 31 ? -1 : 1];
351};
352
353/* function implementations */
354void applyrules(Client *c)
355{
356 const char *class, *instance;
357 unsigned int i;
358 const Rule *r;
359 Monitor *m;
360 XClassHint ch = { NULL, NULL };
361
362 /* rule matching */
363 c->isfloating = 0;
364 c->tags = 0;
365 XGetClassHint(dpy, c->win, &ch);
366 class = ch.res_class ? ch.res_class : broken;
367 instance = ch.res_name ? ch.res_name : broken;
368
369 for (i = 0; i < LENGTH(rules); i++) {
370 r = &rules[i];
371 if ((!r->title || strstr(c->name, r->title))
372 && (!r->class || strstr(class, r->class))
373 && (!r->instance || strstr(instance, r->instance))) {
374 c->isterminal = r->isterminal;
375 c->noswallow = r->noswallow;
376 c->isfloating = r->isfloating;
377 c->tags |= r->tags;
378 for (m = mons; m && m->num != r->monitor; m = m->next) ;
379 if (m)
380 c->mon = m;
381 }
382 }
383 if (ch.res_class)
384 XFree(ch.res_class);
385 if (ch.res_name)
386 XFree(ch.res_name);
387 c->tags =
388 c->tags & TAGMASK ? c->tags & TAGMASK : c->mon->tagset[c->mon->
389 seltags];
390}
391
392int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact)
393{
394 int baseismin;
395 Monitor *m = c->mon;
396
397 /* set minimum possible */
398 *w = MAX(1, *w);
399 *h = MAX(1, *h);
400 if (interact) {
401 if (*x > sw)
402 *x = sw - WIDTH(c);
403 if (*y > sh)
404 *y = sh - HEIGHT(c);
405 if (*x + *w + 2 * c->bw < 0)
406 *x = 0;
407 if (*y + *h + 2 * c->bw < 0)
408 *y = 0;
409 } else {
410 if (*x >= m->wx + m->ww)
411 *x = m->wx + m->ww - WIDTH(c);
412 if (*y >= m->wy + m->wh)
413 *y = m->wy + m->wh - HEIGHT(c);
414 if (*x + *w + 2 * c->bw <= m->wx)
415 *x = m->wx;
416 if (*y + *h + 2 * c->bw <= m->wy)
417 *y = m->wy;
418 }
419 if (*h < bh)
420 *h = bh;
421 if (*w < bh)
422 *w = bh;
423 if (resizehints || c->isfloating || !c->mon->lt[c->mon->sellt]->arrange) {
424 if (!c->hintsvalid)
425 updatesizehints(c);
426 /* see last two sentences in ICCCM 4.1.2.3 */
427 baseismin = c->basew == c->minw && c->baseh == c->minh;
428 if (!baseismin) { /* temporarily remove base dimensions */
429 *w -= c->basew;
430 *h -= c->baseh;
431 }
432 /* adjust for aspect limits */
433 if (c->mina > 0 && c->maxa > 0) {
434 if (c->maxa < (float)*w / *h)
435 *w = *h * c->maxa + 0.5;
436 else if (c->mina < (float)*h / *w)
437 *h = *w * c->mina + 0.5;
438 }
439 if (baseismin) { /* increment calculation requires this */
440 *w -= c->basew;
441 *h -= c->baseh;
442 }
443 /* adjust for increment value */
444 if (c->incw)
445 *w -= *w % c->incw;
446 if (c->inch)
447 *h -= *h % c->inch;
448 /* restore base dimensions */
449 *w = MAX(*w + c->basew, c->minw);
450 *h = MAX(*h + c->baseh, c->minh);
451 if (c->maxw)
452 *w = MIN(*w, c->maxw);
453 if (c->maxh)
454 *h = MIN(*h, c->maxh);
455 }
456 return *x != c->x || *y != c->y || *w != c->w || *h != c->h;
457}
458
459void arrange(Monitor *m)
460{
461 if (m)
462 showhide(m->stack);
463 else
464 for (m = mons; m; m = m->next)
465 showhide(m->stack);
466 if (m) {
467 arrangemon(m);
468 restack(m);
469 } else
470 for (m = mons; m; m = m->next)
471 arrangemon(m);
472}
473
474void arrangemon(Monitor *m)
475{
476 strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol);
477 if (m->lt[m->sellt]->arrange)
478 m->lt[m->sellt]->arrange(m);
479}
480
481void attach(Client *c)
482{
483 c->next = c->mon->clients;
484 c->mon->clients = c;
485}
486
487void attachstack(Client *c)
488{
489 c->snext = c->mon->stack;
490 c->mon->stack = c;
491}
492
493void swallow(Client *p, Client *c)
494{
495
496 if (c->noswallow || c->isterminal)
497 return;
498 if (c->noswallow && !swallowfloating && c->isfloating)
499 return;
500
501 detach(c);
502 detachstack(c);
503
504 setclientstate(c, WithdrawnState);
505 XUnmapWindow(dpy, p->win);
506
507 p->swallowing = c;
508 c->mon = p->mon;
509
510 Window w = p->win;
511 p->win = c->win;
512 c->win = w;
513 updatetitle(p);
514 XMoveResizeWindow(dpy, p->win, p->x, p->y, p->w, p->h);
515 arrange(p->mon);
516 configure(p);
517 updateclientlist();
518}
519
520void unswallow(Client *c)
521{
522 c->win = c->swallowing->win;
523
524 free(c->swallowing);
525 c->swallowing = NULL;
526
527 /* unfullscreen the client */
528 setfullscreen(c, 0);
529 updatetitle(c);
530 arrange(c->mon);
531 XMapWindow(dpy, c->win);
532 XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h);
533 setclientstate(c, NormalState);
534 focus(NULL);
535 arrange(c->mon);
536}
537
538void buttonpress(XEvent *e)
539{
540 unsigned int i, x, click;
541 Arg arg = { 0 };
542 Client *c;
543 Monitor *m;
544 XButtonPressedEvent *ev = &e->xbutton;
545
546 click = ClkRootWin;
547 /* focus monitor if necessary */
548 if ((m = wintomon(ev->window)) && m != selmon) {
549 unfocus(selmon->sel, 1);
550 selmon = m;
551 focus(NULL);
552 }
553 if (ev->window == selmon->barwin) {
554 i = x = 0;
555 unsigned int occ = 0;
556 for(c = m->clients; c; c=c->next)
557 occ |= c->tags;
558 do {
559 /* Do not reserve space for vacant tags */
560 if (!(occ & 1 << i || m->tagset[m->seltags] & 1 << i))
561 continue;
562 x += TEXTW(tags[i]);
563 } while (ev->x >= x && ++i < LENGTH(tags));
564 if (i < LENGTH(tags)) {
565 click = ClkTagBar;
566 arg.ui = 1 << i;
567 } else if (ev->x < x + TEXTW(selmon->ltsymbol))
568 click = ClkLtSymbol;
569 else if (ev->x >
570 selmon->ww - (int)TEXTW(stext) - getsystraywidth())
571 click = ClkStatusText;
572 else
573 click = ClkWinTitle;
574 } else if ((c = wintoclient(ev->window))) {
575 focus(c);
576 restack(selmon);
577 XAllowEvents(dpy, ReplayPointer, CurrentTime);
578 click = ClkClientWin;
579 }
580 for (i = 0; i < LENGTH(buttons); i++)
581 if (click == buttons[i].click && buttons[i].func
582 && buttons[i].button == ev->button
583 && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state))
584 buttons[i].func(click == ClkTagBar
585 && buttons[i].arg.i ==
586 0 ? &arg : &buttons[i].arg);
587}
588
589void checkotherwm(void)
590{
591 xerrorxlib = XSetErrorHandler(xerrorstart);
592 /* this causes an error if some other window manager is running */
593 XSelectInput(dpy, DefaultRootWindow(dpy), SubstructureRedirectMask);
594 XSync(dpy, False);
595 XSetErrorHandler(xerror);
596 XSync(dpy, False);
597}
598
599void cleanup(void)
600{
601 Arg a = {.ui = ~0 };
602 Layout foo = { "", NULL };
603 Monitor *m;
604 size_t i;
605
606 view(&a);
607 selmon->lt[selmon->sellt] = &foo;
608 for (m = mons; m; m = m->next)
609 while (m->stack)
610 unmanage(m->stack, 0);
611 XUngrabKey(dpy, AnyKey, AnyModifier, root);
612 while (mons)
613 cleanupmon(mons);
614
615 if (showsystray) {
616 XUnmapWindow(dpy, systray->win);
617 XDestroyWindow(dpy, systray->win);
618 free(systray);
619 }
620
621 for (i = 0; i < CurLast; i++)
622 drw_cur_free(drw, cursor[i]);
623 for (i = 0; i < LENGTH(colors); i++)
624 free(scheme[i]);
625 free(scheme);
626 XDestroyWindow(dpy, wmcheckwin);
627 drw_free(drw);
628 XSync(dpy, False);
629 XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
630 XDeleteProperty(dpy, root, netatom[NetActiveWindow]);
631}
632
633void cleanupmon(Monitor *mon)
634{
635 Monitor *m;
636
637 if (mon == mons)
638 mons = mons->next;
639 else {
640 for (m = mons; m && m->next != mon; m = m->next) ;
641 m->next = mon->next;
642 }
643 XUnmapWindow(dpy, mon->barwin);
644 XDestroyWindow(dpy, mon->barwin);
645 free(mon);
646}
647
648void clientmessage(XEvent *e)
649{
650 XWindowAttributes wa;
651 XSetWindowAttributes swa;
652 XClientMessageEvent *cme = &e->xclient;
653 Client *c = wintoclient(cme->window);
654
655 if (showsystray && cme->window == systray->win
656 && cme->message_type == netatom[NetSystemTrayOP]) {
657 /* add systray icons */
658 if (cme->data.l[1] == SYSTEM_TRAY_REQUEST_DOCK) {
659 if (!(c = (Client *) calloc(1, sizeof(Client))))
660 die("fatal: could not malloc() %u bytes\n",
661 sizeof(Client));
662 if (!(c->win = cme->data.l[2])) {
663 free(c);
664 return;
665 }
666 c->mon = selmon;
667 c->next = systray->icons;
668 systray->icons = c;
669 if (!XGetWindowAttributes(dpy, c->win, &wa)) {
670 /* use sane defaults */
671 wa.width = bh;
672 wa.height = bh;
673 wa.border_width = 0;
674 }
675 c->x = c->oldx = c->y = c->oldy = 0;
676 c->w = c->oldw = wa.width;
677 c->h = c->oldh = wa.height;
678 c->oldbw = wa.border_width;
679 c->bw = 0;
680 c->isfloating = True;
681 /* reuse tags field as mapped status */
682 c->tags = 1;
683 updatesizehints(c);
684 updatesystrayicongeom(c, wa.width, wa.height);
685 XAddToSaveSet(dpy, c->win);
686 XSelectInput(dpy, c->win,
687 StructureNotifyMask | PropertyChangeMask |
688 ResizeRedirectMask);
689 XReparentWindow(dpy, c->win, systray->win, 0, 0);
690 /* use parents background color */
691 swa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
692 XChangeWindowAttributes(dpy, c->win, CWBackPixel, &swa);
693 sendevent(c->win, netatom[Xembed], StructureNotifyMask,
694 CurrentTime, XEMBED_EMBEDDED_NOTIFY, 0,
695 systray->win, XEMBED_EMBEDDED_VERSION);
696 /* FIXME not sure if I have to send these events, too */
697 sendevent(c->win, netatom[Xembed], StructureNotifyMask,
698 CurrentTime, XEMBED_FOCUS_IN, 0, systray->win,
699 XEMBED_EMBEDDED_VERSION);
700 sendevent(c->win, netatom[Xembed], StructureNotifyMask,
701 CurrentTime, XEMBED_WINDOW_ACTIVATE, 0,
702 systray->win, XEMBED_EMBEDDED_VERSION);
703 sendevent(c->win, netatom[Xembed], StructureNotifyMask,
704 CurrentTime, XEMBED_MODALITY_ON, 0,
705 systray->win, XEMBED_EMBEDDED_VERSION);
706 XSync(dpy, False);
707 resizebarwin(selmon);
708 updatesystray();
709 setclientstate(c, NormalState);
710 }
711 return;
712 }
713
714 if (!c)
715 return;
716 if (cme->message_type == netatom[NetWMState]) {
717 if (cme->data.l[1] == netatom[NetWMFullscreen]
718 || cme->data.l[2] == netatom[NetWMFullscreen])
719 setfullscreen(c, (cme->data.l[0] == 1 /* _NET_WM_STATE_ADD */
720 || (cme->data.l[0] ==
721 2 /* _NET_WM_STATE_TOGGLE */
722 && !c->isfullscreen)));
723 } else if (cme->message_type == netatom[NetActiveWindow]) {
724 if (c != selmon->sel && !c->isurgent)
725 seturgent(c, 1);
726 }
727}
728
729void configure(Client *c)
730{
731 XConfigureEvent ce;
732
733 ce.type = ConfigureNotify;
734 ce.display = dpy;
735 ce.event = c->win;
736 ce.window = c->win;
737 ce.x = c->x;
738 ce.y = c->y;
739 ce.width = c->w;
740 ce.height = c->h;
741 ce.border_width = c->bw;
742 ce.above = None;
743 ce.override_redirect = False;
744 XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *) & ce);
745}
746
747void configurenotify(XEvent *e)
748{
749 Monitor *m;
750 Client *c;
751 XConfigureEvent *ev = &e->xconfigure;
752 int dirty;
753
754 /* TODO: updategeom handling sucks, needs to be simplified */
755 if (ev->window == root) {
756 dirty = (sw != ev->width || sh != ev->height);
757 sw = ev->width;
758 sh = ev->height;
759 if (updategeom() || dirty) {
760 drw_resize(drw, sw, bh);
761 updatebars();
762 for (m = mons; m; m = m->next) {
763 for (c = m->clients; c; c = c->next)
764 if (c->isfullscreen)
765 resizeclient(c, m->mx, m->my,
766 m->mw, m->mh);
767 resizebarwin(m);
768 }
769 focus(NULL);
770 arrange(NULL);
771 }
772 }
773}
774
775void configurerequest(XEvent *e)
776{
777 Client *c;
778 Monitor *m;
779 XConfigureRequestEvent *ev = &e->xconfigurerequest;
780 XWindowChanges wc;
781
782 if ((c = wintoclient(ev->window))) {
783 if (ev->value_mask & CWBorderWidth)
784 c->bw = ev->border_width;
785 else if (c->isfloating || !selmon->lt[selmon->sellt]->arrange) {
786 m = c->mon;
787 if (ev->value_mask & CWX) {
788 c->oldx = c->x;
789 c->x = m->mx + ev->x;
790 }
791 if (ev->value_mask & CWY) {
792 c->oldy = c->y;
793 c->y = m->my + ev->y;
794 }
795 if (ev->value_mask & CWWidth) {
796 c->oldw = c->w;
797 c->w = ev->width;
798 }
799 if (ev->value_mask & CWHeight) {
800 c->oldh = c->h;
801 c->h = ev->height;
802 }
803 if ((c->x + c->w) > m->mx + m->mw && c->isfloating)
804 c->x = m->mx + (m->mw / 2 - WIDTH(c) / 2); /* center in x direction */
805 if ((c->y + c->h) > m->my + m->mh && c->isfloating)
806 c->y = m->my + (m->mh / 2 - HEIGHT(c) / 2); /* center in y direction */
807 if ((ev->value_mask & (CWX | CWY))
808 && !(ev->value_mask & (CWWidth | CWHeight)))
809 configure(c);
810 if (ISVISIBLE(c))
811 XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w,
812 c->h);
813 } else
814 configure(c);
815 } else {
816 wc.x = ev->x;
817 wc.y = ev->y;
818 wc.width = ev->width;
819 wc.height = ev->height;
820 wc.border_width = ev->border_width;
821 wc.sibling = ev->above;
822 wc.stack_mode = ev->detail;
823 XConfigureWindow(dpy, ev->window, ev->value_mask, &wc);
824 }
825 XSync(dpy, False);
826}
827
828Monitor *createmon(void)
829{
830 Monitor *m;
831
832 m = ecalloc(1, sizeof(Monitor));
833 m->tagset[0] = m->tagset[1] = 1;
834 m->mfact = mfact;
835 m->nmaster = nmaster;
836 m->showbar = showbar;
837 m->topbar = topbar;
838 m->gappih = gappih;
839 m->gappiv = gappiv;
840 m->gappoh = gappoh;
841 m->gappov = gappov;
842 m->lt[0] = &layouts[0];
843 m->lt[1] = &layouts[1 % LENGTH(layouts)];
844 strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol);
845 return m;
846}
847
848void destroynotify(XEvent *e)
849{
850 Client *c;
851 XDestroyWindowEvent *ev = &e->xdestroywindow;
852
853 if ((c = wintoclient(ev->window)))
854 unmanage(c, 1);
855 else if ((c = wintosystrayicon(ev->window))) {
856 removesystrayicon(c);
857 resizebarwin(selmon);
858 updatesystray();
859 } else if ((c = swallowingclient(ev->window)))
860 unmanage(c->swallowing, 1);
861}
862
863void detach(Client *c)
864{
865 Client **tc;
866
867 for (tc = &c->mon->clients; *tc && *tc != c; tc = &(*tc)->next) ;
868 *tc = c->next;
869}
870
871void detachstack(Client *c)
872{
873 Client **tc, *t;
874
875 for (tc = &c->mon->stack; *tc && *tc != c; tc = &(*tc)->snext) ;
876 *tc = c->snext;
877
878 if (c == c->mon->sel) {
879 for (t = c->mon->stack; t && !ISVISIBLE(t); t = t->snext) ;
880 c->mon->sel = t;
881 }
882}
883
884Monitor *dirtomon(int dir)
885{
886 Monitor *m = NULL;
887
888 if (dir > 0) {
889 if (!(m = selmon->next))
890 m = mons;
891 } else if (selmon == mons)
892 for (m = mons; m->next; m = m->next) ;
893 else
894 for (m = mons; m->next != selmon; m = m->next) ;
895 return m;
896}
897
898void drawbar(Monitor *m)
899{
900 int x, w, tw = 0, stw = 0;
901 int boxs = drw->fonts->h / 9;
902 int boxw = drw->fonts->h / 6 + 2;
903 unsigned int i, occ = 0, urg = 0;
904 Client *c;
905
906 if (!m->showbar)
907 return;
908
909 if (showsystray && m == systraytomon(m) && !systrayonleft)
910 stw = getsystraywidth();
911
912 /* draw status first so it can be overdrawn by tags later */
913 if (m == selmon) { /* status is only drawn on selected monitor */
914 drw_setscheme(drw, scheme[SchemeNorm]);
915 tw = TEXTW(stext) - lrpad / 2 + 2; /* 2px extra right padding */
916 drw_text(drw, m->ww - tw - stw, 0, tw, bh, lrpad / 2 - 2, stext,
917 0);
918 }
919
920 resizebarwin(m);
921 for (c = m->clients; c; c = c->next) {
922 occ |= c->tags;
923 if (c->isurgent)
924 urg |= c->tags;
925 }
926 x = 0;
927 for (i = 0; i < LENGTH(tags); i++) {
928 /* Do not draw vacant tags */
929 if(!(occ & 1 << i || m->tagset[m->seltags] & 1 << i))
930 continue;
931 w = TEXTW(tags[i]);
932 drw_setscheme(drw,
933 scheme[m->
934 tagset[m->
935 seltags] & 1 << i ? SchemeSel :
936 SchemeNorm]);
937 drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i);
938 if (occ & 1 << i)
939 drw_rect(drw, x + boxs, boxs, boxw, boxw,
940 m == selmon && selmon->sel
941 && selmon->sel->tags & 1 << i, urg & 1 << i);
942 x += w;
943 }
944 w = TEXTW(m->ltsymbol);
945 drw_setscheme(drw, scheme[SchemeNorm]);
946 x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0);
947
948 if ((w = m->ww - tw - stw - x) > bh) {
949 if (m->sel) {
950 drw_setscheme(drw,
951 scheme[m ==
952 selmon ? SchemeSel : SchemeNorm]);
953 drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0);
954 if (m->sel->isfloating)
955 drw_rect(drw, x + boxs, boxs, boxw, boxw,
956 m->sel->isfixed, 0);
957 } else {
958 drw_setscheme(drw, scheme[SchemeNorm]);
959 drw_rect(drw, x, 0, w, bh, 1, 1);
960 }
961 }
962 drw_map(drw, m->barwin, 0, 0, m->ww - stw, bh);
963}
964
965void drawbars(void)
966{
967 Monitor *m;
968
969 for (m = mons; m; m = m->next)
970 drawbar(m);
971}
972
973void enternotify(XEvent *e)
974{
975 Client *c;
976 Monitor *m;
977 XCrossingEvent *ev = &e->xcrossing;
978
979 if ((ev->mode != NotifyNormal || ev->detail == NotifyInferior)
980 && ev->window != root)
981 return;
982 c = wintoclient(ev->window);
983 m = c ? c->mon : wintomon(ev->window);
984 if (m != selmon) {
985 unfocus(selmon->sel, 1);
986 selmon = m;
987 } else if (!c || c == selmon->sel)
988 return;
989 focus(c);
990}
991
992void expose(XEvent *e)
993{
994 Monitor *m;
995 XExposeEvent *ev = &e->xexpose;
996
997 if (ev->count == 0 && (m = wintomon(ev->window))) {
998 drawbar(m);
999 if (m == selmon)
1000 updatesystray();
1001 }
1002}
1003
1004void focus(Client *c)
1005{
1006 if (!c || !ISVISIBLE(c))
1007 for (c = selmon->stack; c && !ISVISIBLE(c); c = c->snext) ;
1008 if (selmon->sel && selmon->sel != c)
1009 unfocus(selmon->sel, 0);
1010 if (c) {
1011 if (c->mon != selmon)
1012 selmon = c->mon;
1013 if (c->isurgent)
1014 seturgent(c, 0);
1015 detachstack(c);
1016 attachstack(c);
1017 grabbuttons(c, 1);
1018 XSetWindowBorder(dpy, c->win,
1019 scheme[SchemeSel][ColBorder].pixel);
1020 setfocus(c);
1021 } else {
1022 XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
1023 XDeleteProperty(dpy, root, netatom[NetActiveWindow]);
1024 }
1025 selmon->sel = c;
1026 drawbars();
1027}
1028
1029/* there are some broken focus acquiring clients needing extra handling */
1030void focusin(XEvent *e)
1031{
1032 XFocusChangeEvent *ev = &e->xfocus;
1033
1034 if (selmon->sel && ev->window != selmon->sel->win)
1035 setfocus(selmon->sel);
1036}
1037
1038void focusmon(const Arg *arg)
1039{
1040 Monitor *m;
1041
1042 if (!mons->next)
1043 return;
1044 if ((m = dirtomon(arg->i)) == selmon)
1045 return;
1046 unfocus(selmon->sel, 0);
1047 selmon = m;
1048 focus(NULL);
1049}
1050
1051void focusstack(const Arg *arg)
1052{
1053 Client *c = NULL, *i;
1054
1055 if (!selmon->sel || (selmon->sel->isfullscreen && lockfullscreen))
1056 return;
1057 if (arg->i > 0) {
1058 for (c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next) ;
1059 if (!c)
1060 for (c = selmon->clients; c && !ISVISIBLE(c);
1061 c = c->next) ;
1062 } else {
1063 for (i = selmon->clients; i != selmon->sel; i = i->next)
1064 if (ISVISIBLE(i))
1065 c = i;
1066 if (!c)
1067 for (; i; i = i->next)
1068 if (ISVISIBLE(i))
1069 c = i;
1070 }
1071 if (c) {
1072 focus(c);
1073 restack(selmon);
1074 }
1075}
1076
1077Atom getatomprop(Client *c, Atom prop)
1078{
1079 int di;
1080 unsigned long dl;
1081 unsigned char *p = NULL;
1082 Atom da, atom = None;
1083
1084 /* FIXME getatomprop should return the number of items and a pointer to
1085 * the stored data instead of this workaround */
1086 Atom req = XA_ATOM;
1087 if (prop == xatom[XembedInfo])
1088 req = xatom[XembedInfo];
1089
1090 if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, req,
1091 &da, &di, &dl, &dl, &p) == Success && p) {
1092 atom = *(Atom *) p;
1093 if (da == xatom[XembedInfo] && dl == 2)
1094 atom = ((Atom *) p)[1];
1095 XFree(p);
1096 }
1097 return atom;
1098}
1099
1100unsigned int getsystraywidth()
1101{
1102 unsigned int w = 0;
1103 Client *i;
1104 if (showsystray)
1105 for (i = systray->icons; i;
1106 w += i->w + systrayspacing, i = i->next) ;
1107 return w ? w + systrayspacing : 1;
1108}
1109
1110int getrootptr(int *x, int *y)
1111{
1112 int di;
1113 unsigned int dui;
1114 Window dummy;
1115
1116 return XQueryPointer(dpy, root, &dummy, &dummy, x, y, &di, &di, &dui);
1117}
1118
1119long getstate(Window w)
1120{
1121 int format;
1122 long result = -1;
1123 unsigned char *p = NULL;
1124 unsigned long n, extra;
1125 Atom real;
1126
1127 if (XGetWindowProperty
1128 (dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState], &real,
1129 &format, &n, &extra, (unsigned char **)&p) != Success)
1130 return -1;
1131 if (n != 0)
1132 result = *p;
1133 XFree(p);
1134 return result;
1135}
1136
1137int gettextprop(Window w, Atom atom, char *text, unsigned int size)
1138{
1139 char **list = NULL;
1140 int n;
1141 XTextProperty name;
1142
1143 if (!text || size == 0)
1144 return 0;
1145 text[0] = '\0';
1146 if (!XGetTextProperty(dpy, w, &name, atom) || !name.nitems)
1147 return 0;
1148 if (name.encoding == XA_STRING) {
1149 strncpy(text, (char *)name.value, size - 1);
1150 } else if (XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success
1151 && n > 0 && *list) {
1152 strncpy(text, *list, size - 1);
1153 XFreeStringList(list);
1154 }
1155 text[size - 1] = '\0';
1156 XFree(name.value);
1157 return 1;
1158}
1159
1160void grabbuttons(Client *c, int focused)
1161{
1162 updatenumlockmask();
1163 {
1164 unsigned int i, j;
1165 unsigned int modifiers[] =
1166 { 0, LockMask, numlockmask, numlockmask | LockMask };
1167 XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
1168 if (!focused)
1169 XGrabButton(dpy, AnyButton, AnyModifier, c->win, False,
1170 BUTTONMASK, GrabModeSync, GrabModeSync,
1171 None, None);
1172 for (i = 0; i < LENGTH(buttons); i++)
1173 if (buttons[i].click == ClkClientWin)
1174 for (j = 0; j < LENGTH(modifiers); j++)
1175 XGrabButton(dpy, buttons[i].button,
1176 buttons[i].
1177 mask | modifiers[j], c->win,
1178 False, BUTTONMASK,
1179 GrabModeAsync, GrabModeSync,
1180 None, None);
1181 }
1182}
1183
1184void grabkeys(void)
1185{
1186 updatenumlockmask();
1187 {
1188 unsigned int i, j, k;
1189 unsigned int modifiers[] =
1190 { 0, LockMask, numlockmask, numlockmask | LockMask };
1191 int start, end, skip;
1192 KeySym *syms;
1193
1194 XUngrabKey(dpy, AnyKey, AnyModifier, root);
1195 XDisplayKeycodes(dpy, &start, &end);
1196 syms = XGetKeyboardMapping(dpy, start, end - start + 1, &skip);
1197 if (!syms)
1198 return;
1199 for (k = start; k <= end; k++)
1200 for (i = 0; i < LENGTH(keys); i++)
1201 /* skip modifier codes, we do that ourselves */
1202 if (keys[i].keysym == syms[(k - start) * skip])
1203 for (j = 0; j < LENGTH(modifiers); j++)
1204 XGrabKey(dpy, k,
1205 keys[i].
1206 mod | modifiers[j],
1207 root, True,
1208 GrabModeAsync,
1209 GrabModeAsync);
1210 XFree(syms);
1211 }
1212}
1213
1214void incnmaster(const Arg *arg)
1215{
1216 selmon->nmaster = MAX(selmon->nmaster + arg->i, 0);
1217 arrange(selmon);
1218}
1219
1220#ifdef XINERAMA
1221static int
1222isuniquegeom(XineramaScreenInfo *unique, size_t n, XineramaScreenInfo *info)
1223{
1224 while (n--)
1225 if (unique[n].x_org == info->x_org
1226 && unique[n].y_org == info->y_org
1227 && unique[n].width == info->width
1228 && unique[n].height == info->height)
1229 return 0;
1230 return 1;
1231}
1232#endif /* XINERAMA */
1233
1234void keypress(XEvent *e)
1235{
1236 unsigned int i;
1237 KeySym keysym;
1238 XKeyEvent *ev;
1239
1240 ev = &e->xkey;
1241 keysym = XKeycodeToKeysym(dpy, (KeyCode) ev->keycode, 0);
1242 for (i = 0; i < LENGTH(keys); i++)
1243 if (keysym == keys[i].keysym
1244 && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state)
1245 && keys[i].func)
1246 keys[i].func(&(keys[i].arg));
1247}
1248
1249void killclient(const Arg *arg)
1250{
1251 if (!selmon->sel)
1252 return;
1253
1254 if (!sendevent
1255 (selmon->sel->win, wmatom[WMDelete], NoEventMask, wmatom[WMDelete],
1256 CurrentTime, 0, 0, 0)) {
1257 XGrabServer(dpy);
1258 XSetErrorHandler(xerrordummy);
1259 XSetCloseDownMode(dpy, DestroyAll);
1260 XKillClient(dpy, selmon->sel->win);
1261 XSync(dpy, False);
1262 XSetErrorHandler(xerror);
1263 XUngrabServer(dpy);
1264 }
1265}
1266
1267void manage(Window w, XWindowAttributes *wa)
1268{
1269 Client *c, *t = NULL, *term = NULL;
1270 Window trans = None;
1271 XWindowChanges wc;
1272
1273 c = ecalloc(1, sizeof(Client));
1274 c->win = w;
1275 c->pid = winpid(w);
1276 /* geometry */
1277 c->x = c->oldx = wa->x;
1278 c->y = c->oldy = wa->y;
1279 c->w = c->oldw = wa->width;
1280 c->h = c->oldh = wa->height;
1281 c->oldbw = wa->border_width;
1282
1283 updatetitle(c);
1284 if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) {
1285 c->mon = t->mon;
1286 c->tags = t->tags;
1287 } else {
1288 c->mon = selmon;
1289 applyrules(c);
1290 term = termforwin(c);
1291 }
1292
1293 if (c->x + WIDTH(c) > c->mon->wx + c->mon->ww)
1294 c->x = c->mon->wx + c->mon->ww - WIDTH(c);
1295 if (c->y + HEIGHT(c) > c->mon->wy + c->mon->wh)
1296 c->y = c->mon->wy + c->mon->wh - HEIGHT(c);
1297 c->x = MAX(c->x, c->mon->wx);
1298 c->y = MAX(c->y, c->mon->wy);
1299 c->bw = borderpx;
1300
1301 wc.border_width = c->bw;
1302 XConfigureWindow(dpy, w, CWBorderWidth, &wc);
1303 XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel);
1304 configure(c); /* propagates border_width, if size doesn't change */
1305 updatewindowtype(c);
1306 updatesizehints(c);
1307 updatewmhints(c);
1308 XSelectInput(dpy, w,
1309 EnterWindowMask | FocusChangeMask | PropertyChangeMask |
1310 StructureNotifyMask);
1311 grabbuttons(c, 0);
1312 if (!c->isfloating)
1313 c->isfloating = c->oldstate = trans != None || c->isfixed;
1314 if (c->isfloating)
1315 XRaiseWindow(dpy, c->win);
1316 attach(c);
1317 attachstack(c);
1318 XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32,
1319 PropModeAppend, (unsigned char *)&(c->win), 1);
1320 XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */
1321 setclientstate(c, NormalState);
1322 if (c->mon == selmon)
1323 unfocus(selmon->sel, 0);
1324 c->mon->sel = c;
1325 arrange(c->mon);
1326 XMapWindow(dpy, c->win);
1327 if (term)
1328 swallow(term, c);
1329 focus(NULL);
1330}
1331
1332void mappingnotify(XEvent *e)
1333{
1334 XMappingEvent *ev = &e->xmapping;
1335
1336 XRefreshKeyboardMapping(ev);
1337 if (ev->request == MappingKeyboard)
1338 grabkeys();
1339}
1340
1341void maprequest(XEvent *e)
1342{
1343 static XWindowAttributes wa;
1344 XMapRequestEvent *ev = &e->xmaprequest;
1345
1346 Client *i;
1347 if ((i = wintosystrayicon(ev->window))) {
1348 sendevent(i->win, netatom[Xembed], StructureNotifyMask,
1349 CurrentTime, XEMBED_WINDOW_ACTIVATE, 0, systray->win,
1350 XEMBED_EMBEDDED_VERSION);
1351 resizebarwin(selmon);
1352 updatesystray();
1353 }
1354
1355 if (!XGetWindowAttributes(dpy, ev->window, &wa) || wa.override_redirect)
1356 return;
1357 if (!wintoclient(ev->window))
1358 manage(ev->window, &wa);
1359}
1360
1361void monocle(Monitor *m)
1362{
1363 unsigned int n = 0;
1364 Client *c;
1365
1366 for (c = m->clients; c; c = c->next)
1367 if (ISVISIBLE(c))
1368 n++;
1369 if (n > 0) /* override layout symbol */
1370 snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n);
1371 for (c = nexttiled(m->clients); c; c = nexttiled(c->next))
1372 resize(c, m->wx, m->wy, m->ww - 2 * c->bw, m->wh - 2 * c->bw,
1373 0);
1374}
1375
1376void motionnotify(XEvent *e)
1377{
1378 static Monitor *mon = NULL;
1379 Monitor *m;
1380 XMotionEvent *ev = &e->xmotion;
1381
1382 if (ev->window != root)
1383 return;
1384 if ((m = recttomon(ev->x_root, ev->y_root, 1, 1)) != mon && mon) {
1385 unfocus(selmon->sel, 1);
1386 selmon = m;
1387 focus(NULL);
1388 }
1389 mon = m;
1390}
1391
1392void movemouse(const Arg *arg)
1393{
1394 int x, y, ocx, ocy, nx, ny;
1395 Client *c;
1396 Monitor *m;
1397 XEvent ev;
1398 Time lasttime = 0;
1399
1400 if (!(c = selmon->sel))
1401 return;
1402 if (c->isfullscreen) /* no support moving fullscreen windows by mouse */
1403 return;
1404 restack(selmon);
1405 ocx = c->x;
1406 ocy = c->y;
1407 if (XGrabPointer
1408 (dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, None,
1409 cursor[CurMove]->cursor, CurrentTime) != GrabSuccess)
1410 return;
1411 if (!getrootptr(&x, &y))
1412 return;
1413 do {
1414 XMaskEvent(dpy,
1415 MOUSEMASK | ExposureMask | SubstructureRedirectMask,
1416 &ev);
1417 switch (ev.type) {
1418 case ConfigureRequest:
1419 case Expose:
1420 case MapRequest:
1421 handler[ev.type] (&ev);
1422 break;
1423 case MotionNotify:
1424 if ((ev.xmotion.time - lasttime) <= (1000 / 60))
1425 continue;
1426 lasttime = ev.xmotion.time;
1427
1428 nx = ocx + (ev.xmotion.x - x);
1429 ny = ocy + (ev.xmotion.y - y);
1430 if (abs(selmon->wx - nx) < snap)
1431 nx = selmon->wx;
1432 else if (abs
1433 ((selmon->wx + selmon->ww) - (nx + WIDTH(c))) <
1434 snap)
1435 nx = selmon->wx + selmon->ww - WIDTH(c);
1436 if (abs(selmon->wy - ny) < snap)
1437 ny = selmon->wy;
1438 else if (abs
1439 ((selmon->wy + selmon->wh) -
1440 (ny + HEIGHT(c))) < snap)
1441 ny = selmon->wy + selmon->wh - HEIGHT(c);
1442 if (!c->isfloating && selmon->lt[selmon->sellt]->arrange
1443 && (abs(nx - c->x) > snap || abs(ny - c->y) > snap))
1444 togglefloating(NULL);
1445 if (!selmon->lt[selmon->sellt]->arrange
1446 || c->isfloating)
1447 resize(c, nx, ny, c->w, c->h, 1);
1448 break;
1449 }
1450 } while (ev.type != ButtonRelease);
1451 XUngrabPointer(dpy, CurrentTime);
1452 if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) {
1453 sendmon(c, m);
1454 selmon = m;
1455 focus(NULL);
1456 }
1457}
1458
1459Client *nexttiled(Client *c)
1460{
1461 for (; c && (c->isfloating || !ISVISIBLE(c)); c = c->next) ;
1462 return c;
1463}
1464
1465void pop(Client *c)
1466{
1467 detach(c);
1468 attach(c);
1469 focus(c);
1470 arrange(c->mon);
1471}
1472
1473void propertynotify(XEvent *e)
1474{
1475 Client *c;
1476 Window trans;
1477 XPropertyEvent *ev = &e->xproperty;
1478
1479 if ((c = wintosystrayicon(ev->window))) {
1480 if (ev->atom == XA_WM_NORMAL_HINTS) {
1481 updatesizehints(c);
1482 updatesystrayicongeom(c, c->w, c->h);
1483 } else
1484 updatesystrayiconstate(c, ev);
1485 resizebarwin(selmon);
1486 updatesystray();
1487 }
1488
1489 if ((ev->window == root) && (ev->atom == XA_WM_NAME))
1490 updatestatus();
1491 else if (ev->state == PropertyDelete)
1492 return; /* ignore */
1493 else if ((c = wintoclient(ev->window))) {
1494 switch (ev->atom) {
1495 default:
1496 break;
1497 case XA_WM_TRANSIENT_FOR:
1498 if (!c->isfloating
1499 && (XGetTransientForHint(dpy, c->win, &trans))
1500 && (c->isfloating = (wintoclient(trans)) != NULL))
1501 arrange(c->mon);
1502 break;
1503 case XA_WM_NORMAL_HINTS:
1504 c->hintsvalid = 0;
1505 break;
1506 case XA_WM_HINTS:
1507 updatewmhints(c);
1508 drawbars();
1509 break;
1510 }
1511 if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) {
1512 updatetitle(c);
1513 if (c == c->mon->sel)
1514 drawbar(c->mon);
1515 }
1516 if (ev->atom == netatom[NetWMWindowType])
1517 updatewindowtype(c);
1518 }
1519}
1520
1521void quit(const Arg *arg)
1522{
1523 running = 0;
1524}
1525
1526Monitor *recttomon(int x, int y, int w, int h)
1527{
1528 Monitor *m, *r = selmon;
1529 int a, area = 0;
1530
1531 for (m = mons; m; m = m->next)
1532 if ((a = INTERSECT(x, y, w, h, m)) > area) {
1533 area = a;
1534 r = m;
1535 }
1536 return r;
1537}
1538
1539void removesystrayicon(Client *i)
1540{
1541 Client **ii;
1542
1543 if (!showsystray || !i)
1544 return;
1545 for (ii = &systray->icons; *ii && *ii != i; ii = &(*ii)->next) ;
1546 if (ii)
1547 *ii = i->next;
1548 free(i);
1549}
1550
1551void resize(Client *c, int x, int y, int w, int h, int interact)
1552{
1553 if (applysizehints(c, &x, &y, &w, &h, interact))
1554 resizeclient(c, x, y, w, h);
1555}
1556
1557void resizebarwin(Monitor *m)
1558{
1559 unsigned int w = m->ww;
1560 if (showsystray && m == systraytomon(m) && !systrayonleft)
1561 w -= getsystraywidth();
1562 XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, w, bh);
1563}
1564
1565void resizeclient(Client *c, int x, int y, int w, int h)
1566{
1567 XWindowChanges wc;
1568
1569 c->oldx = c->x;
1570 c->x = wc.x = x;
1571 c->oldy = c->y;
1572 c->y = wc.y = y;
1573 c->oldw = c->w;
1574 c->w = wc.width = w;
1575 c->oldh = c->h;
1576 c->h = wc.height = h;
1577 wc.border_width = c->bw;
1578 XConfigureWindow(dpy, c->win,
1579 CWX | CWY | CWWidth | CWHeight | CWBorderWidth, &wc);
1580 configure(c);
1581 XSync(dpy, False);
1582}
1583
1584void resizerequest(XEvent *e)
1585{
1586 XResizeRequestEvent *ev = &e->xresizerequest;
1587 Client *i;
1588
1589 if ((i = wintosystrayicon(ev->window))) {
1590 updatesystrayicongeom(i, ev->width, ev->height);
1591 resizebarwin(selmon);
1592 updatesystray();
1593 }
1594}
1595
1596void resizemouse(const Arg *arg)
1597{
1598 int ocx, ocy, nw, nh;
1599 Client *c;
1600 Monitor *m;
1601 XEvent ev;
1602 Time lasttime = 0;
1603
1604 if (!(c = selmon->sel))
1605 return;
1606 if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */
1607 return;
1608 restack(selmon);
1609 ocx = c->x;
1610 ocy = c->y;
1611 if (XGrabPointer
1612 (dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, None,
1613 cursor[CurResize]->cursor, CurrentTime) != GrabSuccess)
1614 return;
1615 XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1,
1616 c->h + c->bw - 1);
1617 do {
1618 XMaskEvent(dpy,
1619 MOUSEMASK | ExposureMask | SubstructureRedirectMask,
1620 &ev);
1621 switch (ev.type) {
1622 case ConfigureRequest:
1623 case Expose:
1624 case MapRequest:
1625 handler[ev.type] (&ev);
1626 break;
1627 case MotionNotify:
1628 if ((ev.xmotion.time - lasttime) <= (1000 / 60))
1629 continue;
1630 lasttime = ev.xmotion.time;
1631
1632 nw = MAX(ev.xmotion.x - ocx - 2 * c->bw + 1, 1);
1633 nh = MAX(ev.xmotion.y - ocy - 2 * c->bw + 1, 1);
1634 if (c->mon->wx + nw >= selmon->wx
1635 && c->mon->wx + nw <= selmon->wx + selmon->ww
1636 && c->mon->wy + nh >= selmon->wy
1637 && c->mon->wy + nh <= selmon->wy + selmon->wh) {
1638 if (!c->isfloating
1639 && selmon->lt[selmon->sellt]->arrange
1640 && (abs(nw - c->w) > snap
1641 || abs(nh - c->h) > snap))
1642 togglefloating(NULL);
1643 }
1644 if (!selmon->lt[selmon->sellt]->arrange
1645 || c->isfloating)
1646 resize(c, c->x, c->y, nw, nh, 1);
1647 break;
1648 }
1649 } while (ev.type != ButtonRelease);
1650 XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1,
1651 c->h + c->bw - 1);
1652 XUngrabPointer(dpy, CurrentTime);
1653 while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)) ;
1654 if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) {
1655 sendmon(c, m);
1656 selmon = m;
1657 focus(NULL);
1658 }
1659}
1660
1661void restack(Monitor *m)
1662{
1663 Client *c;
1664 XEvent ev;
1665 XWindowChanges wc;
1666
1667 drawbar(m);
1668 if (!m->sel)
1669 return;
1670 if (m->sel->isfloating || !m->lt[m->sellt]->arrange)
1671 XRaiseWindow(dpy, m->sel->win);
1672 if (m->lt[m->sellt]->arrange) {
1673 wc.stack_mode = Below;
1674 wc.sibling = m->barwin;
1675 for (c = m->stack; c; c = c->snext)
1676 if (!c->isfloating && ISVISIBLE(c)) {
1677 XConfigureWindow(dpy, c->win,
1678 CWSibling | CWStackMode, &wc);
1679 wc.sibling = c->win;
1680 }
1681 }
1682 XSync(dpy, False);
1683 while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)) ;
1684}
1685
1686void run(void)
1687{
1688 XEvent ev;
1689 /* main event loop */
1690 XSync(dpy, False);
1691 while (running && !XNextEvent(dpy, &ev))
1692 if (handler[ev.type])
1693 handler[ev.type] (&ev); /* call handler */
1694}
1695
1696void scan(void)
1697{
1698 unsigned int i, num;
1699 Window d1, d2, *wins = NULL;
1700 XWindowAttributes wa;
1701
1702 if (XQueryTree(dpy, root, &d1, &d2, &wins, &num)) {
1703 for (i = 0; i < num; i++) {
1704 if (!XGetWindowAttributes(dpy, wins[i], &wa)
1705 || wa.override_redirect
1706 || XGetTransientForHint(dpy, wins[i], &d1))
1707 continue;
1708 if (wa.map_state == IsViewable
1709 || getstate(wins[i]) == IconicState)
1710 manage(wins[i], &wa);
1711 }
1712 for (i = 0; i < num; i++) { /* now the transients */
1713 if (!XGetWindowAttributes(dpy, wins[i], &wa))
1714 continue;
1715 if (XGetTransientForHint(dpy, wins[i], &d1)
1716 && (wa.map_state == IsViewable
1717 || getstate(wins[i]) == IconicState))
1718 manage(wins[i], &wa);
1719 }
1720 if (wins)
1721 XFree(wins);
1722 }
1723}
1724
1725void sendmon(Client *c, Monitor *m)
1726{
1727 if (c->mon == m)
1728 return;
1729 unfocus(c, 1);
1730 detach(c);
1731 detachstack(c);
1732 c->mon = m;
1733 c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */
1734 attach(c);
1735 attachstack(c);
1736 focus(NULL);
1737 arrange(NULL);
1738}
1739
1740void setclientstate(Client *c, long state)
1741{
1742 long data[] = { state, None };
1743
1744 XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32,
1745 PropModeReplace, (unsigned char *)data, 2);
1746}
1747
1748int
1749sendevent(Window w, Atom proto, int mask, long d0, long d1, long d2, long d3,
1750 long d4)
1751{
1752 int n;
1753 Atom *protocols, mt;
1754 int exists = 0;
1755 XEvent ev;
1756
1757 if (proto == wmatom[WMTakeFocus] || proto == wmatom[WMDelete]) {
1758 mt = wmatom[WMProtocols];
1759 if (XGetWMProtocols(dpy, w, &protocols, &n)) {
1760 while (!exists && n--)
1761 exists = protocols[n] == proto;
1762 XFree(protocols);
1763 }
1764 } else {
1765 exists = True;
1766 mt = proto;
1767 }
1768
1769 if (exists) {
1770 ev.type = ClientMessage;
1771 ev.xclient.window = w;
1772 ev.xclient.message_type = mt;
1773 ev.xclient.format = 32;
1774 ev.xclient.data.l[0] = d0;
1775 ev.xclient.data.l[1] = d1;
1776 ev.xclient.data.l[2] = d2;
1777 ev.xclient.data.l[3] = d3;
1778 ev.xclient.data.l[4] = d4;
1779 XSendEvent(dpy, w, False, mask, &ev);
1780 }
1781 return exists;
1782}
1783
1784void setfocus(Client *c)
1785{
1786 if (!c->neverfocus) {
1787 XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime);
1788 XChangeProperty(dpy, root, netatom[NetActiveWindow],
1789 XA_WINDOW, 32, PropModeReplace,
1790 (unsigned char *)&(c->win), 1);
1791 }
1792 sendevent(c->win, wmatom[WMTakeFocus], NoEventMask, wmatom[WMTakeFocus],
1793 CurrentTime, 0, 0, 0);
1794}
1795
1796void setfullscreen(Client *c, int fullscreen)
1797{
1798 if (fullscreen && !c->isfullscreen) {
1799 XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32,
1800 PropModeReplace,
1801 (unsigned char *)&netatom[NetWMFullscreen], 1);
1802 c->isfullscreen = 1;
1803 c->oldstate = c->isfloating;
1804 c->oldbw = c->bw;
1805 c->bw = 0;
1806 c->isfloating = 1;
1807 resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh);
1808 XRaiseWindow(dpy, c->win);
1809 } else if (!fullscreen && c->isfullscreen) {
1810 XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32,
1811 PropModeReplace, (unsigned char *)0, 0);
1812 c->isfullscreen = 0;
1813 c->isfloating = c->oldstate;
1814 c->bw = c->oldbw;
1815 c->x = c->oldx;
1816 c->y = c->oldy;
1817 c->w = c->oldw;
1818 c->h = c->oldh;
1819 resizeclient(c, c->x, c->y, c->w, c->h);
1820 arrange(c->mon);
1821 }
1822}
1823
1824void setgaps(int oh, int ov, int ih, int iv)
1825{
1826 if (oh < 0)
1827 oh = 0;
1828 if (ov < 0)
1829 ov = 0;
1830 if (ih < 0)
1831 ih = 0;
1832 if (iv < 0)
1833 iv = 0;
1834
1835 selmon->gappoh = oh;
1836 selmon->gappov = ov;
1837 selmon->gappih = ih;
1838 selmon->gappiv = iv;
1839 arrange(selmon);
1840}
1841
1842void togglegaps(const Arg *arg)
1843{
1844 enablegaps = !enablegaps;
1845 arrange(selmon);
1846}
1847
1848void defaultgaps(const Arg *arg)
1849{
1850 setgaps(gappoh, gappov, gappih, gappiv);
1851}
1852
1853void incrgaps(const Arg *arg)
1854{
1855 setgaps(selmon->gappoh + arg->i,
1856 selmon->gappov + arg->i,
1857 selmon->gappih + arg->i, selmon->gappiv + arg->i);
1858}
1859
1860void incrigaps(const Arg *arg)
1861{
1862 setgaps(selmon->gappoh,
1863 selmon->gappov,
1864 selmon->gappih + arg->i, selmon->gappiv + arg->i);
1865}
1866
1867void incrogaps(const Arg *arg)
1868{
1869 setgaps(selmon->gappoh + arg->i,
1870 selmon->gappov + arg->i, selmon->gappih, selmon->gappiv);
1871}
1872
1873void incrohgaps(const Arg *arg)
1874{
1875 setgaps(selmon->gappoh + arg->i,
1876 selmon->gappov, selmon->gappih, selmon->gappiv);
1877}
1878
1879void incrovgaps(const Arg *arg)
1880{
1881 setgaps(selmon->gappoh,
1882 selmon->gappov + arg->i, selmon->gappih, selmon->gappiv);
1883}
1884
1885void incrihgaps(const Arg *arg)
1886{
1887 setgaps(selmon->gappoh,
1888 selmon->gappov, selmon->gappih + arg->i, selmon->gappiv);
1889}
1890
1891void incrivgaps(const Arg *arg)
1892{
1893 setgaps(selmon->gappoh,
1894 selmon->gappov, selmon->gappih, selmon->gappiv + arg->i);
1895}
1896
1897void setlayout(const Arg *arg)
1898{
1899 if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt])
1900 selmon->sellt ^= 1;
1901 if (arg && arg->v)
1902 selmon->lt[selmon->sellt] = (Layout *) arg->v;
1903 strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol,
1904 sizeof selmon->ltsymbol);
1905 if (selmon->sel)
1906 arrange(selmon);
1907 else
1908 drawbar(selmon);
1909}
1910
1911/* arg > 1.0 will set mfact absolutely */
1912void setmfact(const Arg *arg)
1913{
1914 float f;
1915
1916 if (!arg || !selmon->lt[selmon->sellt]->arrange)
1917 return;
1918 f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0;
1919 if (f < 0.05 || f > 0.95)
1920 return;
1921 selmon->mfact = f;
1922 arrange(selmon);
1923}
1924
1925void setup(void)
1926{
1927 int i;
1928 XSetWindowAttributes wa;
1929 Atom utf8string;
1930 struct sigaction sa;
1931
1932 /* do not transform children into zombies when they terminate */
1933 sigemptyset(&sa.sa_mask);
1934 sa.sa_flags = SA_NOCLDSTOP | SA_NOCLDWAIT | SA_RESTART;
1935 sa.sa_handler = SIG_IGN;
1936 sigaction(SIGCHLD, &sa, NULL);
1937
1938 /* clean up any zombies (inherited from .xinitrc etc) immediately */
1939 while (waitpid(-1, NULL, WNOHANG) > 0) ;
1940
1941 /* init screen */
1942 screen = DefaultScreen(dpy);
1943 sw = DisplayWidth(dpy, screen);
1944 sh = DisplayHeight(dpy, screen);
1945 root = RootWindow(dpy, screen);
1946 drw = drw_create(dpy, screen, root, sw, sh);
1947 if (!drw_fontset_create(drw, fonts, LENGTH(fonts)))
1948 die("no fonts could be loaded.");
1949 lrpad = drw->fonts->h;
1950 bh = drw->fonts->h + 2;
1951 updategeom();
1952 /* init atoms */
1953 utf8string = XInternAtom(dpy, "UTF8_STRING", False);
1954 wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False);
1955 wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
1956 wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False);
1957 wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False);
1958 netatom[NetActiveWindow] =
1959 XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False);
1960 netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False);
1961 netatom[NetSystemTray] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_S0", False);
1962 netatom[NetSystemTrayOP] =
1963 XInternAtom(dpy, "_NET_SYSTEM_TRAY_OPCODE", False);
1964 netatom[NetSystemTrayOrientation] =
1965 XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION", False);
1966 netatom[NetSystemTrayOrientationHorz] =
1967 XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION_HORZ", False);
1968 netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False);
1969 netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False);
1970 netatom[NetWMCheck] =
1971 XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False);
1972 netatom[NetWMFullscreen] =
1973 XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
1974 netatom[NetWMWindowType] =
1975 XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);
1976 netatom[NetWMWindowTypeDialog] =
1977 XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False);
1978 netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False);
1979 xatom[Manager] = XInternAtom(dpy, "MANAGER", False);
1980 xatom[Xembed] = XInternAtom(dpy, "_XEMBED", False);
1981 xatom[XembedInfo] = XInternAtom(dpy, "_XEMBED_INFO", False);
1982 /* init cursors */
1983 cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr);
1984 cursor[CurResize] = drw_cur_create(drw, XC_sizing);
1985 cursor[CurMove] = drw_cur_create(drw, XC_fleur);
1986 /* init appearance */
1987 scheme = ecalloc(LENGTH(colors), sizeof(Clr *));
1988 for (i = 0; i < LENGTH(colors); i++)
1989 scheme[i] = drw_scm_create(drw, colors[i], 3);
1990 /* init system tray */
1991 updatesystray();
1992 /* init bars */
1993 updatebars();
1994 updatestatus();
1995 /* supporting window for NetWMCheck */
1996 wmcheckwin = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 0, 0, 0);
1997 XChangeProperty(dpy, wmcheckwin, netatom[NetWMCheck], XA_WINDOW, 32,
1998 PropModeReplace, (unsigned char *)&wmcheckwin, 1);
1999 XChangeProperty(dpy, wmcheckwin, netatom[NetWMName], utf8string, 8,
2000 PropModeReplace, (unsigned char *)"dwm", 3);
2001 XChangeProperty(dpy, root, netatom[NetWMCheck], XA_WINDOW, 32,
2002 PropModeReplace, (unsigned char *)&wmcheckwin, 1);
2003 /* EWMH support per view */
2004 XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32,
2005 PropModeReplace, (unsigned char *)netatom, NetLast);
2006 XDeleteProperty(dpy, root, netatom[NetClientList]);
2007 /* select events */
2008 wa.cursor = cursor[CurNormal]->cursor;
2009 wa.event_mask = SubstructureRedirectMask | SubstructureNotifyMask
2010 | ButtonPressMask | PointerMotionMask | EnterWindowMask
2011 | LeaveWindowMask | StructureNotifyMask | PropertyChangeMask;
2012 XChangeWindowAttributes(dpy, root, CWEventMask | CWCursor, &wa);
2013 XSelectInput(dpy, root, wa.event_mask);
2014 grabkeys();
2015 focus(NULL);
2016}
2017
2018void seturgent(Client *c, int urg)
2019{
2020 XWMHints *wmh;
2021
2022 c->isurgent = urg;
2023 if (!(wmh = XGetWMHints(dpy, c->win)))
2024 return;
2025 wmh->flags =
2026 urg ? (wmh->flags | XUrgencyHint) : (wmh->flags & ~XUrgencyHint);
2027 XSetWMHints(dpy, c->win, wmh);
2028 XFree(wmh);
2029}
2030
2031void showhide(Client *c)
2032{
2033 if (!c)
2034 return;
2035 if (ISVISIBLE(c)) {
2036 /* show clients top down */
2037 XMoveWindow(dpy, c->win, c->x, c->y);
2038 if ((!c->mon->lt[c->mon->sellt]->arrange || c->isfloating)
2039 && !c->isfullscreen)
2040 resize(c, c->x, c->y, c->w, c->h, 0);
2041 showhide(c->snext);
2042 } else {
2043 /* hide clients bottom up */
2044 showhide(c->snext);
2045 XMoveWindow(dpy, c->win, WIDTH(c) * -2, c->y);
2046 }
2047}
2048
2049void spawn(const Arg *arg)
2050{
2051 struct sigaction sa;
2052
2053 if (arg->v == dmenucmd)
2054 dmenumon[0] = '0' + selmon->num;
2055 if (fork() == 0) {
2056 if (dpy)
2057 close(ConnectionNumber(dpy));
2058 setsid();
2059
2060 sigemptyset(&sa.sa_mask);
2061 sa.sa_flags = 0;
2062 sa.sa_handler = SIG_DFL;
2063 sigaction(SIGCHLD, &sa, NULL);
2064
2065 execvp(((char **)arg->v)[0], (char **)arg->v);
2066 die("dwm: execvp '%s' failed:", ((char **)arg->v)[0]);
2067 }
2068}
2069
2070void tag(const Arg *arg)
2071{
2072 if (selmon->sel && arg->ui & TAGMASK) {
2073 selmon->sel->tags = arg->ui & TAGMASK;
2074 focus(NULL);
2075 arrange(selmon);
2076 }
2077}
2078
2079void tagmon(const Arg *arg)
2080{
2081 if (!selmon->sel || !mons->next)
2082 return;
2083 sendmon(selmon->sel, dirtomon(arg->i));
2084}
2085
2086void tile(Monitor *m)
2087{
2088 unsigned int i, n, h, r, oe = enablegaps, ie = enablegaps, mw, my, ty;
2089 Client *c;
2090
2091 for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) ;
2092 if (n == 0)
2093 return;
2094 if (smartgaps == n) {
2095 oe = 0; // outer gaps disabled
2096 }
2097
2098 if (n > m->nmaster)
2099 mw = m->nmaster ? (m->ww + m->gappiv * ie) * m->mfact : 0;
2100 else
2101 mw = m->ww - 2 * m->gappov * oe + m->gappiv * ie;
2102 for (i = 0, my = ty = m->gappoh * oe, c = nexttiled(m->clients); c;
2103 c = nexttiled(c->next), i++)
2104 if (i < m->nmaster) {
2105 r = MIN(n, m->nmaster) - i;
2106 h = (m->wh - my - m->gappoh * oe -
2107 m->gappih * ie * (r - 1)) / r;
2108 resize(c, m->wx + m->gappov * oe, m->wy + my,
2109 mw - (2 * c->bw) - m->gappiv * ie,
2110 h - (2 * c->bw), 0);
2111 my += HEIGHT(c) + m->gappih * ie;
2112
2113 if (my + HEIGHT(c) < m->wh)
2114 my += HEIGHT(c);
2115 } else {
2116 r = n - i;
2117 h = (m->wh - ty - m->gappoh * oe -
2118 m->gappih * ie * (r - 1)) / r;
2119 resize(c, m->wx + mw + m->gappov * oe, m->wy + ty,
2120 m->ww - mw - (2 * c->bw) - 2 * m->gappov * oe,
2121 h - (2 * c->bw), 0);
2122 ty += HEIGHT(c) + m->gappih * ie;
2123 }
2124}
2125
2126void togglebar(const Arg *arg)
2127{
2128 selmon->showbar = !selmon->showbar;
2129 updatebarpos(selmon);
2130 resizebarwin(selmon);
2131 if (showsystray) {
2132 XWindowChanges wc;
2133 if (!selmon->showbar)
2134 wc.y = -bh;
2135 else if (selmon->showbar) {
2136 wc.y = 0;
2137 if (!selmon->topbar)
2138 wc.y = selmon->mh - bh;
2139 }
2140 XConfigureWindow(dpy, systray->win, CWY, &wc);
2141 }
2142 arrange(selmon);
2143}
2144
2145void togglefloating(const Arg *arg)
2146{
2147 if (!selmon->sel)
2148 return;
2149 if (selmon->sel->isfullscreen) /* no support for fullscreen windows */
2150 return;
2151 selmon->sel->isfloating = !selmon->sel->isfloating
2152 || selmon->sel->isfixed;
2153 if (selmon->sel->isfloating)
2154 resize(selmon->sel, selmon->sel->x, selmon->sel->y,
2155 selmon->sel->w, selmon->sel->h, 0);
2156 arrange(selmon);
2157}
2158
2159void togglefullscr(const Arg *arg)
2160{
2161 if (selmon->sel)
2162 setfullscreen(selmon->sel, !selmon->sel->isfullscreen);
2163}
2164
2165void toggletag(const Arg *arg)
2166{
2167 unsigned int newtags;
2168
2169 if (!selmon->sel)
2170 return;
2171 newtags = selmon->sel->tags ^ (arg->ui & TAGMASK);
2172 if (newtags) {
2173 selmon->sel->tags = newtags;
2174 focus(NULL);
2175 arrange(selmon);
2176 }
2177}
2178
2179void toggleview(const Arg *arg)
2180{
2181 unsigned int newtagset =
2182 selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK);
2183
2184 if (newtagset) {
2185 selmon->tagset[selmon->seltags] = newtagset;
2186 focus(NULL);
2187 arrange(selmon);
2188 }
2189}
2190
2191void unfocus(Client *c, int setfocus)
2192{
2193 if (!c)
2194 return;
2195 grabbuttons(c, 0);
2196 XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel);
2197 if (setfocus) {
2198 XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
2199 XDeleteProperty(dpy, root, netatom[NetActiveWindow]);
2200 }
2201}
2202
2203void unmanage(Client *c, int destroyed)
2204{
2205 Monitor *m = c->mon;
2206 XWindowChanges wc;
2207
2208 if (c->swallowing) {
2209 unswallow(c);
2210 return;
2211 }
2212
2213 Client *s = swallowingclient(c->win);
2214 if (s) {
2215 free(s->swallowing);
2216 s->swallowing = NULL;
2217 arrange(m);
2218 focus(NULL);
2219 return;
2220 }
2221
2222 detach(c);
2223 detachstack(c);
2224 if (!destroyed) {
2225 wc.border_width = c->oldbw;
2226 XGrabServer(dpy); /* avoid race conditions */
2227 XSetErrorHandler(xerrordummy);
2228 XSelectInput(dpy, c->win, NoEventMask);
2229 XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */
2230 XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
2231 setclientstate(c, WithdrawnState);
2232 XSync(dpy, False);
2233 XSetErrorHandler(xerror);
2234 XUngrabServer(dpy);
2235 }
2236 free(c);
2237
2238 if (!s) {
2239 arrange(m);
2240 focus(NULL);
2241 updateclientlist();
2242 }
2243}
2244
2245void unmapnotify(XEvent *e)
2246{
2247 Client *c;
2248 XUnmapEvent *ev = &e->xunmap;
2249
2250 if ((c = wintoclient(ev->window))) {
2251 if (ev->send_event)
2252 setclientstate(c, WithdrawnState);
2253 else
2254 unmanage(c, 0);
2255 } else if ((c = wintosystrayicon(ev->window))) {
2256 /* KLUDGE! sometimes icons occasionally unmap their windows, but do
2257 * _not_ destroy them. We map those windows back */
2258 XMapRaised(dpy, c->win);
2259 updatesystray();
2260 }
2261}
2262
2263void updatebars(void)
2264{
2265 unsigned int w;
2266 Monitor *m;
2267 XSetWindowAttributes wa = {
2268 .override_redirect = True,
2269 .background_pixmap = ParentRelative,
2270 .event_mask = ButtonPressMask | ExposureMask
2271 };
2272 XClassHint ch = { "dwm", "dwm" };
2273 for (m = mons; m; m = m->next) {
2274 if (m->barwin)
2275 continue;
2276 w = m->ww;
2277 if (showsystray && m == systraytomon(m))
2278 w -= getsystraywidth();
2279 m->barwin =
2280 XCreateWindow(dpy, root, m->wx, m->by, w, bh, 0,
2281 DefaultDepth(dpy, screen), CopyFromParent,
2282 DefaultVisual(dpy, screen),
2283 CWOverrideRedirect | CWBackPixmap |
2284 CWEventMask, &wa);
2285 XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor);
2286 if (showsystray && m == systraytomon(m))
2287 XMapRaised(dpy, systray->win);
2288 XMapRaised(dpy, m->barwin);
2289 XSetClassHint(dpy, m->barwin, &ch);
2290 }
2291}
2292
2293void updatebarpos(Monitor *m)
2294{
2295 m->wy = m->my;
2296 m->wh = m->mh;
2297 if (m->showbar) {
2298 m->wh -= bh;
2299 m->by = m->topbar ? m->wy : m->wy + m->wh;
2300 m->wy = m->topbar ? m->wy + bh : m->wy;
2301 } else
2302 m->by = -bh;
2303}
2304
2305void updateclientlist()
2306{
2307 Client *c;
2308 Monitor *m;
2309
2310 XDeleteProperty(dpy, root, netatom[NetClientList]);
2311 for (m = mons; m; m = m->next)
2312 for (c = m->clients; c; c = c->next)
2313 XChangeProperty(dpy, root, netatom[NetClientList],
2314 XA_WINDOW, 32, PropModeAppend,
2315 (unsigned char *)&(c->win), 1);
2316}
2317
2318int updategeom(void)
2319{
2320 int dirty = 0;
2321
2322#ifdef XINERAMA
2323 if (XineramaIsActive(dpy)) {
2324 int i, j, n, nn;
2325 Client *c;
2326 Monitor *m;
2327 XineramaScreenInfo *info = XineramaQueryScreens(dpy, &nn);
2328 XineramaScreenInfo *unique = NULL;
2329
2330 for (n = 0, m = mons; m; m = m->next, n++) ;
2331 /* only consider unique geometries as separate screens */
2332 unique = ecalloc(nn, sizeof(XineramaScreenInfo));
2333 for (i = 0, j = 0; i < nn; i++)
2334 if (isuniquegeom(unique, j, &info[i]))
2335 memcpy(&unique[j++], &info[i],
2336 sizeof(XineramaScreenInfo));
2337 XFree(info);
2338 nn = j;
2339
2340 /* new monitors if nn > n */
2341 for (i = n; i < nn; i++) {
2342 for (m = mons; m && m->next; m = m->next) ;
2343 if (m)
2344 m->next = createmon();
2345 else
2346 mons = createmon();
2347 }
2348 for (i = 0, m = mons; i < nn && m; m = m->next, i++)
2349 if (i >= n
2350 || unique[i].x_org != m->mx
2351 || unique[i].y_org != m->my
2352 || unique[i].width != m->mw
2353 || unique[i].height != m->mh) {
2354 dirty = 1;
2355 m->num = i;
2356 m->mx = m->wx = unique[i].x_org;
2357 m->my = m->wy = unique[i].y_org;
2358 m->mw = m->ww = unique[i].width;
2359 m->mh = m->wh = unique[i].height;
2360 updatebarpos(m);
2361 }
2362 /* removed monitors if n > nn */
2363 for (i = nn; i < n; i++) {
2364 for (m = mons; m && m->next; m = m->next) ;
2365 while ((c = m->clients)) {
2366 dirty = 1;
2367 m->clients = c->next;
2368 detachstack(c);
2369 c->mon = mons;
2370 attach(c);
2371 attachstack(c);
2372 }
2373 if (m == selmon)
2374 selmon = mons;
2375 cleanupmon(m);
2376 }
2377 free(unique);
2378 } else
2379#endif /* XINERAMA */
2380 { /* default monitor setup */
2381 if (!mons)
2382 mons = createmon();
2383 if (mons->mw != sw || mons->mh != sh) {
2384 dirty = 1;
2385 mons->mw = mons->ww = sw;
2386 mons->mh = mons->wh = sh;
2387 updatebarpos(mons);
2388 }
2389 }
2390 if (dirty) {
2391 selmon = mons;
2392 selmon = wintomon(root);
2393 }
2394 return dirty;
2395}
2396
2397void updatenumlockmask(void)
2398{
2399 unsigned int i, j;
2400 XModifierKeymap *modmap;
2401
2402 numlockmask = 0;
2403 modmap = XGetModifierMapping(dpy);
2404 for (i = 0; i < 8; i++)
2405 for (j = 0; j < modmap->max_keypermod; j++)
2406 if (modmap->modifiermap[i * modmap->max_keypermod + j]
2407 == XKeysymToKeycode(dpy, XK_Num_Lock))
2408 numlockmask = (1 << i);
2409 XFreeModifiermap(modmap);
2410}
2411
2412void updatesizehints(Client *c)
2413{
2414 long msize;
2415 XSizeHints size;
2416
2417 if (!XGetWMNormalHints(dpy, c->win, &size, &msize))
2418 /* size is uninitialized, ensure that size.flags aren't used */
2419 size.flags = PSize;
2420 if (size.flags & PBaseSize) {
2421 c->basew = size.base_width;
2422 c->baseh = size.base_height;
2423 } else if (size.flags & PMinSize) {
2424 c->basew = size.min_width;
2425 c->baseh = size.min_height;
2426 } else
2427 c->basew = c->baseh = 0;
2428 if (size.flags & PResizeInc) {
2429 c->incw = size.width_inc;
2430 c->inch = size.height_inc;
2431 } else
2432 c->incw = c->inch = 0;
2433 if (size.flags & PMaxSize) {
2434 c->maxw = size.max_width;
2435 c->maxh = size.max_height;
2436 } else
2437 c->maxw = c->maxh = 0;
2438 if (size.flags & PMinSize) {
2439 c->minw = size.min_width;
2440 c->minh = size.min_height;
2441 } else if (size.flags & PBaseSize) {
2442 c->minw = size.base_width;
2443 c->minh = size.base_height;
2444 } else
2445 c->minw = c->minh = 0;
2446 if (size.flags & PAspect) {
2447 c->mina = (float)size.min_aspect.y / size.min_aspect.x;
2448 c->maxa = (float)size.max_aspect.x / size.max_aspect.y;
2449 } else
2450 c->maxa = c->mina = 0.0;
2451 c->isfixed = (c->maxw && c->maxh && c->maxw == c->minw
2452 && c->maxh == c->minh);
2453 c->hintsvalid = 1;
2454}
2455
2456void updatestatus(void)
2457{
2458 if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext)))
2459 strcpy(stext, "dwm-" VERSION);
2460 drawbar(selmon);
2461 updatesystray();
2462}
2463
2464void updatesystrayicongeom(Client *i, int w, int h)
2465{
2466 if (i) {
2467 i->h = bh;
2468 if (w == h)
2469 i->w = bh;
2470 else if (h == bh)
2471 i->w = w;
2472 else
2473 i->w = (int)((float)bh * ((float)w / (float)h));
2474 applysizehints(i, &(i->x), &(i->y), &(i->w), &(i->h), False);
2475 /* force icons into the systray dimensions if they don't want to */
2476 if (i->h > bh) {
2477 if (i->w == i->h)
2478 i->w = bh;
2479 else
2480 i->w =
2481 (int)((float)bh *
2482 ((float)i->w / (float)i->h));
2483 i->h = bh;
2484 }
2485 }
2486}
2487
2488void updatesystrayiconstate(Client *i, XPropertyEvent *ev)
2489{
2490 long flags;
2491 int code = 0;
2492
2493 if (!showsystray || !i || ev->atom != xatom[XembedInfo] ||
2494 !(flags = getatomprop(i, xatom[XembedInfo])))
2495 return;
2496
2497 if (flags & XEMBED_MAPPED && !i->tags) {
2498 i->tags = 1;
2499 code = XEMBED_WINDOW_ACTIVATE;
2500 XMapRaised(dpy, i->win);
2501 setclientstate(i, NormalState);
2502 } else if (!(flags & XEMBED_MAPPED) && i->tags) {
2503 i->tags = 0;
2504 code = XEMBED_WINDOW_DEACTIVATE;
2505 XUnmapWindow(dpy, i->win);
2506 setclientstate(i, WithdrawnState);
2507 } else
2508 return;
2509 sendevent(i->win, xatom[Xembed], StructureNotifyMask, CurrentTime, code,
2510 0, systray->win, XEMBED_EMBEDDED_VERSION);
2511}
2512
2513void updatesystray(void)
2514{
2515 XSetWindowAttributes wa;
2516 XWindowChanges wc;
2517 Client *i;
2518 Monitor *m = systraytomon(NULL);
2519 unsigned int x = m->mx + m->mw;
2520 unsigned int sw = TEXTW(stext) - lrpad + systrayspacing;
2521 unsigned int w = 1;
2522
2523 if (!showsystray)
2524 return;
2525 if (systrayonleft)
2526 x -= sw + lrpad / 2;
2527 if (!systray) {
2528 /* init systray */
2529 if (!(systray = (Systray *) calloc(1, sizeof(Systray))))
2530 die("fatal: could not malloc() %u bytes\n",
2531 sizeof(Systray));
2532 systray->win =
2533 XCreateSimpleWindow(dpy, root, x, m->by, w, bh, 0, 0,
2534 scheme[SchemeSel][ColBg].pixel);
2535 wa.event_mask = ButtonPressMask | ExposureMask;
2536 wa.override_redirect = True;
2537 wa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
2538 XSelectInput(dpy, systray->win, SubstructureNotifyMask);
2539 XChangeProperty(dpy, systray->win,
2540 netatom[NetSystemTrayOrientation], XA_CARDINAL,
2541 32, PropModeReplace,
2542 (unsigned char *)
2543 &netatom[NetSystemTrayOrientationHorz], 1);
2544 XChangeWindowAttributes(dpy, systray->win,
2545 CWEventMask | CWOverrideRedirect |
2546 CWBackPixel, &wa);
2547 XMapRaised(dpy, systray->win);
2548 XSetSelectionOwner(dpy, netatom[NetSystemTray], systray->win,
2549 CurrentTime);
2550 if (XGetSelectionOwner(dpy, netatom[NetSystemTray]) ==
2551 systray->win) {
2552 sendevent(root, xatom[Manager], StructureNotifyMask,
2553 CurrentTime, netatom[NetSystemTray],
2554 systray->win, 0, 0);
2555 XSync(dpy, False);
2556 } else {
2557 fprintf(stderr, "dwm: unable to obtain system tray.\n");
2558 free(systray);
2559 systray = NULL;
2560 return;
2561 }
2562 }
2563 for (w = 0, i = systray->icons; i; i = i->next) {
2564 /* make sure the background color stays the same */
2565 wa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
2566 XChangeWindowAttributes(dpy, i->win, CWBackPixel, &wa);
2567 XMapRaised(dpy, i->win);
2568 w += systrayspacing;
2569 i->x = w;
2570 XMoveResizeWindow(dpy, i->win, i->x, 0, i->w, i->h);
2571 w += i->w;
2572 if (i->mon != m)
2573 i->mon = m;
2574 }
2575 w = w ? w + systrayspacing : 1;
2576 x -= w;
2577 XMoveResizeWindow(dpy, systray->win, x, m->by, w, bh);
2578 wc.x = x;
2579 wc.y = m->by;
2580 wc.width = w;
2581 wc.height = bh;
2582 wc.stack_mode = Above;
2583 wc.sibling = m->barwin;
2584 XConfigureWindow(dpy, systray->win,
2585 CWX | CWY | CWWidth | CWHeight | CWSibling |
2586 CWStackMode, &wc);
2587 XMapWindow(dpy, systray->win);
2588 XMapSubwindows(dpy, systray->win);
2589 /* redraw background */
2590 XSetForeground(dpy, drw->gc, scheme[SchemeNorm][ColBg].pixel);
2591 XFillRectangle(dpy, systray->win, drw->gc, 0, 0, w, bh);
2592 XSync(dpy, False);
2593}
2594
2595void updatetitle(Client *c)
2596{
2597 if (!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name))
2598 gettextprop(c->win, XA_WM_NAME, c->name, sizeof c->name);
2599 if (c->name[0] == '\0') /* hack to mark broken clients */
2600 strcpy(c->name, broken);
2601}
2602
2603void updatewindowtype(Client *c)
2604{
2605 Atom state = getatomprop(c, netatom[NetWMState]);
2606 Atom wtype = getatomprop(c, netatom[NetWMWindowType]);
2607
2608 if (state == netatom[NetWMFullscreen])
2609 setfullscreen(c, 1);
2610 if (wtype == netatom[NetWMWindowTypeDialog])
2611 c->isfloating = 1;
2612}
2613
2614void updatewmhints(Client *c)
2615{
2616 XWMHints *wmh;
2617
2618 if ((wmh = XGetWMHints(dpy, c->win))) {
2619 if (c == selmon->sel && wmh->flags & XUrgencyHint) {
2620 wmh->flags &= ~XUrgencyHint;
2621 XSetWMHints(dpy, c->win, wmh);
2622 } else
2623 c->isurgent = (wmh->flags & XUrgencyHint) ? 1 : 0;
2624 if (wmh->flags & InputHint)
2625 c->neverfocus = !wmh->input;
2626 else
2627 c->neverfocus = 0;
2628 XFree(wmh);
2629 }
2630}
2631
2632void view(const Arg *arg)
2633{
2634 if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags])
2635 return;
2636 selmon->seltags ^= 1; /* toggle sel tagset */
2637 if (arg->ui & TAGMASK)
2638 selmon->tagset[selmon->seltags] = arg->ui & TAGMASK;
2639 focus(NULL);
2640 arrange(selmon);
2641}
2642
2643pid_t winpid(Window w)
2644{
2645
2646 pid_t result = 0;
2647
2648#ifdef __linux__
2649 xcb_res_client_id_spec_t spec = { 0 };
2650 spec.client = w;
2651 spec.mask = XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID;
2652
2653 xcb_generic_error_t *e = NULL;
2654 xcb_res_query_client_ids_cookie_t c =
2655 xcb_res_query_client_ids(xcon, 1, &spec);
2656 xcb_res_query_client_ids_reply_t *r =
2657 xcb_res_query_client_ids_reply(xcon, c, &e);
2658
2659 if (!r)
2660 return (pid_t) 0;
2661
2662 xcb_res_client_id_value_iterator_t i =
2663 xcb_res_query_client_ids_ids_iterator(r);
2664 for (; i.rem; xcb_res_client_id_value_next(&i)) {
2665 spec = i.data->spec;
2666 if (spec.mask & XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID) {
2667 uint32_t *t = xcb_res_client_id_value_value(i.data);
2668 result = *t;
2669 break;
2670 }
2671 }
2672
2673 free(r);
2674
2675 if (result == (pid_t) - 1)
2676 result = 0;
2677
2678#endif /* __linux__ */
2679
2680#ifdef __OpenBSD__
2681 Atom type;
2682 int format;
2683 unsigned long len, bytes;
2684 unsigned char *prop;
2685 pid_t ret;
2686
2687 if (XGetWindowProperty
2688 (dpy, w, XInternAtom(dpy, "_NET_WM_PID", 0), 0, 1, False,
2689 AnyPropertyType, &type, &format, &len, &bytes, &prop) != Success
2690 || !prop)
2691 return 0;
2692
2693 ret = *(pid_t *) prop;
2694 XFree(prop);
2695 result = ret;
2696
2697#endif /* __OpenBSD__ */
2698 return result;
2699}
2700
2701pid_t getparentprocess(pid_t p)
2702{
2703 unsigned int v = 0;
2704
2705#ifdef __linux__
2706 FILE *f;
2707 char buf[256];
2708 snprintf(buf, sizeof(buf) - 1, "/proc/%u/stat", (unsigned)p);
2709
2710 if (!(f = fopen(buf, "r")))
2711 return 0;
2712
2713 fscanf(f, "%*u %*s %*c %u", &v);
2714 fclose(f);
2715#endif /* __linux__ */
2716
2717#ifdef __OpenBSD__
2718 int n;
2719 kvm_t *kd;
2720 struct kinfo_proc *kp;
2721
2722 kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, NULL);
2723 if (!kd)
2724 return 0;
2725
2726 kp = kvm_getprocs(kd, KERN_PROC_PID, p, sizeof(*kp), &n);
2727 v = kp->p_ppid;
2728#endif /* __OpenBSD__ */
2729
2730 return (pid_t) v;
2731}
2732
2733int isdescprocess(pid_t p, pid_t c)
2734{
2735 while (p != c && c != 0)
2736 c = getparentprocess(c);
2737
2738 return (int)c;
2739}
2740
2741Client *termforwin(const Client *w)
2742{
2743 Client *c;
2744 Monitor *m;
2745
2746 if (!w->pid || w->isterminal)
2747 return NULL;
2748
2749 for (m = mons; m; m = m->next) {
2750 for (c = m->clients; c; c = c->next) {
2751 if (c->isterminal && !c->swallowing && c->pid
2752 && isdescprocess(c->pid, w->pid))
2753 return c;
2754 }
2755 }
2756
2757 return NULL;
2758}
2759
2760Client *swallowingclient(Window w)
2761{
2762 Client *c;
2763 Monitor *m;
2764
2765 for (m = mons; m; m = m->next) {
2766 for (c = m->clients; c; c = c->next) {
2767 if (c->swallowing && c->swallowing->win == w)
2768 return c;
2769 }
2770 }
2771
2772 return NULL;
2773}
2774
2775Client *wintoclient(Window w)
2776{
2777 Client *c;
2778 Monitor *m;
2779
2780 for (m = mons; m; m = m->next)
2781 for (c = m->clients; c; c = c->next)
2782 if (c->win == w)
2783 return c;
2784 return NULL;
2785}
2786
2787Client *wintosystrayicon(Window w)
2788{
2789 Client *i = NULL;
2790
2791 if (!showsystray || !w)
2792 return i;
2793 for (i = systray->icons; i && i->win != w; i = i->next) ;
2794 return i;
2795}
2796
2797Monitor *wintomon(Window w)
2798{
2799 int x, y;
2800 Client *c;
2801 Monitor *m;
2802
2803 if (w == root && getrootptr(&x, &y))
2804 return recttomon(x, y, 1, 1);
2805 for (m = mons; m; m = m->next)
2806 if (w == m->barwin)
2807 return m;
2808 if ((c = wintoclient(w)))
2809 return c->mon;
2810 return selmon;
2811}
2812
2813/* There's no way to check accesses to destroyed windows, thus those cases are
2814 * ignored (especially on UnmapNotify's). Other types of errors call Xlibs
2815 * default error handler, which may call exit. */
2816int xerror(Display *dpy, XErrorEvent *ee)
2817{
2818 if (ee->error_code == BadWindow
2819 || (ee->request_code == X_SetInputFocus
2820 && ee->error_code == BadMatch)
2821 || (ee->request_code == X_PolyText8
2822 && ee->error_code == BadDrawable)
2823 || (ee->request_code == X_PolyFillRectangle
2824 && ee->error_code == BadDrawable)
2825 || (ee->request_code == X_PolySegment
2826 && ee->error_code == BadDrawable)
2827 || (ee->request_code == X_ConfigureWindow
2828 && ee->error_code == BadMatch)
2829 || (ee->request_code == X_GrabButton && ee->error_code == BadAccess)
2830 || (ee->request_code == X_GrabKey && ee->error_code == BadAccess)
2831 || (ee->request_code == X_CopyArea
2832 && ee->error_code == BadDrawable))
2833 return 0;
2834 fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n",
2835 ee->request_code, ee->error_code);
2836 return xerrorxlib(dpy, ee); /* may call exit */
2837}
2838
2839int xerrordummy(Display *dpy, XErrorEvent *ee)
2840{
2841 return 0;
2842}
2843
2844/* Startup Error handler to check if another window manager
2845 * is already running. */
2846int xerrorstart(Display *dpy, XErrorEvent *ee)
2847{
2848 die("dwm: another window manager is already running");
2849 return -1;
2850}
2851
2852Monitor *systraytomon(Monitor *m)
2853{
2854 Monitor *t;
2855 int i, n;
2856 if (!systraypinning) {
2857 if (!m)
2858 return selmon;
2859 return m == selmon ? m : NULL;
2860 }
2861 for (n = 1, t = mons; t && t->next; n++, t = t->next) ;
2862 for (i = 1, t = mons; t && t->next && i < systraypinning;
2863 i++, t = t->next) ;
2864 if (systraypinningfailfirst && n < systraypinning)
2865 return mons;
2866 return t;
2867}
2868
2869void zoom(const Arg *arg)
2870{
2871 Client *c = selmon->sel;
2872
2873 if (!selmon->lt[selmon->sellt]->arrange || !c || c->isfloating)
2874 return;
2875 if (c == nexttiled(selmon->clients) && !(c = nexttiled(c->next)))
2876 return;
2877 pop(c);
2878}
2879
2880int main(int argc, char *argv[])
2881{
2882 if (argc == 2 && !strcmp("-v", argv[1]))
2883 die("dwm-" VERSION);
2884 else if (argc != 1)
2885 die("usage: dwm [-v]");
2886 if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())
2887 fputs("warning: no locale support\n", stderr);
2888 if (!(dpy = XOpenDisplay(NULL)))
2889 die("dwm: cannot open display");
2890 if (!(xcon = XGetXCBConnection(dpy)))
2891 die("dwm: cannot get xcb connection\n");
2892 checkotherwm();
2893 setup();
2894#ifdef __OpenBSD__
2895 if (pledge("stdio rpath proc exec ps", NULL) == -1)
2896 die("pledge");
2897#endif /* __OpenBSD__ */
2898 scan();
2899 run();
2900 cleanup();
2901 XCloseDisplay(dpy);
2902 return EXIT_SUCCESS;
2903}
diff --git a/dwm.c~ b/dwm.c~
deleted file mode 100644
index 2680e02..0000000
--- a/dwm.c~
+++ /dev/null
@@ -1,2884 +0,0 @@
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 <signal.h>
26#include <stdarg.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <unistd.h>
31#include <sys/types.h>
32#include <sys/wait.h>
33#include <X11/cursorfont.h>
34#include <X11/keysym.h>
35#include <X11/Xatom.h>
36#include <X11/Xlib.h>
37#include <X11/Xproto.h>
38#include <X11/Xutil.h>
39#ifdef XINERAMA
40#include <X11/extensions/Xinerama.h>
41#endif /* XINERAMA */
42#include <X11/Xft/Xft.h>
43#include <X11/Xlib-xcb.h>
44#include <xcb/res.h>
45#ifdef __OpenBSD__
46#include <sys/sysctl.h>
47#include <kvm.h>
48#endif /* __OpenBSD */
49
50#include "drw.h"
51#include "util.h"
52
53/* macros */
54#define BUTTONMASK (ButtonPressMask|ButtonReleaseMask)
55#define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask))
56#define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \
57 * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy)))
58#define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags]))
59#define LENGTH(X) (sizeof X / sizeof X[0])
60#define MOUSEMASK (BUTTONMASK|PointerMotionMask)
61#define WIDTH(X) ((X)->w + 2 * (X)->bw)
62#define HEIGHT(X) ((X)->h + 2 * (X)->bw)
63#define TAGMASK ((1 << LENGTH(tags)) - 1)
64#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad)
65
66#define SYSTEM_TRAY_REQUEST_DOCK 0
67/* XEMBED messages */
68#define XEMBED_EMBEDDED_NOTIFY 0
69#define XEMBED_WINDOW_ACTIVATE 1
70#define XEMBED_FOCUS_IN 4
71#define XEMBED_MODALITY_ON 10
72#define XEMBED_MAPPED (1 << 0)
73#define XEMBED_WINDOW_ACTIVATE 1
74#define XEMBED_WINDOW_DEACTIVATE 2
75#define VERSION_MAJOR 0
76#define VERSION_MINOR 0
77#define XEMBED_EMBEDDED_VERSION (VERSION_MAJOR << 16) | VERSION_MINOR
78
79/* enums */
80enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */
81enum { SchemeNorm, SchemeSel }; /* color schemes */
82enum { NetSupported, NetWMName, NetWMState, NetWMCheck,
83 NetSystemTray, NetSystemTrayOP, NetSystemTrayOrientation, NetSystemTrayOrientationHorz,
84 NetWMFullscreen, NetActiveWindow, NetWMWindowType,
85 NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */
86enum { Manager, Xembed, XembedInfo, XLast }; /* Xembed atoms */
87enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */
88enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle,
89 ClkClientWin, ClkRootWin, ClkLast }; /* clicks */
90
91typedef union {
92 int i;
93 unsigned int ui;
94 float f;
95 const void *v;
96} Arg;
97
98typedef struct {
99 unsigned int click;
100 unsigned int mask;
101 unsigned int button;
102 void (*func)(const Arg *arg);
103 const Arg arg;
104} Button;
105
106typedef struct Monitor Monitor;
107typedef struct Client Client;
108struct Client {
109 char name[256];
110 float mina, maxa;
111 int x, y, w, h;
112 int oldx, oldy, oldw, oldh;
113 int basew, baseh, incw, inch, maxw, maxh, minw, minh, hintsvalid;
114 int bw, oldbw;
115 unsigned int tags;
116 int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen, isterminal, noswallow;
117 pid_t pid;
118 Client *next;
119 Client *snext;
120 Client *swallowing;
121 Monitor *mon;
122 Window win;
123};
124
125typedef struct {
126 unsigned int mod;
127 KeySym keysym;
128 void (*func)(const Arg *);
129 const Arg arg;
130} Key;
131
132typedef struct {
133 const char *symbol;
134 void (*arrange)(Monitor *);
135} Layout;
136
137struct Monitor {
138 char ltsymbol[16];
139 float mfact;
140 int nmaster;
141 int num;
142 int by; /* bar geometry */
143 int mx, my, mw, mh; /* screen size */
144 int wx, wy, ww, wh; /* window area */
145 int gappih; /* horizontal gap between windows */
146 int gappiv; /* vertical gap between windows */
147 int gappoh; /* horizontal outer gaps */
148 int gappov; /* vertical outer gaps */
149 unsigned int seltags;
150 unsigned int sellt;
151 unsigned int tagset[2];
152 int showbar;
153 int topbar;
154 Client *clients;
155 Client *sel;
156 Client *stack;
157 Monitor *next;
158 Window barwin;
159 const Layout *lt[2];
160};
161
162typedef struct {
163 const char *class;
164 const char *instance;
165 const char *title;
166 unsigned int tags;
167 int isfloating;
168 int isterminal;
169 int noswallow;
170 int monitor;
171} Rule;
172
173typedef struct Systray Systray;
174struct Systray {
175 Window win;
176 Client *icons;
177};
178
179/* function declarations */
180static void applyrules(Client *c);
181static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact);
182static void arrange(Monitor *m);
183static void arrangemon(Monitor *m);
184static void attach(Client *c);
185static void attachstack(Client *c);
186static void buttonpress(XEvent *e);
187static void checkotherwm(void);
188static void cleanup(void);
189static void cleanupmon(Monitor *mon);
190static void clientmessage(XEvent *e);
191static void configure(Client *c);
192static void configurenotify(XEvent *e);
193static void configurerequest(XEvent *e);
194static Monitor *createmon(void);
195static void destroynotify(XEvent *e);
196static void detach(Client *c);
197static void detachstack(Client *c);
198static Monitor *dirtomon(int dir);
199static void drawbar(Monitor *m);
200static void drawbars(void);
201static void enternotify(XEvent *e);
202static void expose(XEvent *e);
203static void focus(Client *c);
204static void focusin(XEvent *e);
205static void focusmon(const Arg *arg);
206static void focusstack(const Arg *arg);
207static Atom getatomprop(Client *c, Atom prop);
208static int getrootptr(int *x, int *y);
209static long getstate(Window w);
210static unsigned int getsystraywidth();
211static int gettextprop(Window w, Atom atom, char *text, unsigned int size);
212static void grabbuttons(Client *c, int focused);
213static void grabkeys(void);
214static void incnmaster(const Arg *arg);
215static void keypress(XEvent *e);
216static void killclient(const Arg *arg);
217static void manage(Window w, XWindowAttributes *wa);
218static void mappingnotify(XEvent *e);
219static void maprequest(XEvent *e);
220static void monocle(Monitor *m);
221static void motionnotify(XEvent *e);
222static void movemouse(const Arg *arg);
223static Client *nexttiled(Client *c);
224static void pop(Client *c);
225static void propertynotify(XEvent *e);
226static void quit(const Arg *arg);
227static Monitor *recttomon(int x, int y, int w, int h);
228static void removesystrayicon(Client *i);
229static void resize(Client *c, int x, int y, int w, int h, int interact);
230static void resizebarwin(Monitor *m);
231static void resizeclient(Client *c, int x, int y, int w, int h);
232static void resizemouse(const Arg *arg);
233static void resizerequest(XEvent *e);
234static void restack(Monitor *m);
235static void run(void);
236static void scan(void);
237static int sendevent(Window w, Atom proto, int m, long d0, long d1, long d2, long d3, long d4);
238static void sendmon(Client *c, Monitor *m);
239static void setclientstate(Client *c, long state);
240static void setfocus(Client *c);
241static void setfullscreen(Client *c, int fullscreen);
242static void setgaps(int oh, int ov, int ih, int iv);
243static void incrgaps(const Arg *arg);
244static void incrigaps(const Arg *arg);
245static void incrogaps(const Arg *arg);
246static void incrohgaps(const Arg *arg);
247static void incrovgaps(const Arg *arg);
248static void incrihgaps(const Arg *arg);
249static void incrivgaps(const Arg *arg);
250static void togglegaps(const Arg *arg);
251static void defaultgaps(const Arg *arg);
252static void setlayout(const Arg *arg);
253static void setmfact(const Arg *arg);
254static void setup(void);
255static void seturgent(Client *c, int urg);
256static void showhide(Client *c);
257static void spawn(const Arg *arg);
258static Monitor *systraytomon(Monitor *m);
259static void tag(const Arg *arg);
260static void tagmon(const Arg *arg);
261static void tile(Monitor *m);
262static void togglebar(const Arg *arg);
263static void togglefloating(const Arg *arg);
264static void togglefullscr(const Arg *arg);
265static void toggletag(const Arg *arg);
266static void toggleview(const Arg *arg);
267static void unfocus(Client *c, int setfocus);
268static void unmanage(Client *c, int destroyed);
269static void unmapnotify(XEvent *e);
270static void updatebarpos(Monitor *m);
271static void updatebars(void);
272static void updateclientlist(void);
273static int updategeom(void);
274static void updatenumlockmask(void);
275static void updatesystray(void);
276static void updatesystrayicongeom(Client *i, int w, int h);
277static void updatesystrayiconstate(Client *i, XPropertyEvent *ev);
278static void updatesizehints(Client *c);
279static void updatestatus(void);
280static void updatetitle(Client *c);
281static void updatewindowtype(Client *c);
282static void updatewmhints(Client *c);
283static void view(const Arg *arg);
284static Client *wintoclient(Window w);
285static Monitor *wintomon(Window w);
286static Client *wintosystrayicon(Window w);
287static int xerror(Display *dpy, XErrorEvent *ee);
288static int xerrordummy(Display *dpy, XErrorEvent *ee);
289static int xerrorstart(Display *dpy, XErrorEvent *ee);
290static void zoom(const Arg *arg);
291
292static pid_t getparentprocess(pid_t p);
293static int isdescprocess(pid_t p, pid_t c);
294static Client *swallowingclient(Window w);
295static Client *termforwin(const Client *c);
296static pid_t winpid(Window w);
297
298/* variables */
299static Systray *systray = NULL;
300static const char broken[] = "broken";
301static char stext[256];
302static int screen;
303static int sw, sh; /* X display screen geometry width, height */
304static int bh; /* bar height */
305static int enablegaps = 1; /* enables gaps, used by togglegaps */
306static int lrpad; /* sum of left and right padding for text */
307static int (*xerrorxlib)(Display *, XErrorEvent *);
308static unsigned int numlockmask = 0;
309static void (*handler[LASTEvent]) (XEvent *) = {
310 [ButtonPress] = buttonpress,
311 [ClientMessage] = clientmessage,
312 [ConfigureRequest] = configurerequest,
313 [ConfigureNotify] = configurenotify,
314 [DestroyNotify] = destroynotify,
315 [EnterNotify] = enternotify,
316 [Expose] = expose,
317 [FocusIn] = focusin,
318 [KeyPress] = keypress,
319 [MappingNotify] = mappingnotify,
320 [MapRequest] = maprequest,
321 [MotionNotify] = motionnotify,
322 [PropertyNotify] = propertynotify,
323 [ResizeRequest] = resizerequest,
324 [UnmapNotify] = unmapnotify
325};
326static Atom wmatom[WMLast], netatom[NetLast], xatom[XLast];
327static int running = 1;
328static Cur *cursor[CurLast];
329static Clr **scheme;
330static Display *dpy;
331static Drw *drw;
332static Monitor *mons, *selmon;
333static Window root, wmcheckwin;
334
335static xcb_connection_t *xcon;
336
337/* configuration, allows nested code to access above variables */
338#include "config.h"
339
340/* compile-time check if all tags fit into an unsigned int bit array. */
341struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; };
342
343/* function implementations */
344void
345applyrules(Client *c)
346{
347 const char *class, *instance;
348 unsigned int i;
349 const Rule *r;
350 Monitor *m;
351 XClassHint ch = { NULL, NULL };
352
353 /* rule matching */
354 c->isfloating = 0;
355 c->tags = 0;
356 XGetClassHint(dpy, c->win, &ch);
357 class = ch.res_class ? ch.res_class : broken;
358 instance = ch.res_name ? ch.res_name : broken;
359
360 for (i = 0; i < LENGTH(rules); i++) {
361 r = &rules[i];
362 if ((!r->title || strstr(c->name, r->title))
363 && (!r->class || strstr(class, r->class))
364 && (!r->instance || strstr(instance, r->instance)))
365 {
366 c->isterminal = r->isterminal;
367 c->noswallow = r->noswallow;
368 c->isfloating = r->isfloating;
369 c->tags |= r->tags;
370 for (m = mons; m && m->num != r->monitor; m = m->next);
371 if (m)
372 c->mon = m;
373 }
374 }
375 if (ch.res_class)
376 XFree(ch.res_class);
377 if (ch.res_name)
378 XFree(ch.res_name);
379 c->tags = c->tags & TAGMASK ? c->tags & TAGMASK : c->mon->tagset[c->mon->seltags];
380}
381
382int
383applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact)
384{
385 int baseismin;
386 Monitor *m = c->mon;
387
388 /* set minimum possible */
389 *w = MAX(1, *w);
390 *h = MAX(1, *h);
391 if (interact) {
392 if (*x > sw)
393 *x = sw - WIDTH(c);
394 if (*y > sh)
395 *y = sh - HEIGHT(c);
396 if (*x + *w + 2 * c->bw < 0)
397 *x = 0;
398 if (*y + *h + 2 * c->bw < 0)
399 *y = 0;
400 } else {
401 if (*x >= m->wx + m->ww)
402 *x = m->wx + m->ww - WIDTH(c);
403 if (*y >= m->wy + m->wh)
404 *y = m->wy + m->wh - HEIGHT(c);
405 if (*x + *w + 2 * c->bw <= m->wx)
406 *x = m->wx;
407 if (*y + *h + 2 * c->bw <= m->wy)
408 *y = m->wy;
409 }
410 if (*h < bh)
411 *h = bh;
412 if (*w < bh)
413 *w = bh;
414 if (resizehints || c->isfloating || !c->mon->lt[c->mon->sellt]->arrange) {
415 if (!c->hintsvalid)
416 updatesizehints(c);
417 /* see last two sentences in ICCCM 4.1.2.3 */
418 baseismin = c->basew == c->minw && c->baseh == c->minh;
419 if (!baseismin) { /* temporarily remove base dimensions */
420 *w -= c->basew;
421 *h -= c->baseh;
422 }
423 /* adjust for aspect limits */
424 if (c->mina > 0 && c->maxa > 0) {
425 if (c->maxa < (float)*w / *h)
426 *w = *h * c->maxa + 0.5;
427 else if (c->mina < (float)*h / *w)
428 *h = *w * c->mina + 0.5;
429 }
430 if (baseismin) { /* increment calculation requires this */
431 *w -= c->basew;
432 *h -= c->baseh;
433 }
434 /* adjust for increment value */
435 if (c->incw)
436 *w -= *w % c->incw;
437 if (c->inch)
438 *h -= *h % c->inch;
439 /* restore base dimensions */
440 *w = MAX(*w + c->basew, c->minw);
441 *h = MAX(*h + c->baseh, c->minh);
442 if (c->maxw)
443 *w = MIN(*w, c->maxw);
444 if (c->maxh)
445 *h = MIN(*h, c->maxh);
446 }
447 return *x != c->x || *y != c->y || *w != c->w || *h != c->h;
448}
449
450void
451arrange(Monitor *m)
452{
453 if (m)
454 showhide(m->stack);
455 else for (m = mons; m; m = m->next)
456 showhide(m->stack);
457 if (m) {
458 arrangemon(m);
459 restack(m);
460 } else for (m = mons; m; m = m->next)
461 arrangemon(m);
462}
463
464void
465arrangemon(Monitor *m)
466{
467 strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol);
468 if (m->lt[m->sellt]->arrange)
469 m->lt[m->sellt]->arrange(m);
470}
471
472void
473attach(Client *c)
474{
475 c->next = c->mon->clients;
476 c->mon->clients = c;
477}
478
479void
480attachstack(Client *c)
481{
482 c->snext = c->mon->stack;
483 c->mon->stack = c;
484}
485
486void
487swallow(Client *p, Client *c)
488{
489
490 if (c->noswallow || c->isterminal)
491 return;
492 if (c->noswallow && !swallowfloating && c->isfloating)
493 return;
494
495 detach(c);
496 detachstack(c);
497
498 setclientstate(c, WithdrawnState);
499 XUnmapWindow(dpy, p->win);
500
501 p->swallowing = c;
502 c->mon = p->mon;
503
504 Window w = p->win;
505 p->win = c->win;
506 c->win = w;
507 updatetitle(p);
508 XMoveResizeWindow(dpy, p->win, p->x, p->y, p->w, p->h);
509 arrange(p->mon);
510 configure(p);
511 updateclientlist();
512}
513
514void
515unswallow(Client *c)
516{
517 c->win = c->swallowing->win;
518
519 free(c->swallowing);
520 c->swallowing = NULL;
521
522 /* unfullscreen the client */
523 setfullscreen(c, 0);
524 updatetitle(c);
525 arrange(c->mon);
526 XMapWindow(dpy, c->win);
527 XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h);
528 setclientstate(c, NormalState);
529 focus(NULL);
530 arrange(c->mon);
531}
532
533void
534buttonpress(XEvent *e)
535{
536 unsigned int i, x, click;
537 Arg arg = {0};
538 Client *c;
539 Monitor *m;
540 XButtonPressedEvent *ev = &e->xbutton;
541
542 click = ClkRootWin;
543 /* focus monitor if necessary */
544 if ((m = wintomon(ev->window)) && m != selmon) {
545 unfocus(selmon->sel, 1);
546 selmon = m;
547 focus(NULL);
548 }
549 if (ev->window == selmon->barwin) {
550 i = x = 0;
551 do
552 x += TEXTW(tags[i]);
553 while (ev->x >= x && ++i < LENGTH(tags));
554 if (i < LENGTH(tags)) {
555 click = ClkTagBar;
556 arg.ui = 1 << i;
557 } else if (ev->x < x + TEXTW(selmon->ltsymbol))
558 click = ClkLtSymbol;
559 else if (ev->x > selmon->ww - (int)TEXTW(stext) - getsystraywidth())
560 click = ClkStatusText;
561 else
562 click = ClkWinTitle;
563 } else if ((c = wintoclient(ev->window))) {
564 focus(c);
565 restack(selmon);
566 XAllowEvents(dpy, ReplayPointer, CurrentTime);
567 click = ClkClientWin;
568 }
569 for (i = 0; i < LENGTH(buttons); i++)
570 if (click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button
571 && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state))
572 buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg);
573}
574
575void
576checkotherwm(void)
577{
578 xerrorxlib = XSetErrorHandler(xerrorstart);
579 /* this causes an error if some other window manager is running */
580 XSelectInput(dpy, DefaultRootWindow(dpy), SubstructureRedirectMask);
581 XSync(dpy, False);
582 XSetErrorHandler(xerror);
583 XSync(dpy, False);
584}
585
586void
587cleanup(void)
588{
589 Arg a = {.ui = ~0};
590 Layout foo = { "", NULL };
591 Monitor *m;
592 size_t i;
593
594 view(&a);
595 selmon->lt[selmon->sellt] = &foo;
596 for (m = mons; m; m = m->next)
597 while (m->stack)
598 unmanage(m->stack, 0);
599 XUngrabKey(dpy, AnyKey, AnyModifier, root);
600 while (mons)
601 cleanupmon(mons);
602
603 if (showsystray) {
604 XUnmapWindow(dpy, systray->win);
605 XDestroyWindow(dpy, systray->win);
606 free(systray);
607 }
608
609 for (i = 0; i < CurLast; i++)
610 drw_cur_free(drw, cursor[i]);
611 for (i = 0; i < LENGTH(colors); i++)
612 free(scheme[i]);
613 free(scheme);
614 XDestroyWindow(dpy, wmcheckwin);
615 drw_free(drw);
616 XSync(dpy, False);
617 XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
618 XDeleteProperty(dpy, root, netatom[NetActiveWindow]);
619}
620
621void
622cleanupmon(Monitor *mon)
623{
624 Monitor *m;
625
626 if (mon == mons)
627 mons = mons->next;
628 else {
629 for (m = mons; m && m->next != mon; m = m->next);
630 m->next = mon->next;
631 }
632 XUnmapWindow(dpy, mon->barwin);
633 XDestroyWindow(dpy, mon->barwin);
634 free(mon);
635}
636
637void
638clientmessage(XEvent *e)
639{
640 XWindowAttributes wa;
641 XSetWindowAttributes swa;
642 XClientMessageEvent *cme = &e->xclient;
643 Client *c = wintoclient(cme->window);
644
645 if (showsystray && cme->window == systray->win && cme->message_type == netatom[NetSystemTrayOP]) {
646 /* add systray icons */
647 if (cme->data.l[1] == SYSTEM_TRAY_REQUEST_DOCK) {
648 if (!(c = (Client *)calloc(1, sizeof(Client))))
649 die("fatal: could not malloc() %u bytes\n", sizeof(Client));
650 if (!(c->win = cme->data.l[2])) {
651 free(c);
652 return;
653 }
654 c->mon = selmon;
655 c->next = systray->icons;
656 systray->icons = c;
657 if (!XGetWindowAttributes(dpy, c->win, &wa)) {
658 /* use sane defaults */
659 wa.width = bh;
660 wa.height = bh;
661 wa.border_width = 0;
662 }
663 c->x = c->oldx = c->y = c->oldy = 0;
664 c->w = c->oldw = wa.width;
665 c->h = c->oldh = wa.height;
666 c->oldbw = wa.border_width;
667 c->bw = 0;
668 c->isfloating = True;
669 /* reuse tags field as mapped status */
670 c->tags = 1;
671 updatesizehints(c);
672 updatesystrayicongeom(c, wa.width, wa.height);
673 XAddToSaveSet(dpy, c->win);
674 XSelectInput(dpy, c->win, StructureNotifyMask | PropertyChangeMask | ResizeRedirectMask);
675 XReparentWindow(dpy, c->win, systray->win, 0, 0);
676 /* use parents background color */
677 swa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
678 XChangeWindowAttributes(dpy, c->win, CWBackPixel, &swa);
679 sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_EMBEDDED_NOTIFY, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
680 /* FIXME not sure if I have to send these events, too */
681 sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_FOCUS_IN, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
682 sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
683 sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_MODALITY_ON, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
684 XSync(dpy, False);
685 resizebarwin(selmon);
686 updatesystray();
687 setclientstate(c, NormalState);
688 }
689 return;
690 }
691
692 if (!c)
693 return;
694 if (cme->message_type == netatom[NetWMState]) {
695 if (cme->data.l[1] == netatom[NetWMFullscreen]
696 || cme->data.l[2] == netatom[NetWMFullscreen])
697 setfullscreen(c, (cme->data.l[0] == 1 /* _NET_WM_STATE_ADD */
698 || (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */ && !c->isfullscreen)));
699 } else if (cme->message_type == netatom[NetActiveWindow]) {
700 if (c != selmon->sel && !c->isurgent)
701 seturgent(c, 1);
702 }
703}
704
705void
706configure(Client *c)
707{
708 XConfigureEvent ce;
709
710 ce.type = ConfigureNotify;
711 ce.display = dpy;
712 ce.event = c->win;
713 ce.window = c->win;
714 ce.x = c->x;
715 ce.y = c->y;
716 ce.width = c->w;
717 ce.height = c->h;
718 ce.border_width = c->bw;
719 ce.above = None;
720 ce.override_redirect = False;
721 XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce);
722}
723
724void
725configurenotify(XEvent *e)
726{
727 Monitor *m;
728 Client *c;
729 XConfigureEvent *ev = &e->xconfigure;
730 int dirty;
731
732 /* TODO: updategeom handling sucks, needs to be simplified */
733 if (ev->window == root) {
734 dirty = (sw != ev->width || sh != ev->height);
735 sw = ev->width;
736 sh = ev->height;
737 if (updategeom() || dirty) {
738 drw_resize(drw, sw, bh);
739 updatebars();
740 for (m = mons; m; m = m->next) {
741 for (c = m->clients; c; c = c->next)
742 if (c->isfullscreen)
743 resizeclient(c, m->mx, m->my, m->mw, m->mh);
744 resizebarwin(m);
745 }
746 focus(NULL);
747 arrange(NULL);
748 }
749 }
750}
751
752void
753configurerequest(XEvent *e)
754{
755 Client *c;
756 Monitor *m;
757 XConfigureRequestEvent *ev = &e->xconfigurerequest;
758 XWindowChanges wc;
759
760 if ((c = wintoclient(ev->window))) {
761 if (ev->value_mask & CWBorderWidth)
762 c->bw = ev->border_width;
763 else if (c->isfloating || !selmon->lt[selmon->sellt]->arrange) {
764 m = c->mon;
765 if (ev->value_mask & CWX) {
766 c->oldx = c->x;
767 c->x = m->mx + ev->x;
768 }
769 if (ev->value_mask & CWY) {
770 c->oldy = c->y;
771 c->y = m->my + ev->y;
772 }
773 if (ev->value_mask & CWWidth) {
774 c->oldw = c->w;
775 c->w = ev->width;
776 }
777 if (ev->value_mask & CWHeight) {
778 c->oldh = c->h;
779 c->h = ev->height;
780 }
781 if ((c->x + c->w) > m->mx + m->mw && c->isfloating)
782 c->x = m->mx + (m->mw / 2 - WIDTH(c) / 2); /* center in x direction */
783 if ((c->y + c->h) > m->my + m->mh && c->isfloating)
784 c->y = m->my + (m->mh / 2 - HEIGHT(c) / 2); /* center in y direction */
785 if ((ev->value_mask & (CWX|CWY)) && !(ev->value_mask & (CWWidth|CWHeight)))
786 configure(c);
787 if (ISVISIBLE(c))
788 XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h);
789 } else
790 configure(c);
791 } else {
792 wc.x = ev->x;
793 wc.y = ev->y;
794 wc.width = ev->width;
795 wc.height = ev->height;
796 wc.border_width = ev->border_width;
797 wc.sibling = ev->above;
798 wc.stack_mode = ev->detail;
799 XConfigureWindow(dpy, ev->window, ev->value_mask, &wc);
800 }
801 XSync(dpy, False);
802}
803
804Monitor *
805createmon(void)
806{
807 Monitor *m;
808
809 m = ecalloc(1, sizeof(Monitor));
810 m->tagset[0] = m->tagset[1] = 1;
811 m->mfact = mfact;
812 m->nmaster = nmaster;
813 m->showbar = showbar;
814 m->topbar = topbar;
815 m->gappih = gappih;
816 m->gappiv = gappiv;
817 m->gappoh = gappoh;
818 m->gappov = gappov;
819 m->lt[0] = &layouts[0];
820 m->lt[1] = &layouts[1 % LENGTH(layouts)];
821 strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol);
822 return m;
823}
824
825void
826destroynotify(XEvent *e)
827{
828 Client *c;
829 XDestroyWindowEvent *ev = &e->xdestroywindow;
830
831 if ((c = wintoclient(ev->window)))
832 unmanage(c, 1);
833 else if ((c = wintosystrayicon(ev->window))) {
834 removesystrayicon(c);
835 resizebarwin(selmon);
836 updatesystray();
837 }
838 else if ((c = swallowingclient(ev->window)))
839 unmanage(c->swallowing, 1);
840}
841
842void
843detach(Client *c)
844{
845 Client **tc;
846
847 for (tc = &c->mon->clients; *tc && *tc != c; tc = &(*tc)->next);
848 *tc = c->next;
849}
850
851void
852detachstack(Client *c)
853{
854 Client **tc, *t;
855
856 for (tc = &c->mon->stack; *tc && *tc != c; tc = &(*tc)->snext);
857 *tc = c->snext;
858
859 if (c == c->mon->sel) {
860 for (t = c->mon->stack; t && !ISVISIBLE(t); t = t->snext);
861 c->mon->sel = t;
862 }
863}
864
865Monitor *
866dirtomon(int dir)
867{
868 Monitor *m = NULL;
869
870 if (dir > 0) {
871 if (!(m = selmon->next))
872 m = mons;
873 } else if (selmon == mons)
874 for (m = mons; m->next; m = m->next);
875 else
876 for (m = mons; m->next != selmon; m = m->next);
877 return m;
878}
879
880void
881drawbar(Monitor *m)
882{
883 int x, w, tw = 0, stw = 0;
884 int boxs = drw->fonts->h / 9;
885 int boxw = drw->fonts->h / 6 + 2;
886 unsigned int i, occ = 0, urg = 0;
887 Client *c;
888
889 if (!m->showbar)
890 return;
891
892 if(showsystray && m == systraytomon(m) && !systrayonleft)
893 stw = getsystraywidth();
894
895 /* draw status first so it can be overdrawn by tags later */
896 if (m == selmon) { /* status is only drawn on selected monitor */
897 drw_setscheme(drw, scheme[SchemeNorm]);
898 tw = TEXTW(stext) - lrpad / 2 + 2; /* 2px extra right padding */
899 drw_text(drw, m->ww - tw - stw, 0, tw, bh, lrpad / 2 - 2, stext, 0);
900 }
901
902 resizebarwin(m);
903 for (c = m->clients; c; c = c->next) {
904 occ |= c->tags;
905 if (c->isurgent)
906 urg |= c->tags;
907 }
908 x = 0;
909 for (i = 0; i < LENGTH(tags); i++) {
910 w = TEXTW(tags[i]);
911 drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]);
912 drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i);
913 if (occ & 1 << i)
914 drw_rect(drw, x + boxs, boxs, boxw, boxw,
915 m == selmon && selmon->sel && selmon->sel->tags & 1 << i,
916 urg & 1 << i);
917 x += w;
918 }
919 w = TEXTW(m->ltsymbol);
920 drw_setscheme(drw, scheme[SchemeNorm]);
921 x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0);
922
923 if ((w = m->ww - tw - stw - x) > bh) {
924 if (m->sel) {
925 drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]);
926 drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0);
927 if (m->sel->isfloating)
928 drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0);
929 } else {
930 drw_setscheme(drw, scheme[SchemeNorm]);
931 drw_rect(drw, x, 0, w, bh, 1, 1);
932 }
933 }
934 drw_map(drw, m->barwin, 0, 0, m->ww - stw, bh);
935}
936
937void
938drawbars(void)
939{
940 Monitor *m;
941
942 for (m = mons; m; m = m->next)
943 drawbar(m);
944}
945
946void
947enternotify(XEvent *e)
948{
949 Client *c;
950 Monitor *m;
951 XCrossingEvent *ev = &e->xcrossing;
952
953 if ((ev->mode != NotifyNormal || ev->detail == NotifyInferior) && ev->window != root)
954 return;
955 c = wintoclient(ev->window);
956 m = c ? c->mon : wintomon(ev->window);
957 if (m != selmon) {
958 unfocus(selmon->sel, 1);
959 selmon = m;
960 } else if (!c || c == selmon->sel)
961 return;
962 focus(c);
963}
964
965void
966expose(XEvent *e)
967{
968 Monitor *m;
969 XExposeEvent *ev = &e->xexpose;
970
971 if (ev->count == 0 && (m = wintomon(ev->window))) {
972 drawbar(m);
973 if (m == selmon)
974 updatesystray();
975 }
976}
977
978void
979focus(Client *c)
980{
981 if (!c || !ISVISIBLE(c))
982 for (c = selmon->stack; c && !ISVISIBLE(c); c = c->snext);
983 if (selmon->sel && selmon->sel != c)
984 unfocus(selmon->sel, 0);
985 if (c) {
986 if (c->mon != selmon)
987 selmon = c->mon;
988 if (c->isurgent)
989 seturgent(c, 0);
990 detachstack(c);
991 attachstack(c);
992 grabbuttons(c, 1);
993 XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel);
994 setfocus(c);
995 } else {
996 XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
997 XDeleteProperty(dpy, root, netatom[NetActiveWindow]);
998 }
999 selmon->sel = c;
1000 drawbars();
1001}
1002
1003/* there are some broken focus acquiring clients needing extra handling */
1004void
1005focusin(XEvent *e)
1006{
1007 XFocusChangeEvent *ev = &e->xfocus;
1008
1009 if (selmon->sel && ev->window != selmon->sel->win)
1010 setfocus(selmon->sel);
1011}
1012
1013void
1014focusmon(const Arg *arg)
1015{
1016 Monitor *m;
1017
1018 if (!mons->next)
1019 return;
1020 if ((m = dirtomon(arg->i)) == selmon)
1021 return;
1022 unfocus(selmon->sel, 0);
1023 selmon = m;
1024 focus(NULL);
1025}
1026
1027void
1028focusstack(const Arg *arg)
1029{
1030 Client *c = NULL, *i;
1031
1032 if (!selmon->sel || (selmon->sel->isfullscreen && lockfullscreen))
1033 return;
1034 if (arg->i > 0) {
1035 for (c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next);
1036 if (!c)
1037 for (c = selmon->clients; c && !ISVISIBLE(c); c = c->next);
1038 } else {
1039 for (i = selmon->clients; i != selmon->sel; i = i->next)
1040 if (ISVISIBLE(i))
1041 c = i;
1042 if (!c)
1043 for (; i; i = i->next)
1044 if (ISVISIBLE(i))
1045 c = i;
1046 }
1047 if (c) {
1048 focus(c);
1049 restack(selmon);
1050 }
1051}
1052
1053Atom
1054getatomprop(Client *c, Atom prop)
1055{
1056 int di;
1057 unsigned long dl;
1058 unsigned char *p = NULL;
1059 Atom da, atom = None;
1060
1061 /* FIXME getatomprop should return the number of items and a pointer to
1062 * the stored data instead of this workaround */
1063 Atom req = XA_ATOM;
1064 if (prop == xatom[XembedInfo])
1065 req = xatom[XembedInfo];
1066
1067 if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, req,
1068 &da, &di, &dl, &dl, &p) == Success && p) {
1069 atom = *(Atom *)p;
1070 if (da == xatom[XembedInfo] && dl == 2)
1071 atom = ((Atom *)p)[1];
1072 XFree(p);
1073 }
1074 return atom;
1075}
1076
1077unsigned int
1078getsystraywidth()
1079{
1080 unsigned int w = 0;
1081 Client *i;
1082 if(showsystray)
1083 for(i = systray->icons; i; w += i->w + systrayspacing, i = i->next) ;
1084 return w ? w + systrayspacing : 1;
1085}
1086
1087int
1088getrootptr(int *x, int *y)
1089{
1090 int di;
1091 unsigned int dui;
1092 Window dummy;
1093
1094 return XQueryPointer(dpy, root, &dummy, &dummy, x, y, &di, &di, &dui);
1095}
1096
1097long
1098getstate(Window w)
1099{
1100 int format;
1101 long result = -1;
1102 unsigned char *p = NULL;
1103 unsigned long n, extra;
1104 Atom real;
1105
1106 if (XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState],
1107 &real, &format, &n, &extra, (unsigned char **)&p) != Success)
1108 return -1;
1109 if (n != 0)
1110 result = *p;
1111 XFree(p);
1112 return result;
1113}
1114
1115int
1116gettextprop(Window w, Atom atom, char *text, unsigned int size)
1117{
1118 char **list = NULL;
1119 int n;
1120 XTextProperty name;
1121
1122 if (!text || size == 0)
1123 return 0;
1124 text[0] = '\0';
1125 if (!XGetTextProperty(dpy, w, &name, atom) || !name.nitems)
1126 return 0;
1127 if (name.encoding == XA_STRING) {
1128 strncpy(text, (char *)name.value, size - 1);
1129 } else if (XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success && n > 0 && *list) {
1130 strncpy(text, *list, size - 1);
1131 XFreeStringList(list);
1132 }
1133 text[size - 1] = '\0';
1134 XFree(name.value);
1135 return 1;
1136}
1137
1138void
1139grabbuttons(Client *c, int focused)
1140{
1141 updatenumlockmask();
1142 {
1143 unsigned int i, j;
1144 unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask };
1145 XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
1146 if (!focused)
1147 XGrabButton(dpy, AnyButton, AnyModifier, c->win, False,
1148 BUTTONMASK, GrabModeSync, GrabModeSync, None, None);
1149 for (i = 0; i < LENGTH(buttons); i++)
1150 if (buttons[i].click == ClkClientWin)
1151 for (j = 0; j < LENGTH(modifiers); j++)
1152 XGrabButton(dpy, buttons[i].button,
1153 buttons[i].mask | modifiers[j],
1154 c->win, False, BUTTONMASK,
1155 GrabModeAsync, GrabModeSync, None, None);
1156 }
1157}
1158
1159void
1160grabkeys(void)
1161{
1162 updatenumlockmask();
1163 {
1164 unsigned int i, j, k;
1165 unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask };
1166 int start, end, skip;
1167 KeySym *syms;
1168
1169 XUngrabKey(dpy, AnyKey, AnyModifier, root);
1170 XDisplayKeycodes(dpy, &start, &end);
1171 syms = XGetKeyboardMapping(dpy, start, end - start + 1, &skip);
1172 if (!syms)
1173 return;
1174 for (k = start; k <= end; k++)
1175 for (i = 0; i < LENGTH(keys); i++)
1176 /* skip modifier codes, we do that ourselves */
1177 if (keys[i].keysym == syms[(k - start) * skip])
1178 for (j = 0; j < LENGTH(modifiers); j++)
1179 XGrabKey(dpy, k,
1180 keys[i].mod | modifiers[j],
1181 root, True,
1182 GrabModeAsync, GrabModeAsync);
1183 XFree(syms);
1184 }
1185}
1186
1187void
1188incnmaster(const Arg *arg)
1189{
1190 selmon->nmaster = MAX(selmon->nmaster + arg->i, 0);
1191 arrange(selmon);
1192}
1193
1194#ifdef XINERAMA
1195static int
1196isuniquegeom(XineramaScreenInfo *unique, size_t n, XineramaScreenInfo *info)
1197{
1198 while (n--)
1199 if (unique[n].x_org == info->x_org && unique[n].y_org == info->y_org
1200 && unique[n].width == info->width && unique[n].height == info->height)
1201 return 0;
1202 return 1;
1203}
1204#endif /* XINERAMA */
1205
1206void
1207keypress(XEvent *e)
1208{
1209 unsigned int i;
1210 KeySym keysym;
1211 XKeyEvent *ev;
1212
1213 ev = &e->xkey;
1214 keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0);
1215 for (i = 0; i < LENGTH(keys); i++)
1216 if (keysym == keys[i].keysym
1217 && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state)
1218 && keys[i].func)
1219 keys[i].func(&(keys[i].arg));
1220}
1221
1222void
1223killclient(const Arg *arg)
1224{
1225 if (!selmon->sel)
1226 return;
1227
1228 if (!sendevent(selmon->sel->win, wmatom[WMDelete], NoEventMask, wmatom[WMDelete], CurrentTime, 0 , 0, 0)) {
1229 XGrabServer(dpy);
1230 XSetErrorHandler(xerrordummy);
1231 XSetCloseDownMode(dpy, DestroyAll);
1232 XKillClient(dpy, selmon->sel->win);
1233 XSync(dpy, False);
1234 XSetErrorHandler(xerror);
1235 XUngrabServer(dpy);
1236 }
1237}
1238
1239void
1240manage(Window w, XWindowAttributes *wa)
1241{
1242 Client *c, *t = NULL, *term = NULL;
1243 Window trans = None;
1244 XWindowChanges wc;
1245
1246 c = ecalloc(1, sizeof(Client));
1247 c->win = w;
1248 c->pid = winpid(w);
1249 /* geometry */
1250 c->x = c->oldx = wa->x;
1251 c->y = c->oldy = wa->y;
1252 c->w = c->oldw = wa->width;
1253 c->h = c->oldh = wa->height;
1254 c->oldbw = wa->border_width;
1255
1256 updatetitle(c);
1257 if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) {
1258 c->mon = t->mon;
1259 c->tags = t->tags;
1260 } else {
1261 c->mon = selmon;
1262 applyrules(c);
1263 term = termforwin(c);
1264 }
1265
1266 if (c->x + WIDTH(c) > c->mon->wx + c->mon->ww)
1267 c->x = c->mon->wx + c->mon->ww - WIDTH(c);
1268 if (c->y + HEIGHT(c) > c->mon->wy + c->mon->wh)
1269 c->y = c->mon->wy + c->mon->wh - HEIGHT(c);
1270 c->x = MAX(c->x, c->mon->wx);
1271 c->y = MAX(c->y, c->mon->wy);
1272 c->bw = borderpx;
1273
1274 wc.border_width = c->bw;
1275 XConfigureWindow(dpy, w, CWBorderWidth, &wc);
1276 XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel);
1277 configure(c); /* propagates border_width, if size doesn't change */
1278 updatewindowtype(c);
1279 updatesizehints(c);
1280 updatewmhints(c);
1281 XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask);
1282 grabbuttons(c, 0);
1283 if (!c->isfloating)
1284 c->isfloating = c->oldstate = trans != None || c->isfixed;
1285 if (c->isfloating)
1286 XRaiseWindow(dpy, c->win);
1287 attach(c);
1288 attachstack(c);
1289 XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend,
1290 (unsigned char *) &(c->win), 1);
1291 XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */
1292 setclientstate(c, NormalState);
1293 if (c->mon == selmon)
1294 unfocus(selmon->sel, 0);
1295 c->mon->sel = c;
1296 arrange(c->mon);
1297 XMapWindow(dpy, c->win);
1298 if (term)
1299 swallow(term, c);
1300 focus(NULL);
1301}
1302
1303void
1304mappingnotify(XEvent *e)
1305{
1306 XMappingEvent *ev = &e->xmapping;
1307
1308 XRefreshKeyboardMapping(ev);
1309 if (ev->request == MappingKeyboard)
1310 grabkeys();
1311}
1312
1313void
1314maprequest(XEvent *e)
1315{
1316 static XWindowAttributes wa;
1317 XMapRequestEvent *ev = &e->xmaprequest;
1318
1319 Client *i;
1320 if ((i = wintosystrayicon(ev->window))) {
1321 sendevent(i->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0, systray->win, XEMBED_EMBEDDED_VERSION);
1322 resizebarwin(selmon);
1323 updatesystray();
1324 }
1325
1326 if (!XGetWindowAttributes(dpy, ev->window, &wa) || wa.override_redirect)
1327 return;
1328 if (!wintoclient(ev->window))
1329 manage(ev->window, &wa);
1330}
1331
1332void
1333monocle(Monitor *m)
1334{
1335 unsigned int n = 0;
1336 Client *c;
1337
1338 for (c = m->clients; c; c = c->next)
1339 if (ISVISIBLE(c))
1340 n++;
1341 if (n > 0) /* override layout symbol */
1342 snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n);
1343 for (c = nexttiled(m->clients); c; c = nexttiled(c->next))
1344 resize(c, m->wx, m->wy, m->ww - 2 * c->bw, m->wh - 2 * c->bw, 0);
1345}
1346
1347void
1348motionnotify(XEvent *e)
1349{
1350 static Monitor *mon = NULL;
1351 Monitor *m;
1352 XMotionEvent *ev = &e->xmotion;
1353
1354 if (ev->window != root)
1355 return;
1356 if ((m = recttomon(ev->x_root, ev->y_root, 1, 1)) != mon && mon) {
1357 unfocus(selmon->sel, 1);
1358 selmon = m;
1359 focus(NULL);
1360 }
1361 mon = m;
1362}
1363
1364void
1365movemouse(const Arg *arg)
1366{
1367 int x, y, ocx, ocy, nx, ny;
1368 Client *c;
1369 Monitor *m;
1370 XEvent ev;
1371 Time lasttime = 0;
1372
1373 if (!(c = selmon->sel))
1374 return;
1375 if (c->isfullscreen) /* no support moving fullscreen windows by mouse */
1376 return;
1377 restack(selmon);
1378 ocx = c->x;
1379 ocy = c->y;
1380 if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync,
1381 None, cursor[CurMove]->cursor, CurrentTime) != GrabSuccess)
1382 return;
1383 if (!getrootptr(&x, &y))
1384 return;
1385 do {
1386 XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev);
1387 switch(ev.type) {
1388 case ConfigureRequest:
1389 case Expose:
1390 case MapRequest:
1391 handler[ev.type](&ev);
1392 break;
1393 case MotionNotify:
1394 if ((ev.xmotion.time - lasttime) <= (1000 / 60))
1395 continue;
1396 lasttime = ev.xmotion.time;
1397
1398 nx = ocx + (ev.xmotion.x - x);
1399 ny = ocy + (ev.xmotion.y - y);
1400 if (abs(selmon->wx - nx) < snap)
1401 nx = selmon->wx;
1402 else if (abs((selmon->wx + selmon->ww) - (nx + WIDTH(c))) < snap)
1403 nx = selmon->wx + selmon->ww - WIDTH(c);
1404 if (abs(selmon->wy - ny) < snap)
1405 ny = selmon->wy;
1406 else if (abs((selmon->wy + selmon->wh) - (ny + HEIGHT(c))) < snap)
1407 ny = selmon->wy + selmon->wh - HEIGHT(c);
1408 if (!c->isfloating && selmon->lt[selmon->sellt]->arrange
1409 && (abs(nx - c->x) > snap || abs(ny - c->y) > snap))
1410 togglefloating(NULL);
1411 if (!selmon->lt[selmon->sellt]->arrange || c->isfloating)
1412 resize(c, nx, ny, c->w, c->h, 1);
1413 break;
1414 }
1415 } while (ev.type != ButtonRelease);
1416 XUngrabPointer(dpy, CurrentTime);
1417 if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) {
1418 sendmon(c, m);
1419 selmon = m;
1420 focus(NULL);
1421 }
1422}
1423
1424Client *
1425nexttiled(Client *c)
1426{
1427 for (; c && (c->isfloating || !ISVISIBLE(c)); c = c->next);
1428 return c;
1429}
1430
1431void
1432pop(Client *c)
1433{
1434 detach(c);
1435 attach(c);
1436 focus(c);
1437 arrange(c->mon);
1438}
1439
1440void
1441propertynotify(XEvent *e)
1442{
1443 Client *c;
1444 Window trans;
1445 XPropertyEvent *ev = &e->xproperty;
1446
1447 if ((c = wintosystrayicon(ev->window))) {
1448 if (ev->atom == XA_WM_NORMAL_HINTS) {
1449 updatesizehints(c);
1450 updatesystrayicongeom(c, c->w, c->h);
1451 }
1452 else
1453 updatesystrayiconstate(c, ev);
1454 resizebarwin(selmon);
1455 updatesystray();
1456 }
1457
1458 if ((ev->window == root) && (ev->atom == XA_WM_NAME))
1459 updatestatus();
1460 else if (ev->state == PropertyDelete)
1461 return; /* ignore */
1462 else if ((c = wintoclient(ev->window))) {
1463 switch(ev->atom) {
1464 default: break;
1465 case XA_WM_TRANSIENT_FOR:
1466 if (!c->isfloating && (XGetTransientForHint(dpy, c->win, &trans)) &&
1467 (c->isfloating = (wintoclient(trans)) != NULL))
1468 arrange(c->mon);
1469 break;
1470 case XA_WM_NORMAL_HINTS:
1471 c->hintsvalid = 0;
1472 break;
1473 case XA_WM_HINTS:
1474 updatewmhints(c);
1475 drawbars();
1476 break;
1477 }
1478 if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) {
1479 updatetitle(c);
1480 if (c == c->mon->sel)
1481 drawbar(c->mon);
1482 }
1483 if (ev->atom == netatom[NetWMWindowType])
1484 updatewindowtype(c);
1485 }
1486}
1487
1488void
1489quit(const Arg *arg)
1490{
1491 running = 0;
1492}
1493
1494Monitor *
1495recttomon(int x, int y, int w, int h)
1496{
1497 Monitor *m, *r = selmon;
1498 int a, area = 0;
1499
1500 for (m = mons; m; m = m->next)
1501 if ((a = INTERSECT(x, y, w, h, m)) > area) {
1502 area = a;
1503 r = m;
1504 }
1505 return r;
1506}
1507
1508void
1509removesystrayicon(Client *i)
1510{
1511 Client **ii;
1512
1513 if (!showsystray || !i)
1514 return;
1515 for (ii = &systray->icons; *ii && *ii != i; ii = &(*ii)->next);
1516 if (ii)
1517 *ii = i->next;
1518 free(i);
1519}
1520
1521void
1522resize(Client *c, int x, int y, int w, int h, int interact)
1523{
1524 if (applysizehints(c, &x, &y, &w, &h, interact))
1525 resizeclient(c, x, y, w, h);
1526}
1527
1528void
1529resizebarwin(Monitor *m) {
1530 unsigned int w = m->ww;
1531 if (showsystray && m == systraytomon(m) && !systrayonleft)
1532 w -= getsystraywidth();
1533 XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, w, bh);
1534}
1535
1536void
1537resizeclient(Client *c, int x, int y, int w, int h)
1538{
1539 XWindowChanges wc;
1540
1541 c->oldx = c->x; c->x = wc.x = x;
1542 c->oldy = c->y; c->y = wc.y = y;
1543 c->oldw = c->w; c->w = wc.width = w;
1544 c->oldh = c->h; c->h = wc.height = h;
1545 wc.border_width = c->bw;
1546 XConfigureWindow(dpy, c->win, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc);
1547 configure(c);
1548 XSync(dpy, False);
1549}
1550
1551void
1552resizerequest(XEvent *e)
1553{
1554 XResizeRequestEvent *ev = &e->xresizerequest;
1555 Client *i;
1556
1557 if ((i = wintosystrayicon(ev->window))) {
1558 updatesystrayicongeom(i, ev->width, ev->height);
1559 resizebarwin(selmon);
1560 updatesystray();
1561 }
1562}
1563
1564void
1565resizemouse(const Arg *arg)
1566{
1567 int ocx, ocy, nw, nh;
1568 Client *c;
1569 Monitor *m;
1570 XEvent ev;
1571 Time lasttime = 0;
1572
1573 if (!(c = selmon->sel))
1574 return;
1575 if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */
1576 return;
1577 restack(selmon);
1578 ocx = c->x;
1579 ocy = c->y;
1580 if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync,
1581 None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess)
1582 return;
1583 XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1);
1584 do {
1585 XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev);
1586 switch(ev.type) {
1587 case ConfigureRequest:
1588 case Expose:
1589 case MapRequest:
1590 handler[ev.type](&ev);
1591 break;
1592 case MotionNotify:
1593 if ((ev.xmotion.time - lasttime) <= (1000 / 60))
1594 continue;
1595 lasttime = ev.xmotion.time;
1596
1597 nw = MAX(ev.xmotion.x - ocx - 2 * c->bw + 1, 1);
1598 nh = MAX(ev.xmotion.y - ocy - 2 * c->bw + 1, 1);
1599 if (c->mon->wx + nw >= selmon->wx && c->mon->wx + nw <= selmon->wx + selmon->ww
1600 && c->mon->wy + nh >= selmon->wy && c->mon->wy + nh <= selmon->wy + selmon->wh)
1601 {
1602 if (!c->isfloating && selmon->lt[selmon->sellt]->arrange
1603 && (abs(nw - c->w) > snap || abs(nh - c->h) > snap))
1604 togglefloating(NULL);
1605 }
1606 if (!selmon->lt[selmon->sellt]->arrange || c->isfloating)
1607 resize(c, c->x, c->y, nw, nh, 1);
1608 break;
1609 }
1610 } while (ev.type != ButtonRelease);
1611 XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1);
1612 XUngrabPointer(dpy, CurrentTime);
1613 while (XCheckMaskEvent(dpy, EnterWindowMask, &ev));
1614 if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) {
1615 sendmon(c, m);
1616 selmon = m;
1617 focus(NULL);
1618 }
1619}
1620
1621void
1622restack(Monitor *m)
1623{
1624 Client *c;
1625 XEvent ev;
1626 XWindowChanges wc;
1627
1628 drawbar(m);
1629 if (!m->sel)
1630 return;
1631 if (m->sel->isfloating || !m->lt[m->sellt]->arrange)
1632 XRaiseWindow(dpy, m->sel->win);
1633 if (m->lt[m->sellt]->arrange) {
1634 wc.stack_mode = Below;
1635 wc.sibling = m->barwin;
1636 for (c = m->stack; c; c = c->snext)
1637 if (!c->isfloating && ISVISIBLE(c)) {
1638 XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc);
1639 wc.sibling = c->win;
1640 }
1641 }
1642 XSync(dpy, False);
1643 while (XCheckMaskEvent(dpy, EnterWindowMask, &ev));
1644}
1645
1646void
1647run(void)
1648{
1649 XEvent ev;
1650 /* main event loop */
1651 XSync(dpy, False);
1652 while (running && !XNextEvent(dpy, &ev))
1653 if (handler[ev.type])
1654 handler[ev.type](&ev); /* call handler */
1655}
1656
1657void
1658scan(void)
1659{
1660 unsigned int i, num;
1661 Window d1, d2, *wins = NULL;
1662 XWindowAttributes wa;
1663
1664 if (XQueryTree(dpy, root, &d1, &d2, &wins, &num)) {
1665 for (i = 0; i < num; i++) {
1666 if (!XGetWindowAttributes(dpy, wins[i], &wa)
1667 || wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1))
1668 continue;
1669 if (wa.map_state == IsViewable || getstate(wins[i]) == IconicState)
1670 manage(wins[i], &wa);
1671 }
1672 for (i = 0; i < num; i++) { /* now the transients */
1673 if (!XGetWindowAttributes(dpy, wins[i], &wa))
1674 continue;
1675 if (XGetTransientForHint(dpy, wins[i], &d1)
1676 && (wa.map_state == IsViewable || getstate(wins[i]) == IconicState))
1677 manage(wins[i], &wa);
1678 }
1679 if (wins)
1680 XFree(wins);
1681 }
1682}
1683
1684void
1685sendmon(Client *c, Monitor *m)
1686{
1687 if (c->mon == m)
1688 return;
1689 unfocus(c, 1);
1690 detach(c);
1691 detachstack(c);
1692 c->mon = m;
1693 c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */
1694 attach(c);
1695 attachstack(c);
1696 focus(NULL);
1697 arrange(NULL);
1698}
1699
1700void
1701setclientstate(Client *c, long state)
1702{
1703 long data[] = { state, None };
1704
1705 XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32,
1706 PropModeReplace, (unsigned char *)data, 2);
1707}
1708
1709int
1710sendevent(Window w, Atom proto, int mask, long d0, long d1, long d2, long d3, long d4)
1711{
1712 int n;
1713 Atom *protocols, mt;
1714 int exists = 0;
1715 XEvent ev;
1716
1717 if (proto == wmatom[WMTakeFocus] || proto == wmatom[WMDelete]) {
1718 mt = wmatom[WMProtocols];
1719 if (XGetWMProtocols(dpy, w, &protocols, &n)) {
1720 while (!exists && n--)
1721 exists = protocols[n] == proto;
1722 XFree(protocols);
1723 }
1724 }
1725 else {
1726 exists = True;
1727 mt = proto;
1728 }
1729
1730 if (exists) {
1731 ev.type = ClientMessage;
1732 ev.xclient.window = w;
1733 ev.xclient.message_type = mt;
1734 ev.xclient.format = 32;
1735 ev.xclient.data.l[0] = d0;
1736 ev.xclient.data.l[1] = d1;
1737 ev.xclient.data.l[2] = d2;
1738 ev.xclient.data.l[3] = d3;
1739 ev.xclient.data.l[4] = d4;
1740 XSendEvent(dpy, w, False, mask, &ev);
1741 }
1742 return exists;
1743}
1744
1745void
1746setfocus(Client *c)
1747{
1748 if (!c->neverfocus) {
1749 XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime);
1750 XChangeProperty(dpy, root, netatom[NetActiveWindow],
1751 XA_WINDOW, 32, PropModeReplace,
1752 (unsigned char *) &(c->win), 1);
1753 }
1754 sendevent(c->win, wmatom[WMTakeFocus], NoEventMask, wmatom[WMTakeFocus], CurrentTime, 0, 0, 0);
1755}
1756
1757void
1758setfullscreen(Client *c, int fullscreen)
1759{
1760 if (fullscreen && !c->isfullscreen) {
1761 XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32,
1762 PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1);
1763 c->isfullscreen = 1;
1764 c->oldstate = c->isfloating;
1765 c->oldbw = c->bw;
1766 c->bw = 0;
1767 c->isfloating = 1;
1768 resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh);
1769 XRaiseWindow(dpy, c->win);
1770 } else if (!fullscreen && c->isfullscreen){
1771 XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32,
1772 PropModeReplace, (unsigned char*)0, 0);
1773 c->isfullscreen = 0;
1774 c->isfloating = c->oldstate;
1775 c->bw = c->oldbw;
1776 c->x = c->oldx;
1777 c->y = c->oldy;
1778 c->w = c->oldw;
1779 c->h = c->oldh;
1780 resizeclient(c, c->x, c->y, c->w, c->h);
1781 arrange(c->mon);
1782 }
1783}
1784
1785void
1786setgaps(int oh, int ov, int ih, int iv)
1787{
1788 if (oh < 0) oh = 0;
1789 if (ov < 0) ov = 0;
1790 if (ih < 0) ih = 0;
1791 if (iv < 0) iv = 0;
1792
1793 selmon->gappoh = oh;
1794 selmon->gappov = ov;
1795 selmon->gappih = ih;
1796 selmon->gappiv = iv;
1797 arrange(selmon);
1798}
1799
1800void
1801togglegaps(const Arg *arg)
1802{
1803 enablegaps = !enablegaps;
1804 arrange(selmon);
1805}
1806
1807void
1808defaultgaps(const Arg *arg)
1809{
1810 setgaps(gappoh, gappov, gappih, gappiv);
1811}
1812
1813void
1814incrgaps(const Arg *arg)
1815{
1816 setgaps(
1817 selmon->gappoh + arg->i,
1818 selmon->gappov + arg->i,
1819 selmon->gappih + arg->i,
1820 selmon->gappiv + arg->i
1821 );
1822}
1823
1824void
1825incrigaps(const Arg *arg)
1826{
1827 setgaps(
1828 selmon->gappoh,
1829 selmon->gappov,
1830 selmon->gappih + arg->i,
1831 selmon->gappiv + arg->i
1832 );
1833}
1834
1835void
1836incrogaps(const Arg *arg)
1837{
1838 setgaps(
1839 selmon->gappoh + arg->i,
1840 selmon->gappov + arg->i,
1841 selmon->gappih,
1842 selmon->gappiv
1843 );
1844}
1845
1846void
1847incrohgaps(const Arg *arg)
1848{
1849 setgaps(
1850 selmon->gappoh + arg->i,
1851 selmon->gappov,
1852 selmon->gappih,
1853 selmon->gappiv
1854 );
1855}
1856
1857void
1858incrovgaps(const Arg *arg)
1859{
1860 setgaps(
1861 selmon->gappoh,
1862 selmon->gappov + arg->i,
1863 selmon->gappih,
1864 selmon->gappiv
1865 );
1866}
1867
1868void
1869incrihgaps(const Arg *arg)
1870{
1871 setgaps(
1872 selmon->gappoh,
1873 selmon->gappov,
1874 selmon->gappih + arg->i,
1875 selmon->gappiv
1876 );
1877}
1878
1879void
1880incrivgaps(const Arg *arg)
1881{
1882 setgaps(
1883 selmon->gappoh,
1884 selmon->gappov,
1885 selmon->gappih,
1886 selmon->gappiv + arg->i
1887 );
1888}
1889
1890void
1891setlayout(const Arg *arg)
1892{
1893 if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt])
1894 selmon->sellt ^= 1;
1895 if (arg && arg->v)
1896 selmon->lt[selmon->sellt] = (Layout *)arg->v;
1897 strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol);
1898 if (selmon->sel)
1899 arrange(selmon);
1900 else
1901 drawbar(selmon);
1902}
1903
1904/* arg > 1.0 will set mfact absolutely */
1905void
1906setmfact(const Arg *arg)
1907{
1908 float f;
1909
1910 if (!arg || !selmon->lt[selmon->sellt]->arrange)
1911 return;
1912 f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0;
1913 if (f < 0.05 || f > 0.95)
1914 return;
1915 selmon->mfact = f;
1916 arrange(selmon);
1917}
1918
1919void
1920setup(void)
1921{
1922 int i;
1923 XSetWindowAttributes wa;
1924 Atom utf8string;
1925 struct sigaction sa;
1926
1927 /* do not transform children into zombies when they terminate */
1928 sigemptyset(&sa.sa_mask);
1929 sa.sa_flags = SA_NOCLDSTOP | SA_NOCLDWAIT | SA_RESTART;
1930 sa.sa_handler = SIG_IGN;
1931 sigaction(SIGCHLD, &sa, NULL);
1932
1933 /* clean up any zombies (inherited from .xinitrc etc) immediately */
1934 while (waitpid(-1, NULL, WNOHANG) > 0);
1935
1936 /* init screen */
1937 screen = DefaultScreen(dpy);
1938 sw = DisplayWidth(dpy, screen);
1939 sh = DisplayHeight(dpy, screen);
1940 root = RootWindow(dpy, screen);
1941 drw = drw_create(dpy, screen, root, sw, sh);
1942 if (!drw_fontset_create(drw, fonts, LENGTH(fonts)))
1943 die("no fonts could be loaded.");
1944 lrpad = drw->fonts->h;
1945 bh = drw->fonts->h + 2;
1946 updategeom();
1947 /* init atoms */
1948 utf8string = XInternAtom(dpy, "UTF8_STRING", False);
1949 wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False);
1950 wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
1951 wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False);
1952 wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False);
1953 netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False);
1954 netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False);
1955 netatom[NetSystemTray] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_S0", False);
1956 netatom[NetSystemTrayOP] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_OPCODE", False);
1957 netatom[NetSystemTrayOrientation] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION", False);
1958 netatom[NetSystemTrayOrientationHorz] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION_HORZ", False);
1959 netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False);
1960 netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False);
1961 netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False);
1962 netatom[NetWMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
1963 netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);
1964 netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False);
1965 netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False);
1966 xatom[Manager] = XInternAtom(dpy, "MANAGER", False);
1967 xatom[Xembed] = XInternAtom(dpy, "_XEMBED", False);
1968 xatom[XembedInfo] = XInternAtom(dpy, "_XEMBED_INFO", False);
1969 /* init cursors */
1970 cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr);
1971 cursor[CurResize] = drw_cur_create(drw, XC_sizing);
1972 cursor[CurMove] = drw_cur_create(drw, XC_fleur);
1973 /* init appearance */
1974 scheme = ecalloc(LENGTH(colors), sizeof(Clr *));
1975 for (i = 0; i < LENGTH(colors); i++)
1976 scheme[i] = drw_scm_create(drw, colors[i], 3);
1977 /* init system tray */
1978 updatesystray();
1979 /* init bars */
1980 updatebars();
1981 updatestatus();
1982 /* supporting window for NetWMCheck */
1983 wmcheckwin = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 0, 0, 0);
1984 XChangeProperty(dpy, wmcheckwin, netatom[NetWMCheck], XA_WINDOW, 32,
1985 PropModeReplace, (unsigned char *) &wmcheckwin, 1);
1986 XChangeProperty(dpy, wmcheckwin, netatom[NetWMName], utf8string, 8,
1987 PropModeReplace, (unsigned char *) "dwm", 3);
1988 XChangeProperty(dpy, root, netatom[NetWMCheck], XA_WINDOW, 32,
1989 PropModeReplace, (unsigned char *) &wmcheckwin, 1);
1990 /* EWMH support per view */
1991 XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32,
1992 PropModeReplace, (unsigned char *) netatom, NetLast);
1993 XDeleteProperty(dpy, root, netatom[NetClientList]);
1994 /* select events */
1995 wa.cursor = cursor[CurNormal]->cursor;
1996 wa.event_mask = SubstructureRedirectMask|SubstructureNotifyMask
1997 |ButtonPressMask|PointerMotionMask|EnterWindowMask
1998 |LeaveWindowMask|StructureNotifyMask|PropertyChangeMask;
1999 XChangeWindowAttributes(dpy, root, CWEventMask|CWCursor, &wa);
2000 XSelectInput(dpy, root, wa.event_mask);
2001 grabkeys();
2002 focus(NULL);
2003}
2004
2005void
2006seturgent(Client *c, int urg)
2007{
2008 XWMHints *wmh;
2009
2010 c->isurgent = urg;
2011 if (!(wmh = XGetWMHints(dpy, c->win)))
2012 return;
2013 wmh->flags = urg ? (wmh->flags | XUrgencyHint) : (wmh->flags & ~XUrgencyHint);
2014 XSetWMHints(dpy, c->win, wmh);
2015 XFree(wmh);
2016}
2017
2018void
2019showhide(Client *c)
2020{
2021 if (!c)
2022 return;
2023 if (ISVISIBLE(c)) {
2024 /* show clients top down */
2025 XMoveWindow(dpy, c->win, c->x, c->y);
2026 if ((!c->mon->lt[c->mon->sellt]->arrange || c->isfloating) && !c->isfullscreen)
2027 resize(c, c->x, c->y, c->w, c->h, 0);
2028 showhide(c->snext);
2029 } else {
2030 /* hide clients bottom up */
2031 showhide(c->snext);
2032 XMoveWindow(dpy, c->win, WIDTH(c) * -2, c->y);
2033 }
2034}
2035
2036void
2037spawn(const Arg *arg)
2038{
2039 struct sigaction sa;
2040
2041 if (arg->v == dmenucmd)
2042 dmenumon[0] = '0' + selmon->num;
2043 if (fork() == 0) {
2044 if (dpy)
2045 close(ConnectionNumber(dpy));
2046 setsid();
2047
2048 sigemptyset(&sa.sa_mask);
2049 sa.sa_flags = 0;
2050 sa.sa_handler = SIG_DFL;
2051 sigaction(SIGCHLD, &sa, NULL);
2052
2053 execvp(((char **)arg->v)[0], (char **)arg->v);
2054 die("dwm: execvp '%s' failed:", ((char **)arg->v)[0]);
2055 }
2056}
2057
2058void
2059tag(const Arg *arg)
2060{
2061 if (selmon->sel && arg->ui & TAGMASK) {
2062 selmon->sel->tags = arg->ui & TAGMASK;
2063 focus(NULL);
2064 arrange(selmon);
2065 }
2066}
2067
2068void
2069tagmon(const Arg *arg)
2070{
2071 if (!selmon->sel || !mons->next)
2072 return;
2073 sendmon(selmon->sel, dirtomon(arg->i));
2074}
2075
2076void
2077tile(Monitor *m)
2078{
2079 unsigned int i, n, h, r, oe = enablegaps, ie = enablegaps, mw, my, ty;
2080 Client *c;
2081
2082 for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++);
2083 if (n == 0)
2084 return;
2085 if (smartgaps == n) {
2086 oe = 0; // outer gaps disabled
2087 }
2088
2089 if (n > m->nmaster)
2090 mw = m->nmaster ? (m->ww + m->gappiv*ie) * m->mfact : 0;
2091 else
2092 mw = m->ww - 2*m->gappov*oe + m->gappiv*ie;
2093 for (i = 0, my = ty = m->gappoh*oe, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++)
2094 if (i < m->nmaster) {
2095 r = MIN(n, m->nmaster) - i;
2096 h = (m->wh - my - m->gappoh*oe - m->gappih*ie * (r - 1)) / r;
2097 resize(c, m->wx + m->gappov*oe, m->wy + my, mw - (2*c->bw) - m->gappiv*ie, h - (2*c->bw), 0);
2098 my += HEIGHT(c) + m->gappih*ie;
2099
2100 if (my + HEIGHT(c) < m->wh)
2101 my += HEIGHT(c);
2102 } else {
2103 r = n - i;
2104 h = (m->wh - ty - m->gappoh*oe - m->gappih*ie * (r - 1)) / r;
2105 resize(c, m->wx + mw + m->gappov*oe, m->wy + ty, m->ww - mw - (2*c->bw) - 2*m->gappov*oe, h - (2*c->bw), 0);
2106 ty += HEIGHT(c) + m->gappih*ie;
2107 }
2108}
2109
2110void
2111togglebar(const Arg *arg)
2112{
2113 selmon->showbar = !selmon->showbar;
2114 updatebarpos(selmon);
2115 resizebarwin(selmon);
2116 if (showsystray) {
2117 XWindowChanges wc;
2118 if (!selmon->showbar)
2119 wc.y = -bh;
2120 else if (selmon->showbar) {
2121 wc.y = 0;
2122 if (!selmon->topbar)
2123 wc.y = selmon->mh - bh;
2124 }
2125 XConfigureWindow(dpy, systray->win, CWY, &wc);
2126 }
2127 arrange(selmon);
2128}
2129
2130void
2131togglefloating(const Arg *arg)
2132{
2133 if (!selmon->sel)
2134 return;
2135 if (selmon->sel->isfullscreen) /* no support for fullscreen windows */
2136 return;
2137 selmon->sel->isfloating = !selmon->sel->isfloating || selmon->sel->isfixed;
2138 if (selmon->sel->isfloating)
2139 resize(selmon->sel, selmon->sel->x, selmon->sel->y,
2140 selmon->sel->w, selmon->sel->h, 0);
2141 arrange(selmon);
2142}
2143
2144void
2145togglefullscr(const Arg *arg)
2146{
2147 if(selmon->sel)
2148 setfullscreen(selmon->sel, !selmon->sel->isfullscreen);
2149}
2150
2151void
2152toggletag(const Arg *arg)
2153{
2154 unsigned int newtags;
2155
2156 if (!selmon->sel)
2157 return;
2158 newtags = selmon->sel->tags ^ (arg->ui & TAGMASK);
2159 if (newtags) {
2160 selmon->sel->tags = newtags;
2161 focus(NULL);
2162 arrange(selmon);
2163 }
2164}
2165
2166void
2167toggleview(const Arg *arg)
2168{
2169 unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK);
2170
2171 if (newtagset) {
2172 selmon->tagset[selmon->seltags] = newtagset;
2173 focus(NULL);
2174 arrange(selmon);
2175 }
2176}
2177
2178void
2179unfocus(Client *c, int setfocus)
2180{
2181 if (!c)
2182 return;
2183 grabbuttons(c, 0);
2184 XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel);
2185 if (setfocus) {
2186 XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
2187 XDeleteProperty(dpy, root, netatom[NetActiveWindow]);
2188 }
2189}
2190
2191void
2192unmanage(Client *c, int destroyed)
2193{
2194 Monitor *m = c->mon;
2195 XWindowChanges wc;
2196
2197 if (c->swallowing) {
2198 unswallow(c);
2199 return;
2200 }
2201
2202 Client *s = swallowingclient(c->win);
2203 if (s) {
2204 free(s->swallowing);
2205 s->swallowing = NULL;
2206 arrange(m);
2207 focus(NULL);
2208 return;
2209 }
2210
2211 detach(c);
2212 detachstack(c);
2213 if (!destroyed) {
2214 wc.border_width = c->oldbw;
2215 XGrabServer(dpy); /* avoid race conditions */
2216 XSetErrorHandler(xerrordummy);
2217 XSelectInput(dpy, c->win, NoEventMask);
2218 XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */
2219 XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
2220 setclientstate(c, WithdrawnState);
2221 XSync(dpy, False);
2222 XSetErrorHandler(xerror);
2223 XUngrabServer(dpy);
2224 }
2225 free(c);
2226
2227 if (!s) {
2228 arrange(m);
2229 focus(NULL);
2230 updateclientlist();
2231 }
2232}
2233
2234void
2235unmapnotify(XEvent *e)
2236{
2237 Client *c;
2238 XUnmapEvent *ev = &e->xunmap;
2239
2240 if ((c = wintoclient(ev->window))) {
2241 if (ev->send_event)
2242 setclientstate(c, WithdrawnState);
2243 else
2244 unmanage(c, 0);
2245 }
2246 else if ((c = wintosystrayicon(ev->window))) {
2247 /* KLUDGE! sometimes icons occasionally unmap their windows, but do
2248 * _not_ destroy them. We map those windows back */
2249 XMapRaised(dpy, c->win);
2250 updatesystray();
2251 }
2252}
2253
2254void
2255updatebars(void)
2256{
2257 unsigned int w;
2258 Monitor *m;
2259 XSetWindowAttributes wa = {
2260 .override_redirect = True,
2261 .background_pixmap = ParentRelative,
2262 .event_mask = ButtonPressMask|ExposureMask
2263 };
2264 XClassHint ch = {"dwm", "dwm"};
2265 for (m = mons; m; m = m->next) {
2266 if (m->barwin)
2267 continue;
2268 w = m->ww;
2269 if (showsystray && m == systraytomon(m))
2270 w -= getsystraywidth();
2271 m->barwin = XCreateWindow(dpy, root, m->wx, m->by, w, bh, 0, DefaultDepth(dpy, screen),
2272 CopyFromParent, DefaultVisual(dpy, screen),
2273 CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa);
2274 XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor);
2275 if (showsystray && m == systraytomon(m))
2276 XMapRaised(dpy, systray->win);
2277 XMapRaised(dpy, m->barwin);
2278 XSetClassHint(dpy, m->barwin, &ch);
2279 }
2280}
2281
2282void
2283updatebarpos(Monitor *m)
2284{
2285 m->wy = m->my;
2286 m->wh = m->mh;
2287 if (m->showbar) {
2288 m->wh -= bh;
2289 m->by = m->topbar ? m->wy : m->wy + m->wh;
2290 m->wy = m->topbar ? m->wy + bh : m->wy;
2291 } else
2292 m->by = -bh;
2293}
2294
2295void
2296updateclientlist()
2297{
2298 Client *c;
2299 Monitor *m;
2300
2301 XDeleteProperty(dpy, root, netatom[NetClientList]);
2302 for (m = mons; m; m = m->next)
2303 for (c = m->clients; c; c = c->next)
2304 XChangeProperty(dpy, root, netatom[NetClientList],
2305 XA_WINDOW, 32, PropModeAppend,
2306 (unsigned char *) &(c->win), 1);
2307}
2308
2309int
2310updategeom(void)
2311{
2312 int dirty = 0;
2313
2314#ifdef XINERAMA
2315 if (XineramaIsActive(dpy)) {
2316 int i, j, n, nn;
2317 Client *c;
2318 Monitor *m;
2319 XineramaScreenInfo *info = XineramaQueryScreens(dpy, &nn);
2320 XineramaScreenInfo *unique = NULL;
2321
2322 for (n = 0, m = mons; m; m = m->next, n++);
2323 /* only consider unique geometries as separate screens */
2324 unique = ecalloc(nn, sizeof(XineramaScreenInfo));
2325 for (i = 0, j = 0; i < nn; i++)
2326 if (isuniquegeom(unique, j, &info[i]))
2327 memcpy(&unique[j++], &info[i], sizeof(XineramaScreenInfo));
2328 XFree(info);
2329 nn = j;
2330
2331 /* new monitors if nn > n */
2332 for (i = n; i < nn; i++) {
2333 for (m = mons; m && m->next; m = m->next);
2334 if (m)
2335 m->next = createmon();
2336 else
2337 mons = createmon();
2338 }
2339 for (i = 0, m = mons; i < nn && m; m = m->next, i++)
2340 if (i >= n
2341 || unique[i].x_org != m->mx || unique[i].y_org != m->my
2342 || unique[i].width != m->mw || unique[i].height != m->mh)
2343 {
2344 dirty = 1;
2345 m->num = i;
2346 m->mx = m->wx = unique[i].x_org;
2347 m->my = m->wy = unique[i].y_org;
2348 m->mw = m->ww = unique[i].width;
2349 m->mh = m->wh = unique[i].height;
2350 updatebarpos(m);
2351 }
2352 /* removed monitors if n > nn */
2353 for (i = nn; i < n; i++) {
2354 for (m = mons; m && m->next; m = m->next);
2355 while ((c = m->clients)) {
2356 dirty = 1;
2357 m->clients = c->next;
2358 detachstack(c);
2359 c->mon = mons;
2360 attach(c);
2361 attachstack(c);
2362 }
2363 if (m == selmon)
2364 selmon = mons;
2365 cleanupmon(m);
2366 }
2367 free(unique);
2368 } else
2369#endif /* XINERAMA */
2370 { /* default monitor setup */
2371 if (!mons)
2372 mons = createmon();
2373 if (mons->mw != sw || mons->mh != sh) {
2374 dirty = 1;
2375 mons->mw = mons->ww = sw;
2376 mons->mh = mons->wh = sh;
2377 updatebarpos(mons);
2378 }
2379 }
2380 if (dirty) {
2381 selmon = mons;
2382 selmon = wintomon(root);
2383 }
2384 return dirty;
2385}
2386
2387void
2388updatenumlockmask(void)
2389{
2390 unsigned int i, j;
2391 XModifierKeymap *modmap;
2392
2393 numlockmask = 0;
2394 modmap = XGetModifierMapping(dpy);
2395 for (i = 0; i < 8; i++)
2396 for (j = 0; j < modmap->max_keypermod; j++)
2397 if (modmap->modifiermap[i * modmap->max_keypermod + j]
2398 == XKeysymToKeycode(dpy, XK_Num_Lock))
2399 numlockmask = (1 << i);
2400 XFreeModifiermap(modmap);
2401}
2402
2403void
2404updatesizehints(Client *c)
2405{
2406 long msize;
2407 XSizeHints size;
2408
2409 if (!XGetWMNormalHints(dpy, c->win, &size, &msize))
2410 /* size is uninitialized, ensure that size.flags aren't used */
2411 size.flags = PSize;
2412 if (size.flags & PBaseSize) {
2413 c->basew = size.base_width;
2414 c->baseh = size.base_height;
2415 } else if (size.flags & PMinSize) {
2416 c->basew = size.min_width;
2417 c->baseh = size.min_height;
2418 } else
2419 c->basew = c->baseh = 0;
2420 if (size.flags & PResizeInc) {
2421 c->incw = size.width_inc;
2422 c->inch = size.height_inc;
2423 } else
2424 c->incw = c->inch = 0;
2425 if (size.flags & PMaxSize) {
2426 c->maxw = size.max_width;
2427 c->maxh = size.max_height;
2428 } else
2429 c->maxw = c->maxh = 0;
2430 if (size.flags & PMinSize) {
2431 c->minw = size.min_width;
2432 c->minh = size.min_height;
2433 } else if (size.flags & PBaseSize) {
2434 c->minw = size.base_width;
2435 c->minh = size.base_height;
2436 } else
2437 c->minw = c->minh = 0;
2438 if (size.flags & PAspect) {
2439 c->mina = (float)size.min_aspect.y / size.min_aspect.x;
2440 c->maxa = (float)size.max_aspect.x / size.max_aspect.y;
2441 } else
2442 c->maxa = c->mina = 0.0;
2443 c->isfixed = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh);
2444 c->hintsvalid = 1;
2445}
2446
2447void
2448updatestatus(void)
2449{
2450 if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext)))
2451 strcpy(stext, "dwm-"VERSION);
2452 drawbar(selmon);
2453 updatesystray();
2454}
2455
2456
2457void
2458updatesystrayicongeom(Client *i, int w, int h)
2459{
2460 if (i) {
2461 i->h = bh;
2462 if (w == h)
2463 i->w = bh;
2464 else if (h == bh)
2465 i->w = w;
2466 else
2467 i->w = (int) ((float)bh * ((float)w / (float)h));
2468 applysizehints(i, &(i->x), &(i->y), &(i->w), &(i->h), False);
2469 /* force icons into the systray dimensions if they don't want to */
2470 if (i->h > bh) {
2471 if (i->w == i->h)
2472 i->w = bh;
2473 else
2474 i->w = (int) ((float)bh * ((float)i->w / (float)i->h));
2475 i->h = bh;
2476 }
2477 }
2478}
2479
2480void
2481updatesystrayiconstate(Client *i, XPropertyEvent *ev)
2482{
2483 long flags;
2484 int code = 0;
2485
2486 if (!showsystray || !i || ev->atom != xatom[XembedInfo] ||
2487 !(flags = getatomprop(i, xatom[XembedInfo])))
2488 return;
2489
2490 if (flags & XEMBED_MAPPED && !i->tags) {
2491 i->tags = 1;
2492 code = XEMBED_WINDOW_ACTIVATE;
2493 XMapRaised(dpy, i->win);
2494 setclientstate(i, NormalState);
2495 }
2496 else if (!(flags & XEMBED_MAPPED) && i->tags) {
2497 i->tags = 0;
2498 code = XEMBED_WINDOW_DEACTIVATE;
2499 XUnmapWindow(dpy, i->win);
2500 setclientstate(i, WithdrawnState);
2501 }
2502 else
2503 return;
2504 sendevent(i->win, xatom[Xembed], StructureNotifyMask, CurrentTime, code, 0,
2505 systray->win, XEMBED_EMBEDDED_VERSION);
2506}
2507
2508void
2509updatesystray(void)
2510{
2511 XSetWindowAttributes wa;
2512 XWindowChanges wc;
2513 Client *i;
2514 Monitor *m = systraytomon(NULL);
2515 unsigned int x = m->mx + m->mw;
2516 unsigned int sw = TEXTW(stext) - lrpad + systrayspacing;
2517 unsigned int w = 1;
2518
2519 if (!showsystray)
2520 return;
2521 if (systrayonleft)
2522 x -= sw + lrpad / 2;
2523 if (!systray) {
2524 /* init systray */
2525 if (!(systray = (Systray *)calloc(1, sizeof(Systray))))
2526 die("fatal: could not malloc() %u bytes\n", sizeof(Systray));
2527 systray->win = XCreateSimpleWindow(dpy, root, x, m->by, w, bh, 0, 0, scheme[SchemeSel][ColBg].pixel);
2528 wa.event_mask = ButtonPressMask | ExposureMask;
2529 wa.override_redirect = True;
2530 wa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
2531 XSelectInput(dpy, systray->win, SubstructureNotifyMask);
2532 XChangeProperty(dpy, systray->win, netatom[NetSystemTrayOrientation], XA_CARDINAL, 32,
2533 PropModeReplace, (unsigned char *)&netatom[NetSystemTrayOrientationHorz], 1);
2534 XChangeWindowAttributes(dpy, systray->win, CWEventMask|CWOverrideRedirect|CWBackPixel, &wa);
2535 XMapRaised(dpy, systray->win);
2536 XSetSelectionOwner(dpy, netatom[NetSystemTray], systray->win, CurrentTime);
2537 if (XGetSelectionOwner(dpy, netatom[NetSystemTray]) == systray->win) {
2538 sendevent(root, xatom[Manager], StructureNotifyMask, CurrentTime, netatom[NetSystemTray], systray->win, 0, 0);
2539 XSync(dpy, False);
2540 }
2541 else {
2542 fprintf(stderr, "dwm: unable to obtain system tray.\n");
2543 free(systray);
2544 systray = NULL;
2545 return;
2546 }
2547 }
2548 for (w = 0, i = systray->icons; i; i = i->next) {
2549 /* make sure the background color stays the same */
2550 wa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
2551 XChangeWindowAttributes(dpy, i->win, CWBackPixel, &wa);
2552 XMapRaised(dpy, i->win);
2553 w += systrayspacing;
2554 i->x = w;
2555 XMoveResizeWindow(dpy, i->win, i->x, 0, i->w, i->h);
2556 w += i->w;
2557 if (i->mon != m)
2558 i->mon = m;
2559 }
2560 w = w ? w + systrayspacing : 1;
2561 x -= w;
2562 XMoveResizeWindow(dpy, systray->win, x, m->by, w, bh);
2563 wc.x = x; wc.y = m->by; wc.width = w; wc.height = bh;
2564 wc.stack_mode = Above; wc.sibling = m->barwin;
2565 XConfigureWindow(dpy, systray->win, CWX|CWY|CWWidth|CWHeight|CWSibling|CWStackMode, &wc);
2566 XMapWindow(dpy, systray->win);
2567 XMapSubwindows(dpy, systray->win);
2568 /* redraw background */
2569 XSetForeground(dpy, drw->gc, scheme[SchemeNorm][ColBg].pixel);
2570 XFillRectangle(dpy, systray->win, drw->gc, 0, 0, w, bh);
2571 XSync(dpy, False);
2572}
2573
2574void
2575updatetitle(Client *c)
2576{
2577 if (!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name))
2578 gettextprop(c->win, XA_WM_NAME, c->name, sizeof c->name);
2579 if (c->name[0] == '\0') /* hack to mark broken clients */
2580 strcpy(c->name, broken);
2581}
2582
2583void
2584updatewindowtype(Client *c)
2585{
2586 Atom state = getatomprop(c, netatom[NetWMState]);
2587 Atom wtype = getatomprop(c, netatom[NetWMWindowType]);
2588
2589 if (state == netatom[NetWMFullscreen])
2590 setfullscreen(c, 1);
2591 if (wtype == netatom[NetWMWindowTypeDialog])
2592 c->isfloating = 1;
2593}
2594
2595void
2596updatewmhints(Client *c)
2597{
2598 XWMHints *wmh;
2599
2600 if ((wmh = XGetWMHints(dpy, c->win))) {
2601 if (c == selmon->sel && wmh->flags & XUrgencyHint) {
2602 wmh->flags &= ~XUrgencyHint;
2603 XSetWMHints(dpy, c->win, wmh);
2604 } else
2605 c->isurgent = (wmh->flags & XUrgencyHint) ? 1 : 0;
2606 if (wmh->flags & InputHint)
2607 c->neverfocus = !wmh->input;
2608 else
2609 c->neverfocus = 0;
2610 XFree(wmh);
2611 }
2612}
2613
2614void
2615view(const Arg *arg)
2616{
2617 if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags])
2618 return;
2619 selmon->seltags ^= 1; /* toggle sel tagset */
2620 if (arg->ui & TAGMASK)
2621 selmon->tagset[selmon->seltags] = arg->ui & TAGMASK;
2622 focus(NULL);
2623 arrange(selmon);
2624}
2625
2626pid_t
2627winpid(Window w)
2628{
2629
2630 pid_t result = 0;
2631
2632#ifdef __linux__
2633 xcb_res_client_id_spec_t spec = {0};
2634 spec.client = w;
2635 spec.mask = XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID;
2636
2637 xcb_generic_error_t *e = NULL;
2638 xcb_res_query_client_ids_cookie_t c = xcb_res_query_client_ids(xcon, 1, &spec);
2639 xcb_res_query_client_ids_reply_t *r = xcb_res_query_client_ids_reply(xcon, c, &e);
2640
2641 if (!r)
2642 return (pid_t)0;
2643
2644 xcb_res_client_id_value_iterator_t i = xcb_res_query_client_ids_ids_iterator(r);
2645 for (; i.rem; xcb_res_client_id_value_next(&i)) {
2646 spec = i.data->spec;
2647 if (spec.mask & XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID) {
2648 uint32_t *t = xcb_res_client_id_value_value(i.data);
2649 result = *t;
2650 break;
2651 }
2652 }
2653
2654 free(r);
2655
2656 if (result == (pid_t)-1)
2657 result = 0;
2658
2659#endif /* __linux__ */
2660
2661#ifdef __OpenBSD__
2662 Atom type;
2663 int format;
2664 unsigned long len, bytes;
2665 unsigned char *prop;
2666 pid_t ret;
2667
2668 if (XGetWindowProperty(dpy, w, XInternAtom(dpy, "_NET_WM_PID", 0), 0, 1, False, AnyPropertyType, &type, &format, &len, &bytes, &prop) != Success || !prop)
2669 return 0;
2670
2671 ret = *(pid_t*)prop;
2672 XFree(prop);
2673 result = ret;
2674
2675#endif /* __OpenBSD__ */
2676 return result;
2677}
2678
2679pid_t
2680getparentprocess(pid_t p)
2681{
2682 unsigned int v = 0;
2683
2684#ifdef __linux__
2685 FILE *f;
2686 char buf[256];
2687 snprintf(buf, sizeof(buf) - 1, "/proc/%u/stat", (unsigned)p);
2688
2689 if (!(f = fopen(buf, "r")))
2690 return 0;
2691
2692 fscanf(f, "%*u %*s %*c %u", &v);
2693 fclose(f);
2694#endif /* __linux__*/
2695
2696#ifdef __OpenBSD__
2697 int n;
2698 kvm_t *kd;
2699 struct kinfo_proc *kp;
2700
2701 kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, NULL);
2702 if (!kd)
2703 return 0;
2704
2705 kp = kvm_getprocs(kd, KERN_PROC_PID, p, sizeof(*kp), &n);
2706 v = kp->p_ppid;
2707#endif /* __OpenBSD__ */
2708
2709 return (pid_t)v;
2710}
2711
2712int
2713isdescprocess(pid_t p, pid_t c)
2714{
2715 while (p != c && c != 0)
2716 c = getparentprocess(c);
2717
2718 return (int)c;
2719}
2720
2721Client *
2722termforwin(const Client *w)
2723{
2724 Client *c;
2725 Monitor *m;
2726
2727 if (!w->pid || w->isterminal)
2728 return NULL;
2729
2730 for (m = mons; m; m = m->next) {
2731 for (c = m->clients; c; c = c->next) {
2732 if (c->isterminal && !c->swallowing && c->pid && isdescprocess(c->pid, w->pid))
2733 return c;
2734 }
2735 }
2736
2737 return NULL;
2738}
2739
2740Client *
2741swallowingclient(Window w)
2742{
2743 Client *c;
2744 Monitor *m;
2745
2746 for (m = mons; m; m = m->next) {
2747 for (c = m->clients; c; c = c->next) {
2748 if (c->swallowing && c->swallowing->win == w)
2749 return c;
2750 }
2751 }
2752
2753 return NULL;
2754}
2755
2756Client *
2757wintoclient(Window w)
2758{
2759 Client *c;
2760 Monitor *m;
2761
2762 for (m = mons; m; m = m->next)
2763 for (c = m->clients; c; c = c->next)
2764 if (c->win == w)
2765 return c;
2766 return NULL;
2767}
2768
2769Client *
2770wintosystrayicon(Window w) {
2771 Client *i = NULL;
2772
2773 if (!showsystray || !w)
2774 return i;
2775 for (i = systray->icons; i && i->win != w; i = i->next) ;
2776 return i;
2777}
2778
2779Monitor *
2780wintomon(Window w)
2781{
2782 int x, y;
2783 Client *c;
2784 Monitor *m;
2785
2786 if (w == root && getrootptr(&x, &y))
2787 return recttomon(x, y, 1, 1);
2788 for (m = mons; m; m = m->next)
2789 if (w == m->barwin)
2790 return m;
2791 if ((c = wintoclient(w)))
2792 return c->mon;
2793 return selmon;
2794}
2795
2796/* There's no way to check accesses to destroyed windows, thus those cases are
2797 * ignored (especially on UnmapNotify's). Other types of errors call Xlibs
2798 * default error handler, which may call exit. */
2799int
2800xerror(Display *dpy, XErrorEvent *ee)
2801{
2802 if (ee->error_code == BadWindow
2803 || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch)
2804 || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable)
2805 || (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable)
2806 || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable)
2807 || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch)
2808 || (ee->request_code == X_GrabButton && ee->error_code == BadAccess)
2809 || (ee->request_code == X_GrabKey && ee->error_code == BadAccess)
2810 || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable))
2811 return 0;
2812 fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n",
2813 ee->request_code, ee->error_code);
2814 return xerrorxlib(dpy, ee); /* may call exit */
2815}
2816
2817int
2818xerrordummy(Display *dpy, XErrorEvent *ee)
2819{
2820 return 0;
2821}
2822
2823/* Startup Error handler to check if another window manager
2824 * is already running. */
2825int
2826xerrorstart(Display *dpy, XErrorEvent *ee)
2827{
2828 die("dwm: another window manager is already running");
2829 return -1;
2830}
2831
2832Monitor *
2833systraytomon(Monitor *m) {
2834 Monitor *t;
2835 int i, n;
2836 if(!systraypinning) {
2837 if(!m)
2838 return selmon;
2839 return m == selmon ? m : NULL;
2840 }
2841 for(n = 1, t = mons; t && t->next; n++, t = t->next) ;
2842 for(i = 1, t = mons; t && t->next && i < systraypinning; i++, t = t->next) ;
2843 if(systraypinningfailfirst && n < systraypinning)
2844 return mons;
2845 return t;
2846}
2847
2848void
2849zoom(const Arg *arg)
2850{
2851 Client *c = selmon->sel;
2852
2853 if (!selmon->lt[selmon->sellt]->arrange || !c || c->isfloating)
2854 return;
2855 if (c == nexttiled(selmon->clients) && !(c = nexttiled(c->next)))
2856 return;
2857 pop(c);
2858}
2859
2860int
2861main(int argc, char *argv[])
2862{
2863 if (argc == 2 && !strcmp("-v", argv[1]))
2864 die("dwm-"VERSION);
2865 else if (argc != 1)
2866 die("usage: dwm [-v]");
2867 if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())
2868 fputs("warning: no locale support\n", stderr);
2869 if (!(dpy = XOpenDisplay(NULL)))
2870 die("dwm: cannot open display");
2871 if (!(xcon = XGetXCBConnection(dpy)))
2872 die("dwm: cannot get xcb connection\n");
2873 checkotherwm();
2874 setup();
2875#ifdef __OpenBSD__
2876 if (pledge("stdio rpath proc exec ps", NULL) == -1)
2877 die("pledge");
2878#endif /* __OpenBSD__ */
2879 scan();
2880 run();
2881 cleanup();
2882 XCloseDisplay(dpy);
2883 return EXIT_SUCCESS;
2884}