aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--Makefile51
-rw-r--r--README48
-rw-r--r--colors.h51
-rw-r--r--config.h148
-rw-r--r--config.mk38
-rw-r--r--drw.c435
-rw-r--r--drw.h57
-rw-r--r--dwm.1176
-rw-r--r--dwm.c2452
-rw-r--r--patch-list5
-rw-r--r--transient.c42
-rw-r--r--util.c35
-rw-r--r--util.h8
-rw-r--r--vanitygaps.c809
15 files changed, 4358 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..2eef289
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
1*.o
2dwm
3patches/*
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..77bcbc0
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,51 @@
1# dwm - dynamic window manager
2# See LICENSE file for copyright and license details.
3
4include config.mk
5
6SRC = drw.c dwm.c util.c
7OBJ = ${SRC:.c=.o}
8
9all: options dwm
10
11options:
12 @echo dwm build options:
13 @echo "CFLAGS = ${CFLAGS}"
14 @echo "LDFLAGS = ${LDFLAGS}"
15 @echo "CC = ${CC}"
16
17.c.o:
18 ${CC} -c ${CFLAGS} $<
19
20${OBJ}: config.h config.mk
21
22config.h:
23 cp config.def.h $@
24
25dwm: ${OBJ}
26 ${CC} -o $@ ${OBJ} ${LDFLAGS}
27
28clean:
29 rm -f dwm ${OBJ} dwm-${VERSION}.tar.gz
30
31dist: clean
32 mkdir -p dwm-${VERSION}
33 cp -R LICENSE Makefile README config.def.h config.mk\
34 dwm.1 drw.h util.h ${SRC} dwm.png transient.c dwm-${VERSION}
35 tar -cf dwm-${VERSION}.tar dwm-${VERSION}
36 gzip dwm-${VERSION}.tar
37 rm -rf dwm-${VERSION}
38
39install: all
40 mkdir -p ${DESTDIR}${PREFIX}/bin
41 cp -f dwm ${DESTDIR}${PREFIX}/bin
42 chmod 755 ${DESTDIR}${PREFIX}/bin/dwm
43 mkdir -p ${DESTDIR}${MANPREFIX}/man1
44 sed "s/VERSION/${VERSION}/g" < dwm.1 > ${DESTDIR}${MANPREFIX}/man1/dwm.1
45 chmod 644 ${DESTDIR}${MANPREFIX}/man1/dwm.1
46
47uninstall:
48 rm -f ${DESTDIR}${PREFIX}/bin/dwm\
49 ${DESTDIR}${MANPREFIX}/man1/dwm.1
50
51.PHONY: all options clean dist install uninstall
diff --git a/README b/README
new file mode 100644
index 0000000..95d4fd0
--- /dev/null
+++ b/README
@@ -0,0 +1,48 @@
1dwm - dynamic window manager
2============================
3dwm is an extremely fast, small, and dynamic window manager for X.
4
5
6Requirements
7------------
8In order to build dwm you need the Xlib header files.
9
10
11Installation
12------------
13Edit config.mk to match your local setup (dwm is installed into
14the /usr/local namespace by default).
15
16Afterwards enter the following command to build and install dwm (if
17necessary as root):
18
19 make clean install
20
21
22Running dwm
23-----------
24Add the following line to your .xinitrc to start dwm using startx:
25
26 exec dwm
27
28In order to connect dwm to a specific display, make sure that
29the DISPLAY environment variable is set correctly, e.g.:
30
31 DISPLAY=foo.bar:1 exec dwm
32
33(This will start dwm on display :1 of the host foo.bar.)
34
35In order to display status info in the bar, you can do something
36like this in your .xinitrc:
37
38 while xsetroot -name "`date` `uptime | sed 's/.*,//'`"
39 do
40 sleep 1
41 done &
42 exec dwm
43
44
45Configuration
46-------------
47The configuration of dwm is done by creating a custom config.h
48and (re)compiling the source code.
diff --git a/colors.h b/colors.h
new file mode 100644
index 0000000..24540c1
--- /dev/null
+++ b/colors.h
@@ -0,0 +1,51 @@
1/*Arc-Dark Variables*/
2static const char ad_base[] = "#404552";
3static const char ad_fg[] = "#d3dae3";
4static const char ad_text[] = "#d3dae3";
5static const char ad_bg[] = "#383c4a";
6static const char ad_sel_fg[] = "#ffffff";
7static const char ad_sel_bg[] = "#5294e2";
8static const char ad_hlght[] = "#5294e2";
9
10/* Dwm Defaults */
11static const char col_gray1[] = "#222222";
12static const char col_gray2[] = "#444444";
13static const char col_gray3[] = "#bbbbbb";
14static const char col_gray4[] = "#eeeeee";
15static const char col_cyan[] = "#005577";
16
17/*Dark Blood Variables*/
18static const char db_base[] = "#0A0608";
19static const char db_fg[] = "#B9B9B9";
20static const char db_text[] = "#B9B9B9";
21static const char db_bg[] = "#232323";
22static const char db_sel_fg[] = "#FF9EA7";
23static const char db_sel_bg[] = "#3D1317";
24static const char db_hlght[] = "#3D1317";
25
26/*Dark Cold Variables*/
27static const char dc_base[] = "#060A0F";
28static const char dc_fg[] = "#6EBEFE";
29static const char dc_text[] = "#6EBEFE";
30static const char dc_bg[] = "#232323";
31static const char dc_sel_fg[] = "#FFFFFF";
32static const char dc_sel_bg[] = "#0D377C";
33static const char dc_hlght[] = "#0D377C";
34
35/*Dark Mint Variables*/
36static const char dm_base[] = "#060A08";
37static const char dm_fg[] = "#78B97E";
38static const char dm_text[] = "#78B97E";
39static const char dm_bg[] = "#232323";
40static const char dm_sel_fg[] = "#9EFFA7";
41static const char dm_sel_bg[] = "#133D17";
42static const char dm_hlght[] = "#133D17";
43
44/*Dark Fire Variables*/
45static const char df_base[] = "#0F0606";
46static const char df_fg[] = "#FEFEFE";
47static const char df_text[] = "#FEFEFE";
48static const char df_bg[] = "#232323";
49static const char df_sel_fg[] = "#FFFFFF";
50static const char df_sel_bg[] = "#7C0D0D";
51static const char df_hlght[] = "#7C0D0D";
diff --git a/config.h b/config.h
new file mode 100644
index 0000000..fab08c9
--- /dev/null
+++ b/config.h
@@ -0,0 +1,148 @@
1/* See LICENSE file for copyright and license details. */
2#define TERMCLASS "URxvt"
3#define TERM "urxvt"
4
5/* appearance */
6static const unsigned int borderpx = 3; /* border pixel of windows */
7static const unsigned int snap = 32; /* snap pixel */
8static const unsigned int gappih = 20; /* horiz inner gap between windows */
9static const unsigned int gappiv = 10; /* vert inner gap between windows */
10static const unsigned int gappoh = 10; /* horiz outer gap between windows and screen edge */
11static const unsigned int gappov = 30; /* vert outer gap between windows and screen edge */
12static const int swallowfloating = 0; /* 1 means swallow floating windows by default */
13static int smartgaps = 0; /* 1 means no outer gap when there is only one window */
14static const int showbar = 1; /* 0 means no bar */
15static const int topbar = 1; /* 0 means bottom bar */
16static const char *fonts[] = { "monospace:size=10"};
17static const char dmenufont[] = "monospace:size=10";
18#include "colors.h"
19static const char *colors[][3] = {
20 /* fg bg border */
21 [SchemeNorm] = { ad_fg, ad_bg, ad_base },
22 [SchemeSel] = { ad_sel_fg, ad_sel_bg, ad_hlght },
23};
24
25/* tagging */
26static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
27
28
29static const Rule rules[] = {
30 /* xprop(1):
31 * WM_CLASS(STRING) = instance, class
32 * WM_NAME(STRING) = title
33 */
34 /* class instance title tags mask isfloating isterminal noswallow monitor */
35 { "Firefox", NULL, NULL, 0, 0, 0, -1, -1 },
36 { "URxvt", NULL, NULL, 0, 0, 1, 0, -1 },
37 { "Steam", NULL, NULL, 1, 0, 0, 0, -1 },
38 { NULL, "cmus", NULL, 1 << 8, 0, 1, 1, -1 },
39 { NULL, "ranger", NULL, 0, 0, 1, 1, -1 },
40 { "KeePassXC", NULL, NULL, 1 << 7, 0, 0, 0, -1 },
41 { NULL, NULL, "Event Tester", 0, 0, 0, 1, -1 }, /* xev */
42};
43
44/* layout(s) */
45static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */
46static const int nmaster = 1; /* number of clients in master area */
47static const int resizehints = 1; /* 1 means respect size hints in tiled resizals */
48
49#define FORCE_VSPLIT 1 /* nrowgrid layout: force two clients to always split vertically */
50#include "vanitygaps.c"
51
52static const Layout layouts[] = {
53 /* symbol arrange function */
54 { "[]=", tile }, /* first entry is default, Master on left - slaves on right */
55 { "TTT", bstack }, /* Master on top - slaves on bottom */
56 { "[@]", spiral }, /* Fibonacci */
57 { "HHH", grid }, /* All windows in a grid view */
58 { "><>", NULL }, // no layout function means floating behavior
59 /* Other available layouts that I do not use
60 { "[\\]", dwindle },
61 { "[M]", monocle },
62 { "H[]", deck },
63 { "===", bstackhoriz },
64 { "###", nrowgrid },
65 { "---", horizgrid },
66 { ":::", gaplessgrid },
67 { "|M|", centeredmaster },
68 { ">M>", centeredfloatingmaster },
69 */
70 { NULL, NULL },
71};
72
73/* key definitions */
74#define MODKEY Mod4Mask
75#define TAGKEYS(KEY,TAG) \
76 { MODKEY, KEY, view, {.ui = 1 << TAG} }, \
77 { MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \
78 { MODKEY|ShiftMask, KEY, tag, {.ui = 1 << TAG} }, \
79 { MODKEY|ControlMask|ShiftMask, KEY, toggletag, {.ui = 1 << TAG} },
80
81/* helper for spawning shell commands in the pre dwm-5.0 fashion */
82#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } }
83#define STATUSBAR "dwmblocks"
84static const char *termcmd[] = {"urxvt"};
85
86static Key keys[] = {
87 /* modifier key function argument */
88 /* Layouts */
89 { MODKEY, XK_y, setlayout, {.v = &layouts[0]} },
90 { MODKEY, XK_u, setlayout, {.v = &layouts[1]} },
91 { MODKEY, XK_i, setlayout, {.v = &layouts[2]} },
92 { MODKEY, XK_o, setlayout, {.v = &layouts[3]} },
93 /* Stack Control */
94 { MODKEY, XK_h, setmfact, {.f = -0.05} },
95 { MODKEY, XK_j, focusstack, {.i = +1 } },
96 { MODKEY, XK_k, focusstack, {.i = -1 } },
97 { MODKEY, XK_l, setmfact, {.f = +0.05} },
98 { MODKEY, XK_p, incnmaster, {.i = +1 } },
99 { MODKEY|ShiftMask, XK_p, incnmaster, {.i = -1 } },
100 { MODKEY, XK_space, zoom, {0} },
101 /* Tag Control */
102 TAGKEYS( XK_1, 0)
103 TAGKEYS( XK_2, 1)
104 TAGKEYS( XK_3, 2)
105 TAGKEYS( XK_4, 3)
106 TAGKEYS( XK_5, 4)
107 TAGKEYS( XK_6, 5)
108 TAGKEYS( XK_7, 6)
109 TAGKEYS( XK_8, 7)
110 TAGKEYS( XK_9, 8)
111 { MODKEY, XK_0, view, {.ui = ~0 } },
112 { MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } },
113 { MODKEY, XK_Tab, view, {0} },
114 { MODKEY, XK_Left, focusmon, {.i = -1 } },
115 { MODKEY|ShiftMask, XK_Left, focusmon, {.i = -1 } },
116 { MODKEY, XK_Right, focusmon, {.i = +1 } },
117 { MODKEY|ShiftMask, XK_Right, tagmon, {.i = +1 } },
118 /* Commands */
119 { MODKEY|ShiftMask, XK_q, killclient, {0} },
120 { MODKEY|ShiftMask, XK_Escape, quit, {0} },
121 { MODKEY|ShiftMask, XK_space, togglefloating, {0} },
122 { MODKEY, XK_b, togglebar, {0} },
123 /* Gaps Control */
124 { MODKEY, XK_semicolon, incrgaps, {.i = +1 } },
125 { MODKEY|ShiftMask, XK_semicolon, defaultgaps, {0} },
126 { MODKEY, XK_apostrophe, incrgaps, {.i = -1 } },
127 { MODKEY|ShiftMask, XK_apostrophe, togglegaps, {0} },
128};
129
130/* button definitions */
131/* click can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */
132static Button buttons[] = {
133 /* click event mask button function argument */
134 { ClkLtSymbol, 0, Button1, setlayout, {0} },
135 { ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[5]}},
136 { ClkWinTitle, 0, Button2, zoom, {0} },
137 { ClkStatusText, 0, Button1, sigstatusbar, {.i = 1} },
138 { ClkStatusText, 0, Button2, sigstatusbar, {.i = 2} },
139 { ClkStatusText, 0, Button3, sigstatusbar, {.i = 3} },
140 { ClkClientWin, MODKEY, Button1, movemouse, {0} },
141 { ClkClientWin, MODKEY, Button2, togglefloating, {0} },
142 { ClkClientWin, MODKEY, Button3, resizemouse, {0} },
143 { ClkTagBar, 0, Button1, view, {0} },
144 { ClkTagBar, 0, Button3, toggleview, {0} },
145 { ClkTagBar, MODKEY, Button1, tag, {0} },
146 { ClkTagBar, MODKEY, Button3, toggletag, {0} },
147};
148
diff --git a/config.mk b/config.mk
new file mode 100644
index 0000000..0c20086
--- /dev/null
+++ b/config.mk
@@ -0,0 +1,38 @@
1# dwm version
2VERSION = 6.2
3
4# Customize below to fit your system
5
6# paths
7PREFIX = /usr/local
8MANPREFIX = /usr/share/man
9
10X11INC = /usr/X11R6/include
11X11LIB = /usr/X11R6/lib
12
13# Xinerama, comment if you don't want it
14XINERAMALIBS = -lXinerama
15XINERAMAFLAGS = -DXINERAMA
16
17# freetype
18FREETYPELIBS = -lfontconfig -lXft
19FREETYPEINC = /usr/include/freetype2
20# OpenBSD (uncomment)
21#FREETYPEINC = ${X11INC}/freetype2
22
23# includes and libs
24INCS = -I${X11INC} -I${FREETYPEINC}
25LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} -lX11-xcb -lxcb -lxcb-res
26
27# flags
28CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=2 -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS}
29#CFLAGS = -g -std=c99 -pedantic -Wall -O0 ${INCS} ${CPPFLAGS}
30CFLAGS = -std=c99 -pedantic -Wall -Wno-deprecated-declarations -Os ${INCS} ${CPPFLAGS}
31LDFLAGS = ${LIBS}
32
33# Solaris
34#CFLAGS = -fast ${INCS} -DVERSION=\"${VERSION}\"
35#LDFLAGS = ${LIBS}
36
37# compiler and linker
38CC = cc
diff --git a/drw.c b/drw.c
new file mode 100644
index 0000000..8fd1ca4
--- /dev/null
+++ b/drw.c
@@ -0,0 +1,435 @@
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 free(drw);
99}
100
101/* This function is an implementation detail. Library users should use
102 * drw_fontset_create instead.
103 */
104static Fnt *
105xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern)
106{
107 Fnt *font;
108 XftFont *xfont = NULL;
109 FcPattern *pattern = NULL;
110
111 if (fontname) {
112 /* Using the pattern found at font->xfont->pattern does not yield the
113 * same substitution results as using the pattern returned by
114 * FcNameParse; using the latter results in the desired fallback
115 * behaviour whereas the former just results in missing-character
116 * rectangles being drawn, at least with some fonts. */
117 if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fontname))) {
118 fprintf(stderr, "error, cannot load font from name: '%s'\n", fontname);
119 return NULL;
120 }
121 if (!(pattern = FcNameParse((FcChar8 *) fontname))) {
122 fprintf(stderr, "error, cannot parse font name to pattern: '%s'\n", fontname);
123 XftFontClose(drw->dpy, xfont);
124 return NULL;
125 }
126 } else if (fontpattern) {
127 if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) {
128 fprintf(stderr, "error, cannot load font from pattern.\n");
129 return NULL;
130 }
131 } else {
132 die("no font specified.");
133 }
134
135 /* Do not allow using color fonts. This is a workaround for a BadLength
136 * error from Xft with color glyphs. Modelled on the Xterm workaround. See
137 * https://bugzilla.redhat.com/show_bug.cgi?id=1498269
138 * https://lists.suckless.org/dev/1701/30932.html
139 * https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=916349
140 * and lots more all over the internet.
141 */
142 FcBool iscol;
143 if(FcPatternGetBool(xfont->pattern, FC_COLOR, 0, &iscol) == FcResultMatch && iscol) {
144 XftFontClose(drw->dpy, xfont);
145 return NULL;
146 }
147
148 font = ecalloc(1, sizeof(Fnt));
149 font->xfont = xfont;
150 font->pattern = pattern;
151 font->h = xfont->ascent + xfont->descent;
152 font->dpy = drw->dpy;
153
154 return font;
155}
156
157static void
158xfont_free(Fnt *font)
159{
160 if (!font)
161 return;
162 if (font->pattern)
163 FcPatternDestroy(font->pattern);
164 XftFontClose(font->dpy, font->xfont);
165 free(font);
166}
167
168Fnt*
169drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount)
170{
171 Fnt *cur, *ret = NULL;
172 size_t i;
173
174 if (!drw || !fonts)
175 return NULL;
176
177 for (i = 1; i <= fontcount; i++) {
178 if ((cur = xfont_create(drw, fonts[fontcount - i], NULL))) {
179 cur->next = ret;
180 ret = cur;
181 }
182 }
183 return (drw->fonts = ret);
184}
185
186void
187drw_fontset_free(Fnt *font)
188{
189 if (font) {
190 drw_fontset_free(font->next);
191 xfont_free(font);
192 }
193}
194
195void
196drw_clr_create(Drw *drw, Clr *dest, const char *clrname)
197{
198 if (!drw || !dest || !clrname)
199 return;
200
201 if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen),
202 DefaultColormap(drw->dpy, drw->screen),
203 clrname, dest))
204 die("error, cannot allocate color '%s'", clrname);
205}
206
207/* Wrapper to create color schemes. The caller has to call free(3) on the
208 * returned color scheme when done using it. */
209Clr *
210drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount)
211{
212 size_t i;
213 Clr *ret;
214
215 /* need at least two colors for a scheme */
216 if (!drw || !clrnames || clrcount < 2 || !(ret = ecalloc(clrcount, sizeof(XftColor))))
217 return NULL;
218
219 for (i = 0; i < clrcount; i++)
220 drw_clr_create(drw, &ret[i], clrnames[i]);
221 return ret;
222}
223
224void
225drw_setfontset(Drw *drw, Fnt *set)
226{
227 if (drw)
228 drw->fonts = set;
229}
230
231void
232drw_setscheme(Drw *drw, Clr *scm)
233{
234 if (drw)
235 drw->scheme = scm;
236}
237
238void
239drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert)
240{
241 if (!drw || !drw->scheme)
242 return;
243 XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme[ColBg].pixel : drw->scheme[ColFg].pixel);
244 if (filled)
245 XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
246 else
247 XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w - 1, h - 1);
248}
249
250int
251drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert)
252{
253 char buf[1024];
254 int ty;
255 unsigned int ew;
256 XftDraw *d = NULL;
257 Fnt *usedfont, *curfont, *nextfont;
258 size_t i, len;
259 int utf8strlen, utf8charlen, render = x || y || w || h;
260 long utf8codepoint = 0;
261 const char *utf8str;
262 FcCharSet *fccharset;
263 FcPattern *fcpattern;
264 FcPattern *match;
265 XftResult result;
266 int charexists = 0;
267
268 if (!drw || (render && !drw->scheme) || !text || !drw->fonts)
269 return 0;
270
271 if (!render) {
272 w = ~w;
273 } else {
274 XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel);
275 XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
276 d = XftDrawCreate(drw->dpy, drw->drawable,
277 DefaultVisual(drw->dpy, drw->screen),
278 DefaultColormap(drw->dpy, drw->screen));
279 x += lpad;
280 w -= lpad;
281 }
282
283 usedfont = drw->fonts;
284 while (1) {
285 utf8strlen = 0;
286 utf8str = text;
287 nextfont = NULL;
288 while (*text) {
289 utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ);
290 for (curfont = drw->fonts; curfont; curfont = curfont->next) {
291 charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint);
292 if (charexists) {
293 if (curfont == usedfont) {
294 utf8strlen += utf8charlen;
295 text += utf8charlen;
296 } else {
297 nextfont = curfont;
298 }
299 break;
300 }
301 }
302
303 if (!charexists || nextfont)
304 break;
305 else
306 charexists = 0;
307 }
308
309 if (utf8strlen) {
310 drw_font_getexts(usedfont, utf8str, utf8strlen, &ew, NULL);
311 /* shorten text if necessary */
312 for (len = MIN(utf8strlen, sizeof(buf) - 1); len && ew > w; len--)
313 drw_font_getexts(usedfont, utf8str, len, &ew, NULL);
314
315 if (len) {
316 memcpy(buf, utf8str, len);
317 buf[len] = '\0';
318 if (len < utf8strlen)
319 for (i = len; i && i > len - 3; buf[--i] = '.')
320 ; /* NOP */
321
322 if (render) {
323 ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent;
324 XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg],
325 usedfont->xfont, x, ty, (XftChar8 *)buf, len);
326 }
327 x += ew;
328 w -= ew;
329 }
330 }
331
332 if (!*text) {
333 break;
334 } else if (nextfont) {
335 charexists = 0;
336 usedfont = nextfont;
337 } else {
338 /* Regardless of whether or not a fallback font is found, the
339 * character must be drawn. */
340 charexists = 1;
341
342 fccharset = FcCharSetCreate();
343 FcCharSetAddChar(fccharset, utf8codepoint);
344
345 if (!drw->fonts->pattern) {
346 /* Refer to the comment in xfont_create for more information. */
347 die("the first font in the cache must be loaded from a font string.");
348 }
349
350 fcpattern = FcPatternDuplicate(drw->fonts->pattern);
351 FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset);
352 FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue);
353 FcPatternAddBool(fcpattern, FC_COLOR, FcFalse);
354
355 FcConfigSubstitute(NULL, fcpattern, FcMatchPattern);
356 FcDefaultSubstitute(fcpattern);
357 match = XftFontMatch(drw->dpy, drw->screen, fcpattern, &result);
358
359 FcCharSetDestroy(fccharset);
360 FcPatternDestroy(fcpattern);
361
362 if (match) {
363 usedfont = xfont_create(drw, NULL, match);
364 if (usedfont && XftCharExists(drw->dpy, usedfont->xfont, utf8codepoint)) {
365 for (curfont = drw->fonts; curfont->next; curfont = curfont->next)
366 ; /* NOP */
367 curfont->next = usedfont;
368 } else {
369 xfont_free(usedfont);
370 usedfont = drw->fonts;
371 }
372 }
373 }
374 }
375 if (d)
376 XftDrawDestroy(d);
377
378 return x + (render ? w : 0);
379}
380
381void
382drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h)
383{
384 if (!drw)
385 return;
386
387 XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y);
388 XSync(drw->dpy, False);
389}
390
391unsigned int
392drw_fontset_getwidth(Drw *drw, const char *text)
393{
394 if (!drw || !drw->fonts || !text)
395 return 0;
396 return drw_text(drw, 0, 0, 0, 0, 0, text, 0);
397}
398
399void
400drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h)
401{
402 XGlyphInfo ext;
403
404 if (!font || !text)
405 return;
406
407 XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext);
408 if (w)
409 *w = ext.xOff;
410 if (h)
411 *h = font->h;
412}
413
414Cur *
415drw_cur_create(Drw *drw, int shape)
416{
417 Cur *cur;
418
419 if (!drw || !(cur = ecalloc(1, sizeof(Cur))))
420 return NULL;
421
422 cur->cursor = XCreateFontCursor(drw->dpy, shape);
423
424 return cur;
425}
426
427void
428drw_cur_free(Drw *drw, Cur *cursor)
429{
430 if (!cursor)
431 return;
432
433 XFreeCursor(drw->dpy, cursor->cursor);
434 free(cursor);
435}
diff --git a/drw.h b/drw.h
new file mode 100644
index 0000000..4bcd5ad
--- /dev/null
+++ b/drw.h
@@ -0,0 +1,57 @@
1/* See LICENSE file for copyright and license details. */
2
3typedef struct {
4 Cursor cursor;
5} Cur;
6
7typedef struct Fnt {
8 Display *dpy;
9 unsigned int h;
10 XftFont *xfont;
11 FcPattern *pattern;
12 struct Fnt *next;
13} Fnt;
14
15enum { ColFg, ColBg, ColBorder }; /* Clr scheme index */
16typedef XftColor Clr;
17
18typedef struct {
19 unsigned int w, h;
20 Display *dpy;
21 int screen;
22 Window root;
23 Drawable drawable;
24 GC gc;
25 Clr *scheme;
26 Fnt *fonts;
27} Drw;
28
29/* Drawable abstraction */
30Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h);
31void drw_resize(Drw *drw, unsigned int w, unsigned int h);
32void drw_free(Drw *drw);
33
34/* Fnt abstraction */
35Fnt *drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount);
36void drw_fontset_free(Fnt* set);
37unsigned int drw_fontset_getwidth(Drw *drw, const char *text);
38void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h);
39
40/* Colorscheme abstraction */
41void drw_clr_create(Drw *drw, Clr *dest, const char *clrname);
42Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount);
43
44/* Cursor abstraction */
45Cur *drw_cur_create(Drw *drw, int shape);
46void drw_cur_free(Drw *drw, Cur *cursor);
47
48/* Drawing context manipulation */
49void drw_setfontset(Drw *drw, Fnt *set);
50void drw_setscheme(Drw *drw, Clr *scm);
51
52/* Drawing functions */
53void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert);
54int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert);
55
56/* Map functions */
57void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h);
diff --git a/dwm.1 b/dwm.1
new file mode 100644
index 0000000..13b3729
--- /dev/null
+++ b/dwm.1
@@ -0,0 +1,176 @@
1.TH DWM 1 dwm\-VERSION
2.SH NAME
3dwm \- dynamic window manager
4.SH SYNOPSIS
5.B dwm
6.RB [ \-v ]
7.SH DESCRIPTION
8dwm is a dynamic window manager for X. It manages windows in tiled, monocle
9and floating layouts. Either layout can be applied dynamically, optimising the
10environment for the application in use and the task performed.
11.P
12In tiled layouts windows are managed in a master and stacking area. The master
13area on the left contains one window by default, and the stacking area on the
14right contains all other windows. The number of master area windows can be
15adjusted from zero to an arbitrary number. In monocle layout all windows are
16maximised to the screen size. In floating layout windows can be resized and
17moved freely. Dialog windows are always managed floating, regardless of the
18layout applied.
19.P
20Windows are grouped by tags. Each window can be tagged with one or multiple
21tags. Selecting certain tags displays all windows with these tags.
22.P
23Each screen contains a small status bar which displays all available tags, the
24layout, the title of the focused window, and the text read from the root window
25name property, if the screen is focused. A floating window is indicated with an
26empty square and a maximised floating window is indicated with a filled square
27before the windows title. The selected tags are indicated with a different
28color. The tags of the focused window are indicated with a filled square in the
29top left corner. The tags which are applied to one or more windows are
30indicated with an empty square in the top left corner.
31.P
32dwm draws a small border around windows to indicate the focus state.
33.SH OPTIONS
34.TP
35.B \-v
36prints version information to standard output, then exits.
37.SH USAGE
38.SS Status bar
39.TP
40.B X root window name
41is read and displayed in the status text area. It can be set with the
42.BR xsetroot (1)
43command.
44.TP
45.B Button1
46click on a tag label to display all windows with that tag, click on the layout
47label toggles between tiled and floating layout.
48.TP
49.B Button3
50click on a tag label adds/removes all windows with that tag to/from the view.
51.TP
52.B Mod1\-Button1
53click on a tag label applies that tag to the focused window.
54.TP
55.B Mod1\-Button3
56click on a tag label adds/removes that tag to/from the focused window.
57.SS Keyboard commands
58.TP
59.B Mod1\-Shift\-Return
60Start
61.BR st(1).
62.TP
63.B Mod1\-p
64Spawn
65.BR dmenu(1)
66for launching other programs.
67.TP
68.B Mod1\-,
69Focus previous screen, if any.
70.TP
71.B Mod1\-.
72Focus next screen, if any.
73.TP
74.B Mod1\-Shift\-,
75Send focused window to previous screen, if any.
76.TP
77.B Mod1\-Shift\-.
78Send focused window to next screen, if any.
79.TP
80.B Mod1\-b
81Toggles bar on and off.
82.TP
83.B Mod1\-t
84Sets tiled layout.
85.TP
86.B Mod1\-f
87Sets floating layout.
88.TP
89.B Mod1\-m
90Sets monocle layout.
91.TP
92.B Mod1\-space
93Toggles between current and previous layout.
94.TP
95.B Mod1\-j
96Focus next window.
97.TP
98.B Mod1\-k
99Focus previous window.
100.TP
101.B Mod1\-i
102Increase number of windows in master area.
103.TP
104.B Mod1\-d
105Decrease number of windows in master area.
106.TP
107.B Mod1\-l
108Increase master area size.
109.TP
110.B Mod1\-h
111Decrease master area size.
112.TP
113.B Mod1\-Return
114Zooms/cycles focused window to/from master area (tiled layouts only).
115.TP
116.B Mod1\-Shift\-c
117Close focused window.
118.TP
119.B Mod1\-Shift\-space
120Toggle focused window between tiled and floating state.
121.TP
122.B Mod1\-Tab
123Toggles to the previously selected tags.
124.TP
125.B Mod1\-Shift\-[1..n]
126Apply nth tag to focused window.
127.TP
128.B Mod1\-Shift\-0
129Apply all tags to focused window.
130.TP
131.B Mod1\-Control\-Shift\-[1..n]
132Add/remove nth tag to/from focused window.
133.TP
134.B Mod1\-[1..n]
135View all windows with nth tag.
136.TP
137.B Mod1\-0
138View all windows with any tag.
139.TP
140.B Mod1\-Control\-[1..n]
141Add/remove all windows with nth tag to/from the view.
142.TP
143.B Mod1\-Shift\-q
144Quit dwm.
145.SS Mouse commands
146.TP
147.B Mod1\-Button1
148Move focused window while dragging. Tiled windows will be toggled to the floating state.
149.TP
150.B Mod1\-Button2
151Toggles focused window between floating and tiled state.
152.TP
153.B Mod1\-Button3
154Resize focused window while dragging. Tiled windows will be toggled to the floating state.
155.SH CUSTOMIZATION
156dwm is customized by creating a custom config.h and (re)compiling the source
157code. This keeps it fast, secure and simple.
158.SH SEE ALSO
159.BR dmenu (1),
160.BR st (1)
161.SH ISSUES
162Java applications which use the XToolkit/XAWT backend may draw grey windows
163only. The XToolkit/XAWT backend breaks ICCCM-compliance in recent JDK 1.5 and early
164JDK 1.6 versions, because it assumes a reparenting window manager. Possible workarounds
165are using JDK 1.4 (which doesn't contain the XToolkit/XAWT backend) or setting the
166environment variable
167.BR AWT_TOOLKIT=MToolkit
168(to use the older Motif backend instead) or running
169.B xprop -root -f _NET_WM_NAME 32a -set _NET_WM_NAME LG3D
170or
171.B wmname LG3D
172(to pretend that a non-reparenting window manager is running that the
173XToolkit/XAWT backend can recognize) or when using OpenJDK setting the environment variable
174.BR _JAVA_AWT_WM_NONREPARENTING=1 .
175.SH BUGS
176Send all bug reports with a patch to hackers@suckless.org.
diff --git a/dwm.c b/dwm.c
new file mode 100644
index 0000000..de4eefc
--- /dev/null
+++ b/dwm.c
@@ -0,0 +1,2452 @@
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
46#include "drw.h"
47#include "util.h"
48
49/* macros */
50#define BUTTONMASK (ButtonPressMask|ButtonReleaseMask)
51#define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask))
52#define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \
53 * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy)))
54#define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags]))
55#define LENGTH(X) (sizeof X / sizeof X[0])
56#define MOUSEMASK (BUTTONMASK|PointerMotionMask)
57#define WIDTH(X) ((X)->w + 2 * (X)->bw)
58#define HEIGHT(X) ((X)->h + 2 * (X)->bw)
59#define TAGMASK ((1 << LENGTH(tags)) - 1)
60#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad)
61
62/* enums */
63enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */
64enum { SchemeNorm, SchemeSel }; /* color schemes */
65enum { NetSupported, NetWMName, NetWMState, NetWMCheck,
66 NetWMFullscreen, NetActiveWindow, NetWMWindowType,
67 NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */
68enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */
69enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle,
70 ClkClientWin, ClkRootWin, ClkLast }; /* clicks */
71
72typedef union {
73 int i;
74 unsigned int ui;
75 float f;
76 const void *v;
77} Arg;
78
79typedef struct {
80 unsigned int click;
81 unsigned int mask;
82 unsigned int button;
83 void (*func)(const Arg *arg);
84 const Arg arg;
85} Button;
86
87typedef struct Monitor Monitor;
88typedef struct Client Client;
89struct Client {
90 char name[256];
91 float mina, maxa;
92 int x, y, w, h;
93 int oldx, oldy, oldw, oldh;
94 int basew, baseh, incw, inch, maxw, maxh, minw, minh;
95 int bw, oldbw;
96 unsigned int tags;
97 int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen, isterminal, noswallow;
98 int issteam;
99 pid_t pid;
100 Client *next;
101 Client *snext;
102 Client *swallowing;
103 Monitor *mon;
104 Window win;
105};
106
107typedef struct {
108 unsigned int mod;
109 KeySym keysym;
110 void (*func)(const Arg *);
111 const Arg arg;
112} Key;
113
114typedef struct {
115 const char *symbol;
116 void (*arrange)(Monitor *);
117} Layout;
118
119struct Monitor {
120 char ltsymbol[16];
121 float mfact;
122 int nmaster;
123 int num;
124 int by; /* bar geometry */
125 int mx, my, mw, mh; /* screen size */
126 int wx, wy, ww, wh; /* window area */
127 int gappih; /* horizontal gap between windows */
128 int gappiv; /* vertical gap between windows */
129 int gappoh; /* horizontal outer gaps */
130 int gappov; /* vertical outer gaps */
131 unsigned int seltags;
132 unsigned int sellt;
133 unsigned int tagset[2];
134 int showbar;
135 int topbar;
136 Client *clients;
137 Client *sel;
138 Client *stack;
139 Monitor *next;
140 Window barwin;
141 const Layout *lt[2];
142};
143
144typedef struct {
145 const char *class;
146 const char *instance;
147 const char *title;
148 unsigned int tags;
149 int isfloating;
150 int isterminal;
151 int noswallow;
152 int monitor;
153} Rule;
154
155/* function declarations */
156static void applyrules(Client *c);
157static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact);
158static void arrange(Monitor *m);
159static void arrangemon(Monitor *m);
160static void attach(Client *c);
161static void attachstack(Client *c);
162static void buttonpress(XEvent *e);
163static void checkotherwm(void);
164static void cleanup(void);
165static void cleanupmon(Monitor *mon);
166static void clientmessage(XEvent *e);
167static void configure(Client *c);
168static void configurenotify(XEvent *e);
169static void configurerequest(XEvent *e);
170static Monitor *createmon(void);
171static void destroynotify(XEvent *e);
172static void detach(Client *c);
173static void detachstack(Client *c);
174static Monitor *dirtomon(int dir);
175static void drawbar(Monitor *m);
176static void drawbars(void);
177static void enternotify(XEvent *e);
178static void expose(XEvent *e);
179static void focus(Client *c);
180static void focusin(XEvent *e);
181static void focusmon(const Arg *arg);
182static void focusstack(const Arg *arg);
183static int getrootptr(int *x, int *y);
184static long getstate(Window w);
185static pid_t getstatusbarpid();
186static int gettextprop(Window w, Atom atom, char *text, unsigned int size);
187static void grabbuttons(Client *c, int focused);
188static void grabkeys(void);
189static void incnmaster(const Arg *arg);
190static void keypress(XEvent *e);
191static void killclient(const Arg *arg);
192static void manage(Window w, XWindowAttributes *wa);
193static void mappingnotify(XEvent *e);
194static void maprequest(XEvent *e);
195static void monocle(Monitor *m);
196static void motionnotify(XEvent *e);
197static void movemouse(const Arg *arg);
198static Client *nexttiled(Client *c);
199static void pop(Client *);
200static void propertynotify(XEvent *e);
201static void quit(const Arg *arg);
202static Monitor *recttomon(int x, int y, int w, int h);
203static void resize(Client *c, int x, int y, int w, int h, int interact);
204static void resizeclient(Client *c, int x, int y, int w, int h);
205static void resizemouse(const Arg *arg);
206static void restack(Monitor *m);
207static void run(void);
208static void scan(void);
209static int sendevent(Client *c, Atom proto);
210static void sendmon(Client *c, Monitor *m);
211static void setclientstate(Client *c, long state);
212static void setfocus(Client *c);
213static void setfullscreen(Client *c, int fullscreen);
214static void setlayout(const Arg *arg);
215static void setmfact(const Arg *arg);
216static void setup(void);
217static void seturgent(Client *c, int urg);
218static void showhide(Client *c);
219static void sigchld(int unused);
220static void sigstatusbar(const Arg *arg);
221static void spawn(const Arg *arg);
222static void tag(const Arg *arg);
223static void tagmon(const Arg *arg);
224static void togglebar(const Arg *arg);
225static void togglefloating(const Arg *arg);
226static void toggletag(const Arg *arg);
227static void toggleview(const Arg *arg);
228static void unfocus(Client *c, int setfocus);
229static void unmanage(Client *c, int destroyed);
230static void unmapnotify(XEvent *e);
231static void updatebarpos(Monitor *m);
232static void updatebars(void);
233static void updateclientlist(void);
234static int updategeom(void);
235static void updatenumlockmask(void);
236static void updatesizehints(Client *c);
237static void updatestatus(void);
238static void updatetitle(Client *c);
239static void updatewindowtype(Client *c);
240static void updatewmhints(Client *c);
241static void view(const Arg *arg);
242static Client *wintoclient(Window w);
243static Monitor *wintomon(Window w);
244static int xerror(Display *dpy, XErrorEvent *ee);
245static int xerrordummy(Display *dpy, XErrorEvent *ee);
246static int xerrorstart(Display *dpy, XErrorEvent *ee);
247static void zoom(const Arg *arg);
248
249static pid_t getparentprocess(pid_t p);
250static int isdescprocess(pid_t p, pid_t c);
251static Client *swallowingclient(Window w);
252static Client *termforwin(const Client *c);
253static pid_t winpid(Window w);
254
255/* variables */
256static const char broken[] = "broken";
257static char stext[256];
258static int statusw;
259static int statussig;
260static pid_t statuspid = -1;
261static int screen;
262static int sw, sh; /* X display screen geometry width, height */
263static int bh, blw = 0; /* bar geometry */
264static int lrpad; /* sum of left and right padding for text */
265static int (*xerrorxlib)(Display *, XErrorEvent *);
266static unsigned int numlockmask = 0;
267static void (*handler[LASTEvent]) (XEvent *) = {
268 [ButtonPress] = buttonpress,
269 [ClientMessage] = clientmessage,
270 [ConfigureRequest] = configurerequest,
271 [ConfigureNotify] = configurenotify,
272 [DestroyNotify] = destroynotify,
273 [EnterNotify] = enternotify,
274 [Expose] = expose,
275 [FocusIn] = focusin,
276 [KeyPress] = keypress,
277 [MappingNotify] = mappingnotify,
278 [MapRequest] = maprequest,
279 [MotionNotify] = motionnotify,
280 [PropertyNotify] = propertynotify,
281 [UnmapNotify] = unmapnotify
282};
283static Atom wmatom[WMLast], netatom[NetLast];
284static int running = 1;
285static Cur *cursor[CurLast];
286static Clr **scheme;
287static Display *dpy;
288static Drw *drw;
289static Monitor *mons, *selmon;
290static Window root, wmcheckwin;
291
292static xcb_connection_t *xcon;
293
294/* configuration, allows nested code to access above variables */
295#include "config.h"
296
297/* compile-time check if all tags fit into an unsigned int bit array. */
298struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; };
299
300/* function implementations */
301void
302applyrules(Client *c)
303{
304 const char *class, *instance;
305 unsigned int i;
306 const Rule *r;
307 Monitor *m;
308 XClassHint ch = { NULL, NULL };
309
310 /* rule matching */
311 c->isfloating = 0;
312 c->tags = 0;
313 XGetClassHint(dpy, c->win, &ch);
314 class = ch.res_class ? ch.res_class : broken;
315 instance = ch.res_name ? ch.res_name : broken;
316
317 if (strstr(class, "Steam") || strstr(class, "steam_app_"))
318 c->issteam = 1;
319
320 for (i = 0; i < LENGTH(rules); i++) {
321 r = &rules[i];
322 if ((!r->title || strstr(c->name, r->title))
323 && (!r->class || strstr(class, r->class))
324 && (!r->instance || strstr(instance, r->instance)))
325 {
326 c->isterminal = r->isterminal;
327 c->noswallow = r->noswallow;
328 c->isfloating = r->isfloating;
329 c->tags |= r->tags;
330 for (m = mons; m && m->num != r->monitor; m = m->next);
331 if (m)
332 c->mon = m;
333 }
334 }
335 if (ch.res_class)
336 XFree(ch.res_class);
337 if (ch.res_name)
338 XFree(ch.res_name);
339 c->tags = c->tags & TAGMASK ? c->tags & TAGMASK : c->mon->tagset[c->mon->seltags];
340}
341
342int
343applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact)
344{
345 int baseismin;
346 Monitor *m = c->mon;
347
348 /* set minimum possible */
349 *w = MAX(1, *w);
350 *h = MAX(1, *h);
351 if (interact) {
352 if (*x > sw)
353 *x = sw - WIDTH(c);
354 if (*y > sh)
355 *y = sh - HEIGHT(c);
356 if (*x + *w + 2 * c->bw < 0)
357 *x = 0;
358 if (*y + *h + 2 * c->bw < 0)
359 *y = 0;
360 } else {
361 if (*x >= m->wx + m->ww)
362 *x = m->wx + m->ww - WIDTH(c);
363 if (*y >= m->wy + m->wh)
364 *y = m->wy + m->wh - HEIGHT(c);
365 if (*x + *w + 2 * c->bw <= m->wx)
366 *x = m->wx;
367 if (*y + *h + 2 * c->bw <= m->wy)
368 *y = m->wy;
369 }
370 if (*h < bh)
371 *h = bh;
372 if (*w < bh)
373 *w = bh;
374 if (resizehints || c->isfloating || !c->mon->lt[c->mon->sellt]->arrange) {
375 /* see last two sentences in ICCCM 4.1.2.3 */
376 baseismin = c->basew == c->minw && c->baseh == c->minh;
377 if (!baseismin) { /* temporarily remove base dimensions */
378 *w -= c->basew;
379 *h -= c->baseh;
380 }
381 /* adjust for aspect limits */
382 if (c->mina > 0 && c->maxa > 0) {
383 if (c->maxa < (float)*w / *h)
384 *w = *h * c->maxa + 0.5;
385 else if (c->mina < (float)*h / *w)
386 *h = *w * c->mina + 0.5;
387 }
388 if (baseismin) { /* increment calculation requires this */
389 *w -= c->basew;
390 *h -= c->baseh;
391 }
392 /* adjust for increment value */
393 if (c->incw)
394 *w -= *w % c->incw;
395 if (c->inch)
396 *h -= *h % c->inch;
397 /* restore base dimensions */
398 *w = MAX(*w + c->basew, c->minw);
399 *h = MAX(*h + c->baseh, c->minh);
400 if (c->maxw)
401 *w = MIN(*w, c->maxw);
402 if (c->maxh)
403 *h = MIN(*h, c->maxh);
404 }
405 return *x != c->x || *y != c->y || *w != c->w || *h != c->h;
406}
407
408void
409arrange(Monitor *m)
410{
411 if (m)
412 showhide(m->stack);
413 else for (m = mons; m; m = m->next)
414 showhide(m->stack);
415 if (m) {
416 arrangemon(m);
417 restack(m);
418 } else for (m = mons; m; m = m->next)
419 arrangemon(m);
420}
421
422void
423arrangemon(Monitor *m)
424{
425 strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol);
426 if (m->lt[m->sellt]->arrange)
427 m->lt[m->sellt]->arrange(m);
428}
429
430void
431attach(Client *c)
432{
433 c->next = c->mon->clients;
434 c->mon->clients = c;
435}
436
437void
438attachstack(Client *c)
439{
440 c->snext = c->mon->stack;
441 c->mon->stack = c;
442}
443
444void
445swallow(Client *p, Client *c)
446{
447
448 if (c->noswallow || c->isterminal)
449 return;
450 if (c->noswallow && !swallowfloating && c->isfloating)
451 return;
452
453 detach(c);
454 detachstack(c);
455
456 setclientstate(c, WithdrawnState);
457 XUnmapWindow(dpy, p->win);
458
459 p->swallowing = c;
460 c->mon = p->mon;
461
462 Window w = p->win;
463 p->win = c->win;
464 c->win = w;
465 updatetitle(p);
466 XMoveResizeWindow(dpy, p->win, p->x, p->y, p->w, p->h);
467 arrange(p->mon);
468 configure(p);
469 updateclientlist();
470}
471
472void
473unswallow(Client *c)
474{
475 c->win = c->swallowing->win;
476
477 free(c->swallowing);
478 c->swallowing = NULL;
479
480 /* unfullscreen the client */
481 setfullscreen(c, 0);
482 updatetitle(c);
483 arrange(c->mon);
484 XMapWindow(dpy, c->win);
485 XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h);
486 setclientstate(c, NormalState);
487 focus(NULL);
488 arrange(c->mon);
489}
490
491void
492buttonpress(XEvent *e)
493{
494 unsigned int i, x, click, occ = 0;
495 Arg arg = {0};
496 Client *c;
497 Monitor *m;
498 XButtonPressedEvent *ev = &e->xbutton;
499 char *text, *s, ch;
500
501 click = ClkRootWin;
502 /* focus monitor if necessary */
503 if ((m = wintomon(ev->window)) && m != selmon) {
504 unfocus(selmon->sel, 1);
505 selmon = m;
506 focus(NULL);
507 }
508 if (ev->window == selmon->barwin) {
509 i = x = 0;
510 for (c = m->clients; c; c = c->next)
511 occ |= c->tags == 255 ? 0 : c-> tags;
512 do {
513 /* do not reserve space for vacant tags */
514 if (!(occ & 1 << i || m->tagset[m->seltags] & 1 << i))
515 continue;
516 x += TEXTW(tags[i]);
517 } while (ev->x >= x && ++i < LENGTH(tags));
518 if (i < LENGTH(tags)) {
519 click = ClkTagBar;
520 arg.ui = 1 << i;
521 } else if (ev->x < x + blw)
522 click = ClkLtSymbol;
523 else if (ev->x > selmon->ww - statusw) {
524 x = selmon->ww - statusw;
525 click = ClkStatusText;
526 statussig = 0;
527 for (text = s = stext; *s && x <= ev->x; s++) {
528 if ((unsigned char)(*s) < ' ') {
529 ch = *s;
530 *s = '\0';
531 x += TEXTW(text) - lrpad;
532 *s = ch;
533 text = s + 1;
534 if (x >= ev->x)
535 break;
536 statussig = ch;
537 }
538 }
539 } else
540 click = ClkWinTitle;
541 } else if ((c = wintoclient(ev->window))) {
542 focus(c);
543 restack(selmon);
544 XAllowEvents(dpy, ReplayPointer, CurrentTime);
545 click = ClkClientWin;
546 }
547 for (i = 0; i < LENGTH(buttons); i++)
548 if (click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button
549 && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state))
550 buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg);
551}
552
553void
554checkotherwm(void)
555{
556 xerrorxlib = XSetErrorHandler(xerrorstart);
557 /* this causes an error if some other window manager is running */
558 XSelectInput(dpy, DefaultRootWindow(dpy), SubstructureRedirectMask);
559 XSync(dpy, False);
560 XSetErrorHandler(xerror);
561 XSync(dpy, False);
562}
563
564void
565cleanup(void)
566{
567 Arg a = {.ui = ~0};
568 Layout foo = { "", NULL };
569 Monitor *m;
570 size_t i;
571
572 view(&a);
573 selmon->lt[selmon->sellt] = &foo;
574 for (m = mons; m; m = m->next)
575 while (m->stack)
576 unmanage(m->stack, 0);
577 XUngrabKey(dpy, AnyKey, AnyModifier, root);
578 while (mons)
579 cleanupmon(mons);
580 for (i = 0; i < CurLast; i++)
581 drw_cur_free(drw, cursor[i]);
582 for (i = 0; i < LENGTH(colors); i++)
583 free(scheme[i]);
584 XDestroyWindow(dpy, wmcheckwin);
585 drw_free(drw);
586 XSync(dpy, False);
587 XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
588 XDeleteProperty(dpy, root, netatom[NetActiveWindow]);
589}
590
591void
592cleanupmon(Monitor *mon)
593{
594 Monitor *m;
595
596 if (mon == mons)
597 mons = mons->next;
598 else {
599 for (m = mons; m && m->next != mon; m = m->next);
600 m->next = mon->next;
601 }
602 XUnmapWindow(dpy, mon->barwin);
603 XDestroyWindow(dpy, mon->barwin);
604 free(mon);
605}
606
607void
608clientmessage(XEvent *e)
609{
610 XClientMessageEvent *cme = &e->xclient;
611 Client *c = wintoclient(cme->window);
612
613 if (!c)
614 return;
615 if (cme->message_type == netatom[NetWMState]) {
616 if (cme->data.l[1] == netatom[NetWMFullscreen]
617 || cme->data.l[2] == netatom[NetWMFullscreen])
618 setfullscreen(c, (cme->data.l[0] == 1 /* _NET_WM_STATE_ADD */
619 || (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */ && !c->isfullscreen)));
620 } else if (cme->message_type == netatom[NetActiveWindow]) {
621 if (c != selmon->sel && !c->isurgent)
622 seturgent(c, 1);
623 }
624}
625
626void
627configure(Client *c)
628{
629 XConfigureEvent ce;
630
631 ce.type = ConfigureNotify;
632 ce.display = dpy;
633 ce.event = c->win;
634 ce.window = c->win;
635 ce.x = c->x;
636 ce.y = c->y;
637 ce.width = c->w;
638 ce.height = c->h;
639 ce.border_width = c->bw;
640 ce.above = None;
641 ce.override_redirect = False;
642 XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce);
643}
644
645void
646configurenotify(XEvent *e)
647{
648 Monitor *m;
649 Client *c;
650 XConfigureEvent *ev = &e->xconfigure;
651 int dirty;
652
653 /* TODO: updategeom handling sucks, needs to be simplified */
654 if (ev->window == root) {
655 dirty = (sw != ev->width || sh != ev->height);
656 sw = ev->width;
657 sh = ev->height;
658 if (updategeom() || dirty) {
659 drw_resize(drw, sw, bh);
660 updatebars();
661 for (m = mons; m; m = m->next) {
662 for (c = m->clients; c; c = c->next)
663 if (c->isfullscreen)
664 resizeclient(c, m->mx, m->my, m->mw, m->mh);
665 XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh);
666 }
667 focus(NULL);
668 arrange(NULL);
669 }
670 }
671}
672
673void
674configurerequest(XEvent *e)
675{
676 Client *c;
677 Monitor *m;
678 XConfigureRequestEvent *ev = &e->xconfigurerequest;
679 XWindowChanges wc;
680
681 if ((c = wintoclient(ev->window))) {
682 if (ev->value_mask & CWBorderWidth)
683 c->bw = ev->border_width;
684 else if (c->isfloating || !selmon->lt[selmon->sellt]->arrange) {
685 m = c->mon;
686 if (!c->issteam) {
687 if (ev->value_mask & CWX) {
688 c->oldx = c->x;
689 c->x = m->mx + ev->x;
690 }
691 if (ev->value_mask & CWY) {
692 c->oldy = c->y;
693 c->y = m->my + ev->y;
694 }
695 }
696 if (ev->value_mask & CWWidth) {
697 c->oldw = c->w;
698 c->w = ev->width;
699 }
700 if (ev->value_mask & CWHeight) {
701 c->oldh = c->h;
702 c->h = ev->height;
703 }
704 if ((c->x + c->w) > m->mx + m->mw && c->isfloating)
705 c->x = m->mx + (m->mw / 2 - WIDTH(c) / 2); /* center in x direction */
706 if ((c->y + c->h) > m->my + m->mh && c->isfloating)
707 c->y = m->my + (m->mh / 2 - HEIGHT(c) / 2); /* center in y direction */
708 if ((ev->value_mask & (CWX|CWY)) && !(ev->value_mask & (CWWidth|CWHeight)))
709 configure(c);
710 if (ISVISIBLE(c))
711 XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h);
712 } else
713 configure(c);
714 } else {
715 wc.x = ev->x;
716 wc.y = ev->y;
717 wc.width = ev->width;
718 wc.height = ev->height;
719 wc.border_width = ev->border_width;
720 wc.sibling = ev->above;
721 wc.stack_mode = ev->detail;
722 XConfigureWindow(dpy, ev->window, ev->value_mask, &wc);
723 }
724 XSync(dpy, False);
725}
726
727Monitor *
728createmon(void)
729{
730 Monitor *m;
731
732 m = ecalloc(1, sizeof(Monitor));
733 m->tagset[0] = m->tagset[1] = 1;
734 m->mfact = mfact;
735 m->nmaster = nmaster;
736 m->showbar = showbar;
737 m->topbar = topbar;
738 m->gappih = gappih;
739 m->gappiv = gappiv;
740 m->gappoh = gappoh;
741 m->gappov = gappov;
742 m->lt[0] = &layouts[0];
743 m->lt[1] = &layouts[1 % LENGTH(layouts)];
744 strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol);
745 return m;
746}
747
748void
749destroynotify(XEvent *e)
750{
751 Client *c;
752 XDestroyWindowEvent *ev = &e->xdestroywindow;
753
754 if ((c = wintoclient(ev->window)))
755 unmanage(c, 1);
756
757 else if ((c = swallowingclient(ev->window)))
758 unmanage(c->swallowing, 1);
759}
760
761void
762detach(Client *c)
763{
764 Client **tc;
765
766 for (tc = &c->mon->clients; *tc && *tc != c; tc = &(*tc)->next);
767 *tc = c->next;
768}
769
770void
771detachstack(Client *c)
772{
773 Client **tc, *t;
774
775 for (tc = &c->mon->stack; *tc && *tc != c; tc = &(*tc)->snext);
776 *tc = c->snext;
777
778 if (c == c->mon->sel) {
779 for (t = c->mon->stack; t && !ISVISIBLE(t); t = t->snext);
780 c->mon->sel = t;
781 }
782}
783
784Monitor *
785dirtomon(int dir)
786{
787 Monitor *m = NULL;
788
789 if (dir > 0) {
790 if (!(m = selmon->next))
791 m = mons;
792 } else if (selmon == mons)
793 for (m = mons; m->next; m = m->next);
794 else
795 for (m = mons; m->next != selmon; m = m->next);
796 return m;
797}
798
799void
800drawbar(Monitor *m)
801{
802 int x, w, sw = 0;
803 int boxs = drw->fonts->h / 9;
804 int boxw = drw->fonts->h / 6 + 2;
805 unsigned int i, occ = 0, urg = 0;
806 Client *c;
807
808 /* draw status first so it can be overdrawn by tags later */
809 if (m == selmon) { /* status is only drawn on selected monitor */
810 char *text, *s, ch;
811 drw_setscheme(drw, scheme[SchemeNorm]);
812 x = 0;
813 for (text = s = stext; *s; s++) {
814 if ((unsigned char)(*s) < ' ') {
815 ch = *s;
816 *s = '\0';
817 sw = TEXTW(text) - lrpad;
818 drw_text(drw, m->ww - statusw + x, 0, sw, bh, 0, text, 0);
819 x += sw;
820 *s = ch;
821 text = s + 1;
822 }
823 }
824 sw = TEXTW(text) - lrpad + 2;
825 drw_text(drw, m->ww - statusw + x, 0, sw, bh, 0, text, 0);
826 sw = statusw;
827 }
828
829 for (c = m->clients; c; c = c->next) {
830 occ |= c->tags;
831 if (c->isurgent)
832 urg |= c->tags;
833 }
834 x = 0;
835 for (i = 0; i < LENGTH(tags); i++) {
836 /* do not draw vacant tags */
837 if (!(occ & 1 << i || m ->tagset[m->seltags] & 1 << i))
838 continue;
839
840 w = TEXTW(tags[i]);
841 drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]);
842 drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i);
843 /*if (occ & 1 << i)
844 drw_rect(drw, x + boxs, boxs, boxw, boxw,
845 m == selmon && selmon->sel && selmon->sel->tags & 1 << i,
846 urg & 1 << i);
847 */
848 x += w;
849 }
850 w = blw = TEXTW(m->ltsymbol);
851 drw_setscheme(drw, scheme[SchemeNorm]);
852 x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0);
853
854 if ((w = m->ww - sw - x) > bh) {
855 if (m->sel) {
856 drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]);
857 drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0);
858 if (m->sel->isfloating)
859 drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0);
860 } else {
861 drw_setscheme(drw, scheme[SchemeNorm]);
862 drw_rect(drw, x, 0, w, bh, 1, 1);
863 }
864 }
865 drw_map(drw, m->barwin, 0, 0, m->ww, bh);
866}
867
868void
869drawbars(void)
870{
871 Monitor *m;
872
873 for (m = mons; m; m = m->next)
874 drawbar(m);
875}
876
877void
878enternotify(XEvent *e)
879{
880 Client *c;
881 Monitor *m;
882 XCrossingEvent *ev = &e->xcrossing;
883
884 if ((ev->mode != NotifyNormal || ev->detail == NotifyInferior) && ev->window != root)
885 return;
886 c = wintoclient(ev->window);
887 m = c ? c->mon : wintomon(ev->window);
888 if (m != selmon) {
889 unfocus(selmon->sel, 1);
890 selmon = m;
891 } else if (!c || c == selmon->sel)
892 return;
893 focus(c);
894}
895
896void
897expose(XEvent *e)
898{
899 Monitor *m;
900 XExposeEvent *ev = &e->xexpose;
901
902 if (ev->count == 0 && (m = wintomon(ev->window)))
903 drawbar(m);
904}
905
906void
907focus(Client *c)
908{
909 if (!c || !ISVISIBLE(c))
910 for (c = selmon->stack; c && !ISVISIBLE(c); c = c->snext);
911 if (selmon->sel && selmon->sel != c)
912 unfocus(selmon->sel, 0);
913 if (c) {
914 if (c->mon != selmon)
915 selmon = c->mon;
916 if (c->isurgent)
917 seturgent(c, 0);
918 detachstack(c);
919 attachstack(c);
920 grabbuttons(c, 1);
921 XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel);
922 setfocus(c);
923 } else {
924 XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
925 XDeleteProperty(dpy, root, netatom[NetActiveWindow]);
926 }
927 selmon->sel = c;
928 drawbars();
929}
930
931/* there are some broken focus acquiring clients needing extra handling */
932void
933focusin(XEvent *e)
934{
935 XFocusChangeEvent *ev = &e->xfocus;
936
937 if (selmon->sel && ev->window != selmon->sel->win)
938 setfocus(selmon->sel);
939}
940
941void
942focusmon(const Arg *arg)
943{
944 Monitor *m;
945
946 if (!mons->next)
947 return;
948 if ((m = dirtomon(arg->i)) == selmon)
949 return;
950 unfocus(selmon->sel, 0);
951 selmon = m;
952 focus(NULL);
953}
954
955void
956focusstack(const Arg *arg)
957{
958 Client *c = NULL, *i;
959
960 if (!selmon->sel)
961 return;
962 if (arg->i > 0) {
963 for (c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next);
964 if (!c)
965 for (c = selmon->clients; c && !ISVISIBLE(c); c = c->next);
966 } else {
967 for (i = selmon->clients; i != selmon->sel; i = i->next)
968 if (ISVISIBLE(i))
969 c = i;
970 if (!c)
971 for (; i; i = i->next)
972 if (ISVISIBLE(i))
973 c = i;
974 }
975 if (c) {
976 focus(c);
977 restack(selmon);
978 }
979}
980
981Atom
982getatomprop(Client *c, Atom prop)
983{
984 int di;
985 unsigned long dl;
986 unsigned char *p = NULL;
987 Atom da, atom = None;
988
989 if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM,
990 &da, &di, &dl, &dl, &p) == Success && p) {
991 atom = *(Atom *)p;
992 XFree(p);
993 }
994 return atom;
995}
996
997pid_t
998getstatusbarpid()
999{
1000 char buf[32], *str = buf, *c;
1001 FILE *fp;
1002
1003 if (statuspid > 0) {
1004 snprintf(buf, sizeof(buf), "/proc/%u/cmdline", statuspid);
1005 if ((fp = fopen(buf, "r"))) {
1006 fgets(buf, sizeof(buf), fp);
1007 while ((c = strchr(str, '/')))
1008 str = c + 1;
1009 fclose(fp);
1010 if (!strcmp(str, STATUSBAR))
1011 return statuspid;
1012 }
1013 }
1014 if (!(fp = popen("pidof -s "STATUSBAR, "r")))
1015 return -1;
1016 fgets(buf, sizeof(buf), fp);
1017 pclose(fp);
1018 return strtol(buf, NULL, 10);
1019}
1020
1021int
1022getrootptr(int *x, int *y)
1023{
1024 int di;
1025 unsigned int dui;
1026 Window dummy;
1027
1028 return XQueryPointer(dpy, root, &dummy, &dummy, x, y, &di, &di, &dui);
1029}
1030
1031long
1032getstate(Window w)
1033{
1034 int format;
1035 long result = -1;
1036 unsigned char *p = NULL;
1037 unsigned long n, extra;
1038 Atom real;
1039
1040 if (XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState],
1041 &real, &format, &n, &extra, (unsigned char **)&p) != Success)
1042 return -1;
1043 if (n != 0)
1044 result = *p;
1045 XFree(p);
1046 return result;
1047}
1048
1049int
1050gettextprop(Window w, Atom atom, char *text, unsigned int size)
1051{
1052 char **list = NULL;
1053 int n;
1054 XTextProperty name;
1055
1056 if (!text || size == 0)
1057 return 0;
1058 text[0] = '\0';
1059 if (!XGetTextProperty(dpy, w, &name, atom) || !name.nitems)
1060 return 0;
1061 if (name.encoding == XA_STRING)
1062 strncpy(text, (char *)name.value, size - 1);
1063 else {
1064 if (XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success && n > 0 && *list) {
1065 strncpy(text, *list, size - 1);
1066 XFreeStringList(list);
1067 }
1068 }
1069 text[size - 1] = '\0';
1070 XFree(name.value);
1071 return 1;
1072}
1073
1074void
1075grabbuttons(Client *c, int focused)
1076{
1077 updatenumlockmask();
1078 {
1079 unsigned int i, j;
1080 unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask };
1081 XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
1082 if (!focused)
1083 XGrabButton(dpy, AnyButton, AnyModifier, c->win, False,
1084 BUTTONMASK, GrabModeSync, GrabModeSync, None, None);
1085 for (i = 0; i < LENGTH(buttons); i++)
1086 if (buttons[i].click == ClkClientWin)
1087 for (j = 0; j < LENGTH(modifiers); j++)
1088 XGrabButton(dpy, buttons[i].button,
1089 buttons[i].mask | modifiers[j],
1090 c->win, False, BUTTONMASK,
1091 GrabModeAsync, GrabModeSync, None, None);
1092 }
1093}
1094
1095void
1096grabkeys(void)
1097{
1098 updatenumlockmask();
1099 {
1100 unsigned int i, j;
1101 unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask };
1102 KeyCode code;
1103
1104 XUngrabKey(dpy, AnyKey, AnyModifier, root);
1105 for (i = 0; i < LENGTH(keys); i++)
1106 if ((code = XKeysymToKeycode(dpy, keys[i].keysym)))
1107 for (j = 0; j < LENGTH(modifiers); j++)
1108 XGrabKey(dpy, code, keys[i].mod | modifiers[j], root,
1109 True, GrabModeAsync, GrabModeAsync);
1110 }
1111}
1112
1113void
1114incnmaster(const Arg *arg)
1115{
1116 selmon->nmaster = MAX(selmon->nmaster + arg->i, 0);
1117 arrange(selmon);
1118}
1119
1120#ifdef XINERAMA
1121static int
1122isuniquegeom(XineramaScreenInfo *unique, size_t n, XineramaScreenInfo *info)
1123{
1124 while (n--)
1125 if (unique[n].x_org == info->x_org && unique[n].y_org == info->y_org
1126 && unique[n].width == info->width && unique[n].height == info->height)
1127 return 0;
1128 return 1;
1129}
1130#endif /* XINERAMA */
1131
1132void
1133keypress(XEvent *e)
1134{
1135 unsigned int i;
1136 KeySym keysym;
1137 XKeyEvent *ev;
1138
1139 ev = &e->xkey;
1140 keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0);
1141 for (i = 0; i < LENGTH(keys); i++)
1142 if (keysym == keys[i].keysym
1143 && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state)
1144 && keys[i].func)
1145 keys[i].func(&(keys[i].arg));
1146}
1147
1148void
1149killclient(const Arg *arg)
1150{
1151 if (!selmon->sel)
1152 return;
1153 if (!sendevent(selmon->sel, wmatom[WMDelete])) {
1154 XGrabServer(dpy);
1155 XSetErrorHandler(xerrordummy);
1156 XSetCloseDownMode(dpy, DestroyAll);
1157 XKillClient(dpy, selmon->sel->win);
1158 XSync(dpy, False);
1159 XSetErrorHandler(xerror);
1160 XUngrabServer(dpy);
1161 }
1162}
1163
1164void
1165manage(Window w, XWindowAttributes *wa)
1166{
1167 Client *c, *t = NULL, *term = NULL;
1168 Window trans = None;
1169 XWindowChanges wc;
1170
1171 c = ecalloc(1, sizeof(Client));
1172 c->win = w;
1173 c->pid = winpid(w);
1174 /* geometry */
1175 c->x = c->oldx = wa->x;
1176 c->y = c->oldy = wa->y;
1177 c->w = c->oldw = wa->width;
1178 c->h = c->oldh = wa->height;
1179 c->oldbw = wa->border_width;
1180
1181 updatetitle(c);
1182 if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) {
1183 c->mon = t->mon;
1184 c->tags = t->tags;
1185 } else {
1186 c->mon = selmon;
1187 applyrules(c);
1188 term = termforwin(c);
1189 }
1190
1191 if (c->x + WIDTH(c) > c->mon->mx + c->mon->mw)
1192 c->x = c->mon->mx + c->mon->mw - WIDTH(c);
1193 if (c->y + HEIGHT(c) > c->mon->my + c->mon->mh)
1194 c->y = c->mon->my + c->mon->mh - HEIGHT(c);
1195 c->x = MAX(c->x, c->mon->mx);
1196 /* only fix client y-offset, if the client center might cover the bar */
1197 c->y = MAX(c->y, ((c->mon->by == c->mon->my) && (c->x + (c->w / 2) >= c->mon->wx)
1198 && (c->x + (c->w / 2) < c->mon->wx + c->mon->ww)) ? bh : c->mon->my);
1199 c->bw = borderpx;
1200
1201 wc.border_width = c->bw;
1202 XConfigureWindow(dpy, w, CWBorderWidth, &wc);
1203 XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel);
1204 configure(c); /* propagates border_width, if size doesn't change */
1205 updatewindowtype(c);
1206 updatesizehints(c);
1207 updatewmhints(c);
1208 XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask);
1209 grabbuttons(c, 0);
1210 if (!c->isfloating)
1211 c->isfloating = c->oldstate = trans != None || c->isfixed;
1212 if (c->isfloating)
1213 XRaiseWindow(dpy, c->win);
1214 attach(c);
1215 attachstack(c);
1216 XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend,
1217 (unsigned char *) &(c->win), 1);
1218 XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */
1219 setclientstate(c, NormalState);
1220 if (c->mon == selmon)
1221 unfocus(selmon->sel, 0);
1222 c->mon->sel = c;
1223 arrange(c->mon);
1224 XMapWindow(dpy, c->win);
1225 if (term)
1226 swallow(term, c);
1227 focus(NULL);
1228}
1229
1230void
1231mappingnotify(XEvent *e)
1232{
1233 XMappingEvent *ev = &e->xmapping;
1234
1235 XRefreshKeyboardMapping(ev);
1236 if (ev->request == MappingKeyboard)
1237 grabkeys();
1238}
1239
1240void
1241maprequest(XEvent *e)
1242{
1243 static XWindowAttributes wa;
1244 XMapRequestEvent *ev = &e->xmaprequest;
1245
1246 if (!XGetWindowAttributes(dpy, ev->window, &wa))
1247 return;
1248 if (wa.override_redirect)
1249 return;
1250 if (!wintoclient(ev->window))
1251 manage(ev->window, &wa);
1252}
1253
1254void
1255monocle(Monitor *m)
1256{
1257 unsigned int n = 0;
1258 Client *c;
1259
1260 for (c = m->clients; c; c = c->next)
1261 if (ISVISIBLE(c))
1262 n++;
1263 if (n > 0) /* override layout symbol */
1264 snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n);
1265 for (c = nexttiled(m->clients); c; c = nexttiled(c->next))
1266 resize(c, m->wx, m->wy, m->ww - 2 * c->bw, m->wh - 2 * c->bw, 0);
1267}
1268
1269void
1270motionnotify(XEvent *e)
1271{
1272 static Monitor *mon = NULL;
1273 Monitor *m;
1274 XMotionEvent *ev = &e->xmotion;
1275
1276 if (ev->window != root)
1277 return;
1278 if ((m = recttomon(ev->x_root, ev->y_root, 1, 1)) != mon && mon) {
1279 unfocus(selmon->sel, 1);
1280 selmon = m;
1281 focus(NULL);
1282 }
1283 mon = m;
1284}
1285
1286void
1287movemouse(const Arg *arg)
1288{
1289 int x, y, ocx, ocy, nx, ny;
1290 Client *c;
1291 Monitor *m;
1292 XEvent ev;
1293 Time lasttime = 0;
1294
1295 if (!(c = selmon->sel))
1296 return;
1297 if (c->isfullscreen) /* no support moving fullscreen windows by mouse */
1298 return;
1299 restack(selmon);
1300 ocx = c->x;
1301 ocy = c->y;
1302 if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync,
1303 None, cursor[CurMove]->cursor, CurrentTime) != GrabSuccess)
1304 return;
1305 if (!getrootptr(&x, &y))
1306 return;
1307 do {
1308 XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev);
1309 switch(ev.type) {
1310 case ConfigureRequest:
1311 case Expose:
1312 case MapRequest:
1313 handler[ev.type](&ev);
1314 break;
1315 case MotionNotify:
1316 if ((ev.xmotion.time - lasttime) <= (1000 / 60))
1317 continue;
1318 lasttime = ev.xmotion.time;
1319
1320 nx = ocx + (ev.xmotion.x - x);
1321 ny = ocy + (ev.xmotion.y - y);
1322 if (abs(selmon->wx - nx) < snap)
1323 nx = selmon->wx;
1324 else if (abs((selmon->wx + selmon->ww) - (nx + WIDTH(c))) < snap)
1325 nx = selmon->wx + selmon->ww - WIDTH(c);
1326 if (abs(selmon->wy - ny) < snap)
1327 ny = selmon->wy;
1328 else if (abs((selmon->wy + selmon->wh) - (ny + HEIGHT(c))) < snap)
1329 ny = selmon->wy + selmon->wh - HEIGHT(c);
1330 if (!c->isfloating && selmon->lt[selmon->sellt]->arrange
1331 && (abs(nx - c->x) > snap || abs(ny - c->y) > snap))
1332 togglefloating(NULL);
1333 if (!selmon->lt[selmon->sellt]->arrange || c->isfloating)
1334 resize(c, nx, ny, c->w, c->h, 1);
1335 break;
1336 }
1337 } while (ev.type != ButtonRelease);
1338 XUngrabPointer(dpy, CurrentTime);
1339 if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) {
1340 sendmon(c, m);
1341 selmon = m;
1342 focus(NULL);
1343 }
1344}
1345
1346Client *
1347nexttiled(Client *c)
1348{
1349 for (; c && (c->isfloating || !ISVISIBLE(c)); c = c->next);
1350 return c;
1351}
1352
1353void
1354pop(Client *c)
1355{
1356 detach(c);
1357 attach(c);
1358 focus(c);
1359 arrange(c->mon);
1360}
1361
1362void
1363propertynotify(XEvent *e)
1364{
1365 Client *c;
1366 Window trans;
1367 XPropertyEvent *ev = &e->xproperty;
1368
1369 if ((ev->window == root) && (ev->atom == XA_WM_NAME))
1370 updatestatus();
1371 else if (ev->state == PropertyDelete)
1372 return; /* ignore */
1373 else if ((c = wintoclient(ev->window))) {
1374 switch(ev->atom) {
1375 default: break;
1376 case XA_WM_TRANSIENT_FOR:
1377 if (!c->isfloating && (XGetTransientForHint(dpy, c->win, &trans)) &&
1378 (c->isfloating = (wintoclient(trans)) != NULL))
1379 arrange(c->mon);
1380 break;
1381 case XA_WM_NORMAL_HINTS:
1382 updatesizehints(c);
1383 break;
1384 case XA_WM_HINTS:
1385 updatewmhints(c);
1386 drawbars();
1387 break;
1388 }
1389 if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) {
1390 updatetitle(c);
1391 if (c == c->mon->sel)
1392 drawbar(c->mon);
1393 }
1394 if (ev->atom == netatom[NetWMWindowType])
1395 updatewindowtype(c);
1396 }
1397}
1398
1399void
1400quit(const Arg *arg)
1401{
1402 running = 0;
1403}
1404
1405Monitor *
1406recttomon(int x, int y, int w, int h)
1407{
1408 Monitor *m, *r = selmon;
1409 int a, area = 0;
1410
1411 for (m = mons; m; m = m->next)
1412 if ((a = INTERSECT(x, y, w, h, m)) > area) {
1413 area = a;
1414 r = m;
1415 }
1416 return r;
1417}
1418
1419void
1420resize(Client *c, int x, int y, int w, int h, int interact)
1421{
1422 if (applysizehints(c, &x, &y, &w, &h, interact))
1423 resizeclient(c, x, y, w, h);
1424}
1425
1426void
1427resizeclient(Client *c, int x, int y, int w, int h)
1428{
1429 XWindowChanges wc;
1430
1431 c->oldx = c->x; c->x = wc.x = x;
1432 c->oldy = c->y; c->y = wc.y = y;
1433 c->oldw = c->w; c->w = wc.width = w;
1434 c->oldh = c->h; c->h = wc.height = h;
1435 wc.border_width = c->bw;
1436 XConfigureWindow(dpy, c->win, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc);
1437 configure(c);
1438 XSync(dpy, False);
1439}
1440
1441void
1442resizemouse(const Arg *arg)
1443{
1444 int ocx, ocy, nw, nh;
1445 Client *c;
1446 Monitor *m;
1447 XEvent ev;
1448 Time lasttime = 0;
1449
1450 if (!(c = selmon->sel))
1451 return;
1452 if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */
1453 return;
1454 restack(selmon);
1455 ocx = c->x;
1456 ocy = c->y;
1457 if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync,
1458 None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess)
1459 return;
1460 XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1);
1461 do {
1462 XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev);
1463 switch(ev.type) {
1464 case ConfigureRequest:
1465 case Expose:
1466 case MapRequest:
1467 handler[ev.type](&ev);
1468 break;
1469 case MotionNotify:
1470 if ((ev.xmotion.time - lasttime) <= (1000 / 60))
1471 continue;
1472 lasttime = ev.xmotion.time;
1473
1474 nw = MAX(ev.xmotion.x - ocx - 2 * c->bw + 1, 1);
1475 nh = MAX(ev.xmotion.y - ocy - 2 * c->bw + 1, 1);
1476 if (c->mon->wx + nw >= selmon->wx && c->mon->wx + nw <= selmon->wx + selmon->ww
1477 && c->mon->wy + nh >= selmon->wy && c->mon->wy + nh <= selmon->wy + selmon->wh)
1478 {
1479 if (!c->isfloating && selmon->lt[selmon->sellt]->arrange
1480 && (abs(nw - c->w) > snap || abs(nh - c->h) > snap))
1481 togglefloating(NULL);
1482 }
1483 if (!selmon->lt[selmon->sellt]->arrange || c->isfloating)
1484 resize(c, c->x, c->y, nw, nh, 1);
1485 break;
1486 }
1487 } while (ev.type != ButtonRelease);
1488 XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1);
1489 XUngrabPointer(dpy, CurrentTime);
1490 while (XCheckMaskEvent(dpy, EnterWindowMask, &ev));
1491 if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) {
1492 sendmon(c, m);
1493 selmon = m;
1494 focus(NULL);
1495 }
1496}
1497
1498void
1499restack(Monitor *m)
1500{
1501 Client *c;
1502 XEvent ev;
1503 XWindowChanges wc;
1504
1505 drawbar(m);
1506 if (!m->sel)
1507 return;
1508 if (m->sel->isfloating || !m->lt[m->sellt]->arrange)
1509 XRaiseWindow(dpy, m->sel->win);
1510 if (m->lt[m->sellt]->arrange) {
1511 wc.stack_mode = Below;
1512 wc.sibling = m->barwin;
1513 for (c = m->stack; c; c = c->snext)
1514 if (!c->isfloating && ISVISIBLE(c)) {
1515 XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc);
1516 wc.sibling = c->win;
1517 }
1518 }
1519 XSync(dpy, False);
1520 while (XCheckMaskEvent(dpy, EnterWindowMask, &ev));
1521}
1522
1523void
1524run(void)
1525{
1526 XEvent ev;
1527 /* main event loop */
1528 XSync(dpy, False);
1529 while (running && !XNextEvent(dpy, &ev))
1530 if (handler[ev.type])
1531 handler[ev.type](&ev); /* call handler */
1532}
1533
1534void
1535scan(void)
1536{
1537 unsigned int i, num;
1538 Window d1, d2, *wins = NULL;
1539 XWindowAttributes wa;
1540
1541 if (XQueryTree(dpy, root, &d1, &d2, &wins, &num)) {
1542 for (i = 0; i < num; i++) {
1543 if (!XGetWindowAttributes(dpy, wins[i], &wa)
1544 || wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1))
1545 continue;
1546 if (wa.map_state == IsViewable || getstate(wins[i]) == IconicState)
1547 manage(wins[i], &wa);
1548 }
1549 for (i = 0; i < num; i++) { /* now the transients */
1550 if (!XGetWindowAttributes(dpy, wins[i], &wa))
1551 continue;
1552 if (XGetTransientForHint(dpy, wins[i], &d1)
1553 && (wa.map_state == IsViewable || getstate(wins[i]) == IconicState))
1554 manage(wins[i], &wa);
1555 }
1556 if (wins)
1557 XFree(wins);
1558 }
1559}
1560
1561void
1562sendmon(Client *c, Monitor *m)
1563{
1564 if (c->mon == m)
1565 return;
1566 unfocus(c, 1);
1567 detach(c);
1568 detachstack(c);
1569 c->mon = m;
1570 c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */
1571 attach(c);
1572 attachstack(c);
1573 focus(NULL);
1574 arrange(NULL);
1575}
1576
1577void
1578setclientstate(Client *c, long state)
1579{
1580 long data[] = { state, None };
1581
1582 XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32,
1583 PropModeReplace, (unsigned char *)data, 2);
1584}
1585
1586int
1587sendevent(Client *c, Atom proto)
1588{
1589 int n;
1590 Atom *protocols;
1591 int exists = 0;
1592 XEvent ev;
1593
1594 if (XGetWMProtocols(dpy, c->win, &protocols, &n)) {
1595 while (!exists && n--)
1596 exists = protocols[n] == proto;
1597 XFree(protocols);
1598 }
1599 if (exists) {
1600 ev.type = ClientMessage;
1601 ev.xclient.window = c->win;
1602 ev.xclient.message_type = wmatom[WMProtocols];
1603 ev.xclient.format = 32;
1604 ev.xclient.data.l[0] = proto;
1605 ev.xclient.data.l[1] = CurrentTime;
1606 XSendEvent(dpy, c->win, False, NoEventMask, &ev);
1607 }
1608 return exists;
1609}
1610
1611void
1612setfocus(Client *c)
1613{
1614 if (!c->neverfocus) {
1615 XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime);
1616 XChangeProperty(dpy, root, netatom[NetActiveWindow],
1617 XA_WINDOW, 32, PropModeReplace,
1618 (unsigned char *) &(c->win), 1);
1619 }
1620 sendevent(c, wmatom[WMTakeFocus]);
1621}
1622
1623void
1624setfullscreen(Client *c, int fullscreen)
1625{
1626 if (fullscreen && !c->isfullscreen) {
1627 XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32,
1628 PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1);
1629 c->isfullscreen = 1;
1630 c->oldstate = c->isfloating;
1631 c->oldbw = c->bw;
1632 c->bw = 0;
1633 c->isfloating = 1;
1634 resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh);
1635 XRaiseWindow(dpy, c->win);
1636 } else if (!fullscreen && c->isfullscreen){
1637 XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32,
1638 PropModeReplace, (unsigned char*)0, 0);
1639 c->isfullscreen = 0;
1640 c->isfloating = c->oldstate;
1641 c->bw = c->oldbw;
1642 c->x = c->oldx;
1643 c->y = c->oldy;
1644 c->w = c->oldw;
1645 c->h = c->oldh;
1646 resizeclient(c, c->x, c->y, c->w, c->h);
1647 arrange(c->mon);
1648 }
1649}
1650
1651void
1652setlayout(const Arg *arg)
1653{
1654 if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt])
1655 selmon->sellt ^= 1;
1656 if (arg && arg->v)
1657 selmon->lt[selmon->sellt] = (Layout *)arg->v;
1658 strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol);
1659 if (selmon->sel)
1660 arrange(selmon);
1661 else
1662 drawbar(selmon);
1663}
1664
1665/* arg > 1.0 will set mfact absolutely */
1666void
1667setmfact(const Arg *arg)
1668{
1669 float f;
1670
1671 if (!arg || !selmon->lt[selmon->sellt]->arrange)
1672 return;
1673 f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0;
1674 if (f < 0.1 || f > 0.9)
1675 return;
1676 selmon->mfact = f;
1677 arrange(selmon);
1678}
1679
1680void
1681setup(void)
1682{
1683 int i;
1684 XSetWindowAttributes wa;
1685 Atom utf8string;
1686
1687 /* clean up any zombies immediately */
1688 sigchld(0);
1689
1690 /* init screen */
1691 screen = DefaultScreen(dpy);
1692 sw = DisplayWidth(dpy, screen);
1693 sh = DisplayHeight(dpy, screen);
1694 root = RootWindow(dpy, screen);
1695 drw = drw_create(dpy, screen, root, sw, sh);
1696 if (!drw_fontset_create(drw, fonts, LENGTH(fonts)))
1697 die("no fonts could be loaded.");
1698 lrpad = drw->fonts->h;
1699 bh = drw->fonts->h + 2;
1700 updategeom();
1701 /* init atoms */
1702 utf8string = XInternAtom(dpy, "UTF8_STRING", False);
1703 wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False);
1704 wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
1705 wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False);
1706 wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False);
1707 netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False);
1708 netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False);
1709 netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False);
1710 netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False);
1711 netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False);
1712 netatom[NetWMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
1713 netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);
1714 netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False);
1715 netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False);
1716 /* init cursors */
1717 cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr);
1718 cursor[CurResize] = drw_cur_create(drw, XC_sizing);
1719 cursor[CurMove] = drw_cur_create(drw, XC_fleur);
1720 /* init appearance */
1721 scheme = ecalloc(LENGTH(colors), sizeof(Clr *));
1722 for (i = 0; i < LENGTH(colors); i++)
1723 scheme[i] = drw_scm_create(drw, colors[i], 3);
1724 /* init bars */
1725 updatebars();
1726 updatestatus();
1727 /* supporting window for NetWMCheck */
1728 wmcheckwin = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 0, 0, 0);
1729 XChangeProperty(dpy, wmcheckwin, netatom[NetWMCheck], XA_WINDOW, 32,
1730 PropModeReplace, (unsigned char *) &wmcheckwin, 1);
1731 XChangeProperty(dpy, wmcheckwin, netatom[NetWMName], utf8string, 8,
1732 PropModeReplace, (unsigned char *) "dwm", 3);
1733 XChangeProperty(dpy, root, netatom[NetWMCheck], XA_WINDOW, 32,
1734 PropModeReplace, (unsigned char *) &wmcheckwin, 1);
1735 /* EWMH support per view */
1736 XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32,
1737 PropModeReplace, (unsigned char *) netatom, NetLast);
1738 XDeleteProperty(dpy, root, netatom[NetClientList]);
1739 /* select events */
1740 wa.cursor = cursor[CurNormal]->cursor;
1741 wa.event_mask = SubstructureRedirectMask|SubstructureNotifyMask
1742 |ButtonPressMask|PointerMotionMask|EnterWindowMask
1743 |LeaveWindowMask|StructureNotifyMask|PropertyChangeMask;
1744 XChangeWindowAttributes(dpy, root, CWEventMask|CWCursor, &wa);
1745 XSelectInput(dpy, root, wa.event_mask);
1746 grabkeys();
1747 focus(NULL);
1748}
1749
1750
1751void
1752seturgent(Client *c, int urg)
1753{
1754 XWMHints *wmh;
1755
1756 c->isurgent = urg;
1757 if (!(wmh = XGetWMHints(dpy, c->win)))
1758 return;
1759 wmh->flags = urg ? (wmh->flags | XUrgencyHint) : (wmh->flags & ~XUrgencyHint);
1760 XSetWMHints(dpy, c->win, wmh);
1761 XFree(wmh);
1762}
1763
1764void
1765showhide(Client *c)
1766{
1767 if (!c)
1768 return;
1769 if (ISVISIBLE(c)) {
1770 /* show clients top down */
1771 XMoveWindow(dpy, c->win, c->x, c->y);
1772 if ((!c->mon->lt[c->mon->sellt]->arrange || c->isfloating) && !c->isfullscreen)
1773 resize(c, c->x, c->y, c->w, c->h, 0);
1774 showhide(c->snext);
1775 } else {
1776 /* hide clients bottom up */
1777 showhide(c->snext);
1778 XMoveWindow(dpy, c->win, WIDTH(c) * -2, c->y);
1779 }
1780}
1781
1782void
1783sigchld(int unused)
1784{
1785 if (signal(SIGCHLD, sigchld) == SIG_ERR)
1786 die("can't install SIGCHLD handler:");
1787 while (0 < waitpid(-1, NULL, WNOHANG));
1788}
1789
1790void
1791sigstatusbar(const Arg *arg)
1792{
1793 union sigval sv;
1794
1795 if (!statussig)
1796 return;
1797 sv.sival_int = arg->i;
1798 if ((statuspid = getstatusbarpid()) <= 0)
1799 return;
1800
1801 sigqueue(statuspid, SIGRTMIN+statussig, sv);
1802}
1803
1804void
1805spawn(const Arg *arg)
1806{
1807 if (fork() == 0) {
1808 if (dpy)
1809 close(ConnectionNumber(dpy));
1810 setsid();
1811 execvp(((char **)arg->v)[0], (char **)arg->v);
1812 fprintf(stderr, "dwm: execvp %s", ((char **)arg->v)[0]);
1813 perror(" failed");
1814 exit(EXIT_SUCCESS);
1815 }
1816}
1817
1818void
1819tag(const Arg *arg)
1820{
1821 if (selmon->sel && arg->ui & TAGMASK) {
1822 selmon->sel->tags = arg->ui & TAGMASK;
1823 focus(NULL);
1824 arrange(selmon);
1825 }
1826}
1827
1828void
1829tagmon(const Arg *arg)
1830{
1831 if (!selmon->sel || !mons->next)
1832 return;
1833 sendmon(selmon->sel, dirtomon(arg->i));
1834}
1835
1836void
1837togglebar(const Arg *arg)
1838{
1839 selmon->showbar = !selmon->showbar;
1840 updatebarpos(selmon);
1841 XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh);
1842 arrange(selmon);
1843}
1844
1845void
1846togglefloating(const Arg *arg)
1847{
1848 if (!selmon->sel)
1849 return;
1850 if (selmon->sel->isfullscreen) /* no support for fullscreen windows */
1851 return;
1852 selmon->sel->isfloating = !selmon->sel->isfloating || selmon->sel->isfixed;
1853 if (selmon->sel->isfloating)
1854 resize(selmon->sel, selmon->sel->x, selmon->sel->y,
1855 selmon->sel->w, selmon->sel->h, 0);
1856 arrange(selmon);
1857}
1858
1859void
1860toggletag(const Arg *arg)
1861{
1862 unsigned int newtags;
1863
1864 if (!selmon->sel)
1865 return;
1866 newtags = selmon->sel->tags ^ (arg->ui & TAGMASK);
1867 if (newtags) {
1868 selmon->sel->tags = newtags;
1869 focus(NULL);
1870 arrange(selmon);
1871 }
1872}
1873
1874void
1875toggleview(const Arg *arg)
1876{
1877 unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK);
1878
1879 if (newtagset) {
1880 selmon->tagset[selmon->seltags] = newtagset;
1881 focus(NULL);
1882 arrange(selmon);
1883 }
1884}
1885
1886void
1887unfocus(Client *c, int setfocus)
1888{
1889 if (!c)
1890 return;
1891 grabbuttons(c, 0);
1892 XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel);
1893 if (setfocus) {
1894 XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
1895 XDeleteProperty(dpy, root, netatom[NetActiveWindow]);
1896 }
1897}
1898
1899void
1900unmanage(Client *c, int destroyed)
1901{
1902 Monitor *m = c->mon;
1903 XWindowChanges wc;
1904
1905 if (c->swallowing) {
1906 unswallow(c);
1907 return;
1908 }
1909
1910 Client *s = swallowingclient(c->win);
1911 if (s) {
1912 free(s->swallowing);
1913 s->swallowing = NULL;
1914 arrange(m);
1915 focus(NULL);
1916 return;
1917 }
1918
1919 detach(c);
1920 detachstack(c);
1921 if (!destroyed) {
1922 wc.border_width = c->oldbw;
1923 XGrabServer(dpy); /* avoid race conditions */
1924 XSetErrorHandler(xerrordummy);
1925 XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */
1926 XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
1927 setclientstate(c, WithdrawnState);
1928 XSync(dpy, False);
1929 XSetErrorHandler(xerror);
1930 XUngrabServer(dpy);
1931 }
1932 free(c);
1933
1934 if (!s) {
1935 arrange(m);
1936 focus(NULL);
1937 updateclientlist();
1938 }
1939}
1940
1941void
1942unmapnotify(XEvent *e)
1943{
1944 Client *c;
1945 XUnmapEvent *ev = &e->xunmap;
1946
1947 if ((c = wintoclient(ev->window))) {
1948 if (ev->send_event)
1949 setclientstate(c, WithdrawnState);
1950 else
1951 unmanage(c, 0);
1952 }
1953}
1954
1955void
1956updatebars(void)
1957{
1958 Monitor *m;
1959 XSetWindowAttributes wa = {
1960 .override_redirect = True,
1961 .background_pixmap = ParentRelative,
1962 .event_mask = ButtonPressMask|ExposureMask
1963 };
1964 XClassHint ch = {"dwm", "dwm"};
1965 for (m = mons; m; m = m->next) {
1966 if (m->barwin)
1967 continue;
1968 m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen),
1969 CopyFromParent, DefaultVisual(dpy, screen),
1970 CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa);
1971 XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor);
1972 XMapRaised(dpy, m->barwin);
1973 XSetClassHint(dpy, m->barwin, &ch);
1974 }
1975}
1976
1977void
1978updatebarpos(Monitor *m)
1979{
1980 m->wy = m->my;
1981 m->wh = m->mh;
1982 if (m->showbar) {
1983 m->wh -= bh;
1984 m->by = m->topbar ? m->wy : m->wy + m->wh;
1985 m->wy = m->topbar ? m->wy + bh : m->wy;
1986 } else
1987 m->by = -bh;
1988}
1989
1990void
1991updateclientlist()
1992{
1993 Client *c;
1994 Monitor *m;
1995
1996 XDeleteProperty(dpy, root, netatom[NetClientList]);
1997 for (m = mons; m; m = m->next)
1998 for (c = m->clients; c; c = c->next)
1999 XChangeProperty(dpy, root, netatom[NetClientList],
2000 XA_WINDOW, 32, PropModeAppend,
2001 (unsigned char *) &(c->win), 1);
2002}
2003
2004int
2005updategeom(void)
2006{
2007 int dirty = 0;
2008
2009#ifdef XINERAMA
2010 if (XineramaIsActive(dpy)) {
2011 int i, j, n, nn;
2012 Client *c;
2013 Monitor *m;
2014 XineramaScreenInfo *info = XineramaQueryScreens(dpy, &nn);
2015 XineramaScreenInfo *unique = NULL;
2016
2017 for (n = 0, m = mons; m; m = m->next, n++);
2018 /* only consider unique geometries as separate screens */
2019 unique = ecalloc(nn, sizeof(XineramaScreenInfo));
2020 for (i = 0, j = 0; i < nn; i++)
2021 if (isuniquegeom(unique, j, &info[i]))
2022 memcpy(&unique[j++], &info[i], sizeof(XineramaScreenInfo));
2023 XFree(info);
2024 nn = j;
2025 if (n <= nn) { /* new monitors available */
2026 for (i = 0; i < (nn - n); i++) {
2027 for (m = mons; m && m->next; m = m->next);
2028 if (m)
2029 m->next = createmon();
2030 else
2031 mons = createmon();
2032 }
2033 for (i = 0, m = mons; i < nn && m; m = m->next, i++)
2034 if (i >= n
2035 || unique[i].x_org != m->mx || unique[i].y_org != m->my
2036 || unique[i].width != m->mw || unique[i].height != m->mh)
2037 {
2038 dirty = 1;
2039 m->num = i;
2040 m->mx = m->wx = unique[i].x_org;
2041 m->my = m->wy = unique[i].y_org;
2042 m->mw = m->ww = unique[i].width;
2043 m->mh = m->wh = unique[i].height;
2044 updatebarpos(m);
2045 }
2046 } else { /* less monitors available nn < n */
2047 for (i = nn; i < n; i++) {
2048 for (m = mons; m && m->next; m = m->next);
2049 while ((c = m->clients)) {
2050 dirty = 1;
2051 m->clients = c->next;
2052 detachstack(c);
2053 c->mon = mons;
2054 attach(c);
2055 attachstack(c);
2056 }
2057 if (m == selmon)
2058 selmon = mons;
2059 cleanupmon(m);
2060 }
2061 }
2062 free(unique);
2063 } else
2064#endif /* XINERAMA */
2065 { /* default monitor setup */
2066 if (!mons)
2067 mons = createmon();
2068 if (mons->mw != sw || mons->mh != sh) {
2069 dirty = 1;
2070 mons->mw = mons->ww = sw;
2071 mons->mh = mons->wh = sh;
2072 updatebarpos(mons);
2073 }
2074 }
2075 if (dirty) {
2076 selmon = mons;
2077 selmon = wintomon(root);
2078 }
2079 return dirty;
2080}
2081
2082void
2083updatenumlockmask(void)
2084{
2085 unsigned int i, j;
2086 XModifierKeymap *modmap;
2087
2088 numlockmask = 0;
2089 modmap = XGetModifierMapping(dpy);
2090 for (i = 0; i < 8; i++)
2091 for (j = 0; j < modmap->max_keypermod; j++)
2092 if (modmap->modifiermap[i * modmap->max_keypermod + j]
2093 == XKeysymToKeycode(dpy, XK_Num_Lock))
2094 numlockmask = (1 << i);
2095 XFreeModifiermap(modmap);
2096}
2097
2098void
2099updatesizehints(Client *c)
2100{
2101 long msize;
2102 XSizeHints size;
2103
2104 if (!XGetWMNormalHints(dpy, c->win, &size, &msize))
2105 /* size is uninitialized, ensure that size.flags aren't used */
2106 size.flags = PSize;
2107 if (size.flags & PBaseSize) {
2108 c->basew = size.base_width;
2109 c->baseh = size.base_height;
2110 } else if (size.flags & PMinSize) {
2111 c->basew = size.min_width;
2112 c->baseh = size.min_height;
2113 } else
2114 c->basew = c->baseh = 0;
2115 if (size.flags & PResizeInc) {
2116 c->incw = size.width_inc;
2117 c->inch = size.height_inc;
2118 } else
2119 c->incw = c->inch = 0;
2120 if (size.flags & PMaxSize) {
2121 c->maxw = size.max_width;
2122 c->maxh = size.max_height;
2123 } else
2124 c->maxw = c->maxh = 0;
2125 if (size.flags & PMinSize) {
2126 c->minw = size.min_width;
2127 c->minh = size.min_height;
2128 } else if (size.flags & PBaseSize) {
2129 c->minw = size.base_width;
2130 c->minh = size.base_height;
2131 } else
2132 c->minw = c->minh = 0;
2133 if (size.flags & PAspect) {
2134 c->mina = (float)size.min_aspect.y / size.min_aspect.x;
2135 c->maxa = (float)size.max_aspect.x / size.max_aspect.y;
2136 } else
2137 c->maxa = c->mina = 0.0;
2138 c->isfixed = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh);
2139}
2140
2141void
2142updatestatus(void)
2143{
2144 if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) {
2145 strcpy(stext, "dwm-"VERSION);
2146 statusw = TEXTW(stext) - lrpad + 2;
2147 } else {
2148 char *text, *s, ch;
2149
2150 statusw = 0;
2151 for (text = s = stext; *s; s++) {
2152 if ((unsigned char)(*s) < ' ') {
2153 ch = *s;
2154 *s = '\0';
2155 statusw += TEXTW(text) - lrpad;
2156 *s = ch;
2157 text = s + 1;
2158 }
2159 }
2160 statusw += TEXTW(text) - lrpad + 2;
2161
2162 }
2163 drawbar(selmon);
2164}
2165
2166void
2167updatetitle(Client *c)
2168{
2169 if (!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name))
2170 gettextprop(c->win, XA_WM_NAME, c->name, sizeof c->name);
2171 if (c->name[0] == '\0') /* hack to mark broken clients */
2172 strcpy(c->name, broken);
2173}
2174
2175void
2176updatewindowtype(Client *c)
2177{
2178 Atom state = getatomprop(c, netatom[NetWMState]);
2179 Atom wtype = getatomprop(c, netatom[NetWMWindowType]);
2180
2181 if (state == netatom[NetWMFullscreen])
2182 setfullscreen(c, 1);
2183 if (wtype == netatom[NetWMWindowTypeDialog])
2184 c->isfloating = 1;
2185}
2186
2187void
2188updatewmhints(Client *c)
2189{
2190 XWMHints *wmh;
2191
2192 if ((wmh = XGetWMHints(dpy, c->win))) {
2193 if (c == selmon->sel && wmh->flags & XUrgencyHint) {
2194 wmh->flags &= ~XUrgencyHint;
2195 XSetWMHints(dpy, c->win, wmh);
2196 } else
2197 c->isurgent = (wmh->flags & XUrgencyHint) ? 1 : 0;
2198 if (wmh->flags & InputHint)
2199 c->neverfocus = !wmh->input;
2200 else
2201 c->neverfocus = 0;
2202 XFree(wmh);
2203 }
2204}
2205
2206void
2207view(const Arg *arg)
2208{
2209 if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags])
2210 return;
2211 selmon->seltags ^= 1; /* toggle sel tagset */
2212 if (arg->ui & TAGMASK)
2213 selmon->tagset[selmon->seltags] = arg->ui & TAGMASK;
2214 focus(NULL);
2215 arrange(selmon);
2216}
2217
2218pid_t
2219winpid(Window w)
2220{
2221
2222 pid_t result = 0;
2223
2224#ifdef __linux__
2225 xcb_res_client_id_spec_t spec = {0};
2226 spec.client = w;
2227 spec.mask = XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID;
2228
2229 xcb_generic_error_t *e = NULL;
2230 xcb_res_query_client_ids_cookie_t c = xcb_res_query_client_ids(xcon, 1, &spec);
2231 xcb_res_query_client_ids_reply_t *r = xcb_res_query_client_ids_reply(xcon, c, &e);
2232
2233 if (!r)
2234 return (pid_t)0;
2235
2236 xcb_res_client_id_value_iterator_t i = xcb_res_query_client_ids_ids_iterator(r);
2237 for (; i.rem; xcb_res_client_id_value_next(&i)) {
2238 spec = i.data->spec;
2239 if (spec.mask & XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID) {
2240 uint32_t *t = xcb_res_client_id_value_value(i.data);
2241 result = *t;
2242 break;
2243 }
2244 }
2245
2246 free(r);
2247
2248 if (result == (pid_t)-1)
2249 result = 0;
2250
2251#endif /* __linux__ */
2252
2253#ifdef __OpenBSD__
2254 Atom type;
2255 int format;
2256 unsigned long len, bytes;
2257 unsigned char *prop;
2258 pid_t ret;
2259
2260 if (XGetWindowProperty(dpy, w, XInternAtom(dpy, "_NET_WM_PID", 0), 0, 1, False, AnyPropertyType, &type, &format, &len, &bytes, &prop) != Success || !prop)
2261 return 0;
2262
2263 ret = *(pid_t*)prop;
2264 XFree(prop);
2265 result = ret;
2266
2267#endif /* __OpenBSD__ */
2268 return result;
2269}
2270
2271pid_t
2272getparentprocess(pid_t p)
2273{
2274 unsigned int v = 0;
2275
2276#ifdef __linux__
2277 FILE *f;
2278 char buf[256];
2279 snprintf(buf, sizeof(buf) - 1, "/proc/%u/stat", (unsigned)p);
2280
2281 if (!(f = fopen(buf, "r")))
2282 return 0;
2283
2284 fscanf(f, "%*u %*s %*c %u", &v);
2285 fclose(f);
2286#endif /* __linux__*/
2287
2288#ifdef __OpenBSD__
2289 int n;
2290 kvm_t *kd;
2291 struct kinfo_proc *kp;
2292
2293 kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, NULL);
2294 if (!kd)
2295 return 0;
2296
2297 kp = kvm_getprocs(kd, KERN_PROC_PID, p, sizeof(*kp), &n);
2298 v = kp->p_ppid;
2299#endif /* __OpenBSD__ */
2300
2301 return (pid_t)v;
2302}
2303
2304int
2305isdescprocess(pid_t p, pid_t c)
2306{
2307 while (p != c && c != 0)
2308 c = getparentprocess(c);
2309
2310 return (int)c;
2311}
2312
2313Client *
2314termforwin(const Client *w)
2315{
2316 Client *c;
2317 Monitor *m;
2318
2319 if (!w->pid || w->isterminal)
2320 return NULL;
2321
2322 for (m = mons; m; m = m->next) {
2323 for (c = m->clients; c; c = c->next) {
2324 if (c->isterminal && !c->swallowing && c->pid && isdescprocess(c->pid, w->pid))
2325 return c;
2326 }
2327 }
2328
2329 return NULL;
2330}
2331
2332Client *
2333swallowingclient(Window w)
2334{
2335 Client *c;
2336 Monitor *m;
2337
2338 for (m = mons; m; m = m->next) {
2339 for (c = m->clients; c; c = c->next) {
2340 if (c->swallowing && c->swallowing->win == w)
2341 return c;
2342 }
2343 }
2344
2345 return NULL;
2346}
2347
2348Client *
2349wintoclient(Window w)
2350{
2351 Client *c;
2352 Monitor *m;
2353
2354 for (m = mons; m; m = m->next)
2355 for (c = m->clients; c; c = c->next)
2356 if (c->win == w)
2357 return c;
2358 return NULL;
2359}
2360
2361Monitor *
2362wintomon(Window w)
2363{
2364 int x, y;
2365 Client *c;
2366 Monitor *m;
2367
2368 if (w == root && getrootptr(&x, &y))
2369 return recttomon(x, y, 1, 1);
2370 for (m = mons; m; m = m->next)
2371 if (w == m->barwin)
2372 return m;
2373 if ((c = wintoclient(w)))
2374 return c->mon;
2375 return selmon;
2376}
2377
2378/* There's no way to check accesses to destroyed windows, thus those cases are
2379 * ignored (especially on UnmapNotify's). Other types of errors call Xlibs
2380 * default error handler, which may call exit. */
2381int
2382xerror(Display *dpy, XErrorEvent *ee)
2383{
2384 if (ee->error_code == BadWindow
2385 || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch)
2386 || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable)
2387 || (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable)
2388 || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable)
2389 || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch)
2390 || (ee->request_code == X_GrabButton && ee->error_code == BadAccess)
2391 || (ee->request_code == X_GrabKey && ee->error_code == BadAccess)
2392 || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable))
2393 return 0;
2394 fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n",
2395 ee->request_code, ee->error_code);
2396 return xerrorxlib(dpy, ee); /* may call exit */
2397}
2398
2399int
2400xerrordummy(Display *dpy, XErrorEvent *ee)
2401{
2402 return 0;
2403}
2404
2405/* Startup Error handler to check if another window manager
2406 * is already running. */
2407int
2408xerrorstart(Display *dpy, XErrorEvent *ee)
2409{
2410 die("dwm: another window manager is already running");
2411 return -1;
2412}
2413
2414void
2415zoom(const Arg *arg)
2416{
2417 Client *c = selmon->sel;
2418
2419 if (!selmon->lt[selmon->sellt]->arrange
2420 || (selmon->sel && selmon->sel->isfloating))
2421 return;
2422 if (c == nexttiled(selmon->clients))
2423 if (!c || !(c = nexttiled(c->next)))
2424 return;
2425 pop(c);
2426}
2427
2428int
2429main(int argc, char *argv[])
2430{
2431 if (argc == 2 && !strcmp("-v", argv[1]))
2432 die("dwm-"VERSION);
2433 else if (argc != 1)
2434 die("usage: dwm [-v]");
2435 if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())
2436 fputs("warning: no locale support\n", stderr);
2437 if (!(dpy = XOpenDisplay(NULL)))
2438 die("dwm: cannot open display");
2439 if (!(xcon = XGetXCBConnection(dpy)))
2440 die("dwm: cannot get xcb connection\n");
2441 checkotherwm();
2442 setup();
2443#ifdef __OpenBSD__
2444 if (pledge("stdio rpath proc exec ps", NULL) == -1)
2445 die("pledge");
2446#endif /* __OpenBSD__ */
2447 scan();
2448 run();
2449 cleanup();
2450 XCloseDisplay(dpy);
2451 return EXIT_SUCCESS;
2452}
diff --git a/patch-list b/patch-list
new file mode 100644
index 0000000..6c4ac01
--- /dev/null
+++ b/patch-list
@@ -0,0 +1,5 @@
1hidevacant
2statuscmd
3steam
4swallow
5vanitygaps
diff --git a/transient.c b/transient.c
new file mode 100644
index 0000000..040adb5
--- /dev/null
+++ b/transient.c
@@ -0,0 +1,42 @@
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
new file mode 100644
index 0000000..fe044fc
--- /dev/null
+++ b/util.c
@@ -0,0 +1,35 @@
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 *
10ecalloc(size_t nmemb, size_t size)
11{
12 void *p;
13
14 if (!(p = calloc(nmemb, size)))
15 die("calloc:");
16 return p;
17}
18
19void
20die(const char *fmt, ...) {
21 va_list ap;
22
23 va_start(ap, fmt);
24 vfprintf(stderr, fmt, ap);
25 va_end(ap);
26
27 if (fmt[0] && fmt[strlen(fmt)-1] == ':') {
28 fputc(' ', stderr);
29 perror(NULL);
30 } else {
31 fputc('\n', stderr);
32 }
33
34 exit(1);
35}
diff --git a/util.h b/util.h
new file mode 100644
index 0000000..f633b51
--- /dev/null
+++ b/util.h
@@ -0,0 +1,8 @@
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);
diff --git a/vanitygaps.c b/vanitygaps.c
new file mode 100644
index 0000000..c336ee9
--- /dev/null
+++ b/vanitygaps.c
@@ -0,0 +1,809 @@
1/* Key binding functions */
2static void defaultgaps(const Arg *arg);
3static void incrgaps(const Arg *arg);
4static void incrigaps(const Arg *arg);
5static void incrogaps(const Arg *arg);
6static void incrohgaps(const Arg *arg);
7static void incrovgaps(const Arg *arg);
8static void incrihgaps(const Arg *arg);
9static void incrivgaps(const Arg *arg);
10static void togglegaps(const Arg *arg);
11/* Layouts (delete the ones you do not need) */
12static void bstack(Monitor *m);
13static void bstackhoriz(Monitor *m);
14static void centeredmaster(Monitor *m);
15static void centeredfloatingmaster(Monitor *m);
16static void deck(Monitor *m);
17static void dwindle(Monitor *m);
18static void fibonacci(Monitor *m, int s);
19static void grid(Monitor *m);
20static void nrowgrid(Monitor *m);
21static void spiral(Monitor *m);
22static void tile(Monitor *m);
23/* Internals */
24static void getgaps(Monitor *m, int *oh, int *ov, int *ih, int *iv, unsigned int *nc);
25static void getfacts(Monitor *m, int msize, int ssize, float *mf, float *sf, int *mr, int *sr);
26static void setgaps(int oh, int ov, int ih, int iv);
27
28/* Settings */
29#if !PERTAG_PATCH
30static int enablegaps = 1;
31#endif // PERTAG_PATCH
32
33void
34setgaps(int oh, int ov, int ih, int iv)
35{
36 if (oh < 0) oh = 0;
37 if (ov < 0) ov = 0;
38 if (ih < 0) ih = 0;
39 if (iv < 0) iv = 0;
40
41 selmon->gappoh = oh;
42 selmon->gappov = ov;
43 selmon->gappih = ih;
44 selmon->gappiv = iv;
45 arrange(selmon);
46}
47
48void
49togglegaps(const Arg *arg)
50{
51 #if PERTAG_PATCH
52 selmon->pertag->enablegaps[selmon->pertag->curtag] = !selmon->pertag->enablegaps[selmon->pertag->curtag];
53 #else
54 enablegaps = !enablegaps;
55 #endif // PERTAG_PATCH
56 arrange(NULL);
57}
58
59void
60defaultgaps(const Arg *arg)
61{
62 setgaps(gappoh, gappov, gappih, gappiv);
63}
64
65void
66incrgaps(const Arg *arg)
67{
68 setgaps(
69 selmon->gappoh + arg->i,
70 selmon->gappov + arg->i,
71 selmon->gappih + arg->i,
72 selmon->gappiv + arg->i
73 );
74}
75
76void
77incrigaps(const Arg *arg)
78{
79 setgaps(
80 selmon->gappoh,
81 selmon->gappov,
82 selmon->gappih + arg->i,
83 selmon->gappiv + arg->i
84 );
85}
86
87void
88incrogaps(const Arg *arg)
89{
90 setgaps(
91 selmon->gappoh + arg->i,
92 selmon->gappov + arg->i,
93 selmon->gappih,
94 selmon->gappiv
95 );
96}
97
98void
99incrohgaps(const Arg *arg)
100{
101 setgaps(
102 selmon->gappoh + arg->i,
103 selmon->gappov,
104 selmon->gappih,
105 selmon->gappiv
106 );
107}
108
109void
110incrovgaps(const Arg *arg)
111{
112 setgaps(
113 selmon->gappoh,
114 selmon->gappov + arg->i,
115 selmon->gappih,
116 selmon->gappiv
117 );
118}
119
120void
121incrihgaps(const Arg *arg)
122{
123 setgaps(
124 selmon->gappoh,
125 selmon->gappov,
126 selmon->gappih + arg->i,
127 selmon->gappiv
128 );
129}
130
131void
132incrivgaps(const Arg *arg)
133{
134 setgaps(
135 selmon->gappoh,
136 selmon->gappov,
137 selmon->gappih,
138 selmon->gappiv + arg->i
139 );
140}
141
142void
143getgaps(Monitor *m, int *oh, int *ov, int *ih, int *iv, unsigned int *nc)
144{
145 unsigned int n, oe, ie;
146 #if PERTAG_PATCH
147 oe = ie = selmon->pertag->enablegaps[selmon->pertag->curtag];
148 #else
149 oe = ie = enablegaps;
150 #endif // PERTAG_PATCH
151 Client *c;
152
153 for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++);
154 if (smartgaps && n == 1) {
155 oe = 0; // outer gaps disabled when only one client
156 }
157
158 *oh = m->gappoh*oe; // outer horizontal gap
159 *ov = m->gappov*oe; // outer vertical gap
160 *ih = m->gappih*ie; // inner horizontal gap
161 *iv = m->gappiv*ie; // inner vertical gap
162 *nc = n; // number of clients
163}
164
165void
166getfacts(Monitor *m, int msize, int ssize, float *mf, float *sf, int *mr, int *sr)
167{
168 unsigned int n;
169 float mfacts, sfacts;
170 int mtotal = 0, stotal = 0;
171 Client *c;
172
173 for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++);
174 mfacts = MIN(n, m->nmaster);
175 sfacts = n - m->nmaster;
176
177 for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++)
178 if (n < m->nmaster)
179 mtotal += msize / mfacts;
180 else
181 stotal += ssize / sfacts;
182
183 *mf = mfacts; // total factor of master area
184 *sf = sfacts; // total factor of stack area
185 *mr = msize - mtotal; // the remainder (rest) of pixels after an even master split
186 *sr = ssize - stotal; // the remainder (rest) of pixels after an even stack split
187}
188
189/***
190 * Layouts
191 */
192
193/*
194 * Bottomstack layout + gaps
195 * https://dwm.suckless.org/patches/bottomstack/
196 */
197static void
198bstack(Monitor *m)
199{
200 unsigned int i, n;
201 int oh, ov, ih, iv;
202 int mx = 0, my = 0, mh = 0, mw = 0;
203 int sx = 0, sy = 0, sh = 0, sw = 0;
204 float mfacts, sfacts;
205 int mrest, srest;
206 Client *c;
207
208 getgaps(m, &oh, &ov, &ih, &iv, &n);
209 if (n == 0)
210 return;
211
212 sx = mx = m->wx + ov;
213 sy = my = m->wy + oh;
214 sh = mh = m->wh - 2*oh;
215 mw = m->ww - 2*ov - iv * (MIN(n, m->nmaster) - 1);
216 sw = m->ww - 2*ov - iv * (n - m->nmaster - 1);
217
218 if (m->nmaster && n > m->nmaster) {
219 sh = (mh - ih) * (1 - m->mfact);
220 mh = mh - ih - sh;
221 sx = mx;
222 sy = my + mh + ih;
223 }
224
225 getfacts(m, mw, sw, &mfacts, &sfacts, &mrest, &srest);
226
227 for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) {
228 if (i < m->nmaster) {
229 resize(c, mx, my, (mw / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), mh - (2*c->bw), 0);
230 mx += WIDTH(c) + iv;
231 } else {
232 resize(c, sx, sy, (sw / sfacts) + ((i - m->nmaster) < srest ? 1 : 0) - (2*c->bw), sh - (2*c->bw), 0);
233 sx += WIDTH(c) + iv;
234 }
235 }
236}
237
238static void
239bstackhoriz(Monitor *m)
240{
241 unsigned int i, n;
242 int oh, ov, ih, iv;
243 int mx = 0, my = 0, mh = 0, mw = 0;
244 int sx = 0, sy = 0, sh = 0, sw = 0;
245 float mfacts, sfacts;
246 int mrest, srest;
247 Client *c;
248
249 getgaps(m, &oh, &ov, &ih, &iv, &n);
250 if (n == 0)
251 return;
252
253 sx = mx = m->wx + ov;
254 sy = my = m->wy + oh;
255 mh = m->wh - 2*oh;
256 sh = m->wh - 2*oh - ih * (n - m->nmaster - 1);
257 mw = m->ww - 2*ov - iv * (MIN(n, m->nmaster) - 1);
258 sw = m->ww - 2*ov;
259
260 if (m->nmaster && n > m->nmaster) {
261 sh = (mh - ih) * (1 - m->mfact);
262 mh = mh - ih - sh;
263 sy = my + mh + ih;
264 sh = m->wh - mh - 2*oh - ih * (n - m->nmaster);
265 }
266
267 getfacts(m, mw, sh, &mfacts, &sfacts, &mrest, &srest);
268
269 for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) {
270 if (i < m->nmaster) {
271 resize(c, mx, my, (mw / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), mh - (2*c->bw), 0);
272 mx += WIDTH(c) + iv;
273 } else {
274 resize(c, sx, sy, sw - (2*c->bw), (sh / sfacts) + ((i - m->nmaster) < srest ? 1 : 0) - (2*c->bw), 0);
275 sy += HEIGHT(c) + ih;
276 }
277 }
278}
279
280/*
281 * Centred master layout + gaps
282 * https://dwm.suckless.org/patches/centeredmaster/
283 */
284void
285centeredmaster(Monitor *m)
286{
287 unsigned int i, n;
288 int oh, ov, ih, iv;
289 int mx = 0, my = 0, mh = 0, mw = 0;
290 int lx = 0, ly = 0, lw = 0, lh = 0;
291 int rx = 0, ry = 0, rw = 0, rh = 0;
292 float mfacts = 0, lfacts = 0, rfacts = 0;
293 int mtotal = 0, ltotal = 0, rtotal = 0;
294 int mrest = 0, lrest = 0, rrest = 0;
295 Client *c;
296
297 getgaps(m, &oh, &ov, &ih, &iv, &n);
298 if (n == 0)
299 return;
300
301 /* initialize areas */
302 mx = m->wx + ov;
303 my = m->wy + oh;
304 mh = m->wh - 2*oh - ih * ((!m->nmaster ? n : MIN(n, m->nmaster)) - 1);
305 mw = m->ww - 2*ov;
306 lh = m->wh - 2*oh - ih * (((n - m->nmaster) / 2) - 1);
307 rh = m->wh - 2*oh - ih * (((n - m->nmaster) / 2) - ((n - m->nmaster) % 2 ? 0 : 1));
308
309 if (m->nmaster && n > m->nmaster) {
310 /* go mfact box in the center if more than nmaster clients */
311 if (n - m->nmaster > 1) {
312 /* ||<-S->|<---M--->|<-S->|| */
313 mw = (m->ww - 2*ov - 2*iv) * m->mfact;
314 lw = (m->ww - mw - 2*ov - 2*iv) / 2;
315 rw = (m->ww - mw - 2*ov - 2*iv) - lw;
316 mx += lw + iv;
317 } else {
318 /* ||<---M--->|<-S->|| */
319 mw = (mw - iv) * m->mfact;
320 lw = 0;
321 rw = m->ww - mw - iv - 2*ov;
322 }
323 lx = m->wx + ov;
324 ly = m->wy + oh;
325 rx = mx + mw + iv;
326 ry = m->wy + oh;
327 }
328
329 /* calculate facts */
330 for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) {
331 if (!m->nmaster || n < m->nmaster)
332 mfacts += 1;
333 else if ((n - m->nmaster) % 2)
334 lfacts += 1; // total factor of left hand stack area
335 else
336 rfacts += 1; // total factor of right hand stack area
337 }
338
339 for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++)
340 if (!m->nmaster || n < m->nmaster)
341 mtotal += mh / mfacts;
342 else if ((n - m->nmaster) % 2)
343 ltotal += lh / lfacts;
344 else
345 rtotal += rh / rfacts;
346
347 mrest = mh - mtotal;
348 lrest = lh - ltotal;
349 rrest = rh - rtotal;
350
351 for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) {
352 if (!m->nmaster || i < m->nmaster) {
353 /* nmaster clients are stacked vertically, in the center of the screen */
354 resize(c, mx, my, mw - (2*c->bw), (mh / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), 0);
355 my += HEIGHT(c) + ih;
356 } else {
357 /* stack clients are stacked vertically */
358 if ((i - m->nmaster) % 2 ) {
359 resize(c, lx, ly, lw - (2*c->bw), (lh / lfacts) + ((i - 2*m->nmaster) < 2*lrest ? 1 : 0) - (2*c->bw), 0);
360 ly += HEIGHT(c) + ih;
361 } else {
362 resize(c, rx, ry, rw - (2*c->bw), (rh / rfacts) + ((i - 2*m->nmaster) < 2*rrest ? 1 : 0) - (2*c->bw), 0);
363 ry += HEIGHT(c) + ih;
364 }
365 }
366 }
367}
368
369void
370centeredfloatingmaster(Monitor *m)
371{
372 unsigned int i, n;
373 float mfacts, sfacts;
374 float mivf = 1.0; // master inner vertical gap factor
375 int oh, ov, ih, iv, mrest, srest;
376 int mx = 0, my = 0, mh = 0, mw = 0;
377 int sx = 0, sy = 0, sh = 0, sw = 0;
378 Client *c;
379
380 getgaps(m, &oh, &ov, &ih, &iv, &n);
381 if (n == 0)
382 return;
383
384 sx = mx = m->wx + ov;
385 sy = my = m->wy + oh;
386 sh = mh = m->wh - 2*oh;
387 mw = m->ww - 2*ov - iv*(n - 1);
388 sw = m->ww - 2*ov - iv*(n - m->nmaster - 1);
389
390 if (m->nmaster && n > m->nmaster) {
391 mivf = 0.8;
392 /* go mfact box in the center if more than nmaster clients */
393 if (m->ww > m->wh) {
394 mw = m->ww * m->mfact - iv*mivf*(MIN(n, m->nmaster) - 1);
395 mh = m->wh * 0.9;
396 } else {
397 mw = m->ww * 0.9 - iv*mivf*(MIN(n, m->nmaster) - 1);
398 mh = m->wh * m->mfact;
399 }
400 mx = m->wx + (m->ww - mw) / 2;
401 my = m->wy + (m->wh - mh - 2*oh) / 2;
402
403 sx = m->wx + ov;
404 sy = m->wy + oh;
405 sh = m->wh - 2*oh;
406 }
407
408 getfacts(m, mw, sw, &mfacts, &sfacts, &mrest, &srest);
409
410 for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++)
411 if (i < m->nmaster) {
412 /* nmaster clients are stacked horizontally, in the center of the screen */
413 resize(c, mx, my, (mw / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), mh - (2*c->bw), 0);
414 mx += WIDTH(c) + iv*mivf;
415 } else {
416 /* stack clients are stacked horizontally */
417 resize(c, sx, sy, (sw / sfacts) + ((i - m->nmaster) < srest ? 1 : 0) - (2*c->bw), sh - (2*c->bw), 0);
418 sx += WIDTH(c) + iv;
419 }
420}
421
422/*
423 * Deck layout + gaps
424 * https://dwm.suckless.org/patches/deck/
425 */
426void
427deck(Monitor *m)
428{
429 unsigned int i, n;
430 int oh, ov, ih, iv;
431 int mx = 0, my = 0, mh = 0, mw = 0;
432 int sx = 0, sy = 0, sh = 0, sw = 0;
433 float mfacts, sfacts;
434 int mrest, srest;
435 Client *c;
436
437 getgaps(m, &oh, &ov, &ih, &iv, &n);
438 if (n == 0)
439 return;
440
441 sx = mx = m->wx + ov;
442 sy = my = m->wy + oh;
443 sh = mh = m->wh - 2*oh - ih * (MIN(n, m->nmaster) - 1);
444 sw = mw = m->ww - 2*ov;
445
446 if (m->nmaster && n > m->nmaster) {
447 sw = (mw - iv) * (1 - m->mfact);
448 mw = mw - iv - sw;
449 sx = mx + mw + iv;
450 sh = m->wh - 2*oh;
451 }
452
453 getfacts(m, mh, sh, &mfacts, &sfacts, &mrest, &srest);
454
455 if (n - m->nmaster > 0) /* override layout symbol */
456 snprintf(m->ltsymbol, sizeof m->ltsymbol, "D %d", n - m->nmaster);
457
458 for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++)
459 if (i < m->nmaster) {
460 resize(c, mx, my, mw - (2*c->bw), (mh / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), 0);
461 my += HEIGHT(c) + ih;
462 } else {
463 resize(c, sx, sy, sw - (2*c->bw), sh - (2*c->bw), 0);
464 }
465}
466
467/*
468 * Fibonacci layout + gaps
469 * https://dwm.suckless.org/patches/fibonacci/
470 */
471void
472fibonacci(Monitor *m, int s)
473{
474 unsigned int i, n;
475 int nx, ny, nw, nh;
476 int oh, ov, ih, iv;
477 int nv, hrest = 0, wrest = 0, r = 1;
478 Client *c;
479
480 getgaps(m, &oh, &ov, &ih, &iv, &n);
481 if (n == 0)
482 return;
483
484 nx = m->wx + ov;
485 ny = m->wy + oh;
486 nw = m->ww - 2*ov;
487 nh = m->wh - 2*oh;
488
489 for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next)) {
490 if (r) {
491 if ((i % 2 && (nh - ih) / 2 <= (bh + 2*c->bw))
492 || (!(i % 2) && (nw - iv) / 2 <= (bh + 2*c->bw))) {
493 r = 0;
494 }
495 if (r && i < n - 1) {
496 if (i % 2) {
497 nv = (nh - ih) / 2;
498 hrest = nh - 2*nv - ih;
499 nh = nv;
500 } else {
501 nv = (nw - iv) / 2;
502 wrest = nw - 2*nv - iv;
503 nw = nv;
504 }
505
506 if ((i % 4) == 2 && !s)
507 nx += nw + iv;
508 else if ((i % 4) == 3 && !s)
509 ny += nh + ih;
510 }
511
512 if ((i % 4) == 0) {
513 if (s) {
514 ny += nh + ih;
515 nh += hrest;
516 }
517 else {
518 nh -= hrest;
519 ny -= nh + ih;
520 }
521 }
522 else if ((i % 4) == 1) {
523 nx += nw + iv;
524 nw += wrest;
525 }
526 else if ((i % 4) == 2) {
527 ny += nh + ih;
528 nh += hrest;
529 if (i < n - 1)
530 nw += wrest;
531 }
532 else if ((i % 4) == 3) {
533 if (s) {
534 nx += nw + iv;
535 nw -= wrest;
536 } else {
537 nw -= wrest;
538 nx -= nw + iv;
539 nh += hrest;
540 }
541 }
542 if (i == 0) {
543 if (n != 1) {
544 nw = (m->ww - iv - 2*ov) - (m->ww - iv - 2*ov) * (1 - m->mfact);
545 wrest = 0;
546 }
547 ny = m->wy + oh;
548 }
549 else if (i == 1)
550 nw = m->ww - nw - iv - 2*ov;
551 i++;
552 }
553
554 resize(c, nx, ny, nw - (2*c->bw), nh - (2*c->bw), False);
555 }
556}
557
558void
559dwindle(Monitor *m)
560{
561 fibonacci(m, 1);
562}
563
564void
565spiral(Monitor *m)
566{
567 fibonacci(m, 0);
568}
569
570/*
571 * Gappless grid layout + gaps (ironically)
572 * https://dwm.suckless.org/patches/gaplessgrid/
573 */
574void
575gaplessgrid(Monitor *m)
576{
577 unsigned int i, n;
578 int x, y, cols, rows, ch, cw, cn, rn, rrest, crest; // counters
579 int oh, ov, ih, iv;
580 Client *c;
581
582 getgaps(m, &oh, &ov, &ih, &iv, &n);
583 if (n == 0)
584 return;
585
586 /* grid dimensions */
587 for (cols = 0; cols <= n/2; cols++)
588 if (cols*cols >= n)
589 break;
590 if (n == 5) /* set layout against the general calculation: not 1:2:2, but 2:3 */
591 cols = 2;
592 rows = n/cols;
593 cn = rn = 0; // reset column no, row no, client count
594
595 ch = (m->wh - 2*oh - ih * (rows - 1)) / rows;
596 cw = (m->ww - 2*ov - iv * (cols - 1)) / cols;
597 rrest = (m->wh - 2*oh - ih * (rows - 1)) - ch * rows;
598 crest = (m->ww - 2*ov - iv * (cols - 1)) - cw * cols;
599 x = m->wx + ov;
600 y = m->wy + oh;
601
602 for (i = 0, c = nexttiled(m->clients); c; i++, c = nexttiled(c->next)) {
603 if (i/rows + 1 > cols - n%cols) {
604 rows = n/cols + 1;
605 ch = (m->wh - 2*oh - ih * (rows - 1)) / rows;
606 rrest = (m->wh - 2*oh - ih * (rows - 1)) - ch * rows;
607 }
608 resize(c,
609 x,
610 y + rn*(ch + ih) + MIN(rn, rrest),
611 cw + (cn < crest ? 1 : 0) - 2*c->bw,
612 ch + (rn < rrest ? 1 : 0) - 2*c->bw,
613 0);
614 rn++;
615 if (rn >= rows) {
616 rn = 0;
617 x += cw + ih + (cn < crest ? 1 : 0);
618 cn++;
619 }
620 }
621}
622
623/*
624 * Gridmode layout + gaps
625 * https://dwm.suckless.org/patches/gridmode/
626 */
627void
628grid(Monitor *m)
629{
630 unsigned int i, n;
631 int cx, cy, cw, ch, cc, cr, chrest, cwrest, cols, rows;
632 int oh, ov, ih, iv;
633 Client *c;
634
635 getgaps(m, &oh, &ov, &ih, &iv, &n);
636
637 /* grid dimensions */
638 for (rows = 0; rows <= n/2; rows++)
639 if (rows*rows >= n)
640 break;
641 cols = (rows && (rows - 1) * rows >= n) ? rows - 1 : rows;
642
643 /* window geoms (cell height/width) */
644 ch = (m->wh - 2*oh - ih * (rows - 1)) / (rows ? rows : 1);
645 cw = (m->ww - 2*ov - iv * (cols - 1)) / (cols ? cols : 1);
646 chrest = (m->wh - 2*oh - ih * (rows - 1)) - ch * rows;
647 cwrest = (m->ww - 2*ov - iv * (cols - 1)) - cw * cols;
648 for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) {
649 cc = i / rows;
650 cr = i % rows;
651 cx = m->wx + ov + cc * (cw + iv) + MIN(cc, cwrest);
652 cy = m->wy + oh + cr * (ch + ih) + MIN(cr, chrest);
653 resize(c, cx, cy, cw + (cc < cwrest ? 1 : 0) - 2*c->bw, ch + (cr < chrest ? 1 : 0) - 2*c->bw, False);
654 }
655}
656
657/*
658 * Horizontal grid layout + gaps
659 * https://dwm.suckless.org/patches/horizgrid/
660 */
661void
662horizgrid(Monitor *m) {
663 Client *c;
664 unsigned int n, i;
665 int oh, ov, ih, iv;
666 int mx = 0, my = 0, mh = 0, mw = 0;
667 int sx = 0, sy = 0, sh = 0, sw = 0;
668 int ntop, nbottom = 1;
669 float mfacts, sfacts;
670 int mrest, srest;
671
672 /* Count windows */
673 getgaps(m, &oh, &ov, &ih, &iv, &n);
674 if (n == 0)
675 return;
676
677 if (n <= 2)
678 ntop = n;
679 else {
680 ntop = n / 2;
681 nbottom = n - ntop;
682 }
683 sx = mx = m->wx + ov;
684 sy = my = m->wy + oh;
685 sh = mh = m->wh - 2*oh;
686 sw = mw = m->ww - 2*ov;
687
688 if (n > ntop) {
689 sh = (mh - ih) / 2;
690 mh = mh - ih - sh;
691 sy = my + mh + ih;
692 mw = m->ww - 2*ov - iv * (ntop - 1);
693 sw = m->ww - 2*ov - iv * (nbottom - 1);
694 }
695
696 mfacts = ntop;
697 sfacts = nbottom;
698 mrest = mw - (mw / ntop) * ntop;
699 srest = sw - (sw / nbottom) * nbottom;
700
701 for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++)
702 if (i < ntop) {
703 resize(c, mx, my, (mw / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), mh - (2*c->bw), 0);
704 mx += WIDTH(c) + iv;
705 } else {
706 resize(c, sx, sy, (sw / sfacts) + ((i - ntop) < srest ? 1 : 0) - (2*c->bw), sh - (2*c->bw), 0);
707 sx += WIDTH(c) + iv;
708 }
709}
710
711/*
712 * nrowgrid layout + gaps
713 * https://dwm.suckless.org/patches/nrowgrid/
714 */
715void
716nrowgrid(Monitor *m)
717{
718 unsigned int n;
719 int ri = 0, ci = 0; /* counters */
720 int oh, ov, ih, iv; /* vanitygap settings */
721 unsigned int cx, cy, cw, ch; /* client geometry */
722 unsigned int uw = 0, uh = 0, uc = 0; /* utilization trackers */
723 unsigned int cols, rows = m->nmaster + 1;
724 Client *c;
725
726 /* count clients */
727 getgaps(m, &oh, &ov, &ih, &iv, &n);
728
729 /* nothing to do here */
730 if (n == 0)
731 return;
732
733 /* force 2 clients to always split vertically */
734 if (FORCE_VSPLIT && n == 2)
735 rows = 1;
736
737 /* never allow empty rows */
738 if (n < rows)
739 rows = n;
740
741 /* define first row */
742 cols = n / rows;
743 uc = cols;
744 cy = m->wy + oh;
745 ch = (m->wh - 2*oh - ih*(rows - 1)) / rows;
746 uh = ch;
747
748 for (c = nexttiled(m->clients); c; c = nexttiled(c->next), ci++) {
749 if (ci == cols) {
750 uw = 0;
751 ci = 0;
752 ri++;
753
754 /* next row */
755 cols = (n - uc) / (rows - ri);
756 uc += cols;
757 cy = m->wy + oh + uh + ih;
758 uh += ch + ih;
759 }
760
761 cx = m->wx + ov + uw;
762 cw = (m->ww - 2*ov - uw) / (cols - ci);
763 uw += cw + iv;
764
765 resize(c, cx, cy, cw - (2*c->bw), ch - (2*c->bw), 0);
766 }
767}
768
769/*
770 * Default tile layout + gaps
771 */
772static void
773tile(Monitor *m)
774{
775 unsigned int i, n;
776 int oh, ov, ih, iv;
777 int mx = 0, my = 0, mh = 0, mw = 0;
778 int sx = 0, sy = 0, sh = 0, sw = 0;
779 float mfacts, sfacts;
780 int mrest, srest;
781 Client *c;
782
783 getgaps(m, &oh, &ov, &ih, &iv, &n);
784 if (n == 0)
785 return;
786
787 sx = mx = m->wx + ov;
788 sy = my = m->wy + oh;
789 mh = m->wh - 2*oh - ih * (MIN(n, m->nmaster) - 1);
790 sh = m->wh - 2*oh - ih * (n - m->nmaster - 1);
791 sw = mw = m->ww - 2*ov;
792
793 if (m->nmaster && n > m->nmaster) {
794 sw = (mw - iv) * (1 - m->mfact);
795 mw = mw - iv - sw;
796 sx = mx + mw + iv;
797 }
798
799 getfacts(m, mh, sh, &mfacts, &sfacts, &mrest, &srest);
800
801 for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++)
802 if (i < m->nmaster) {
803 resize(c, mx, my, mw - (2*c->bw), (mh / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), 0);
804 my += HEIGHT(c) + ih;
805 } else {
806 resize(c, sx, sy, sw - (2*c->bw), (sh / sfacts) + ((i - m->nmaster) < srest ? 1 : 0) - (2*c->bw), 0);
807 sy += HEIGHT(c) + ih;
808 }
809}