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