diff options
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | Makefile | 51 | ||||
-rw-r--r-- | README | 48 | ||||
-rw-r--r-- | colors.h | 51 | ||||
-rw-r--r-- | config.h | 148 | ||||
-rw-r--r-- | config.mk | 38 | ||||
-rw-r--r-- | drw.c | 435 | ||||
-rw-r--r-- | drw.h | 57 | ||||
-rw-r--r-- | dwm.1 | 176 | ||||
-rw-r--r-- | dwm.c | 2452 | ||||
-rw-r--r-- | patch-list | 5 | ||||
-rw-r--r-- | transient.c | 42 | ||||
-rw-r--r-- | util.c | 35 | ||||
-rw-r--r-- | util.h | 8 | ||||
-rw-r--r-- | vanitygaps.c | 809 |
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 | ||
2 | dwm | ||
3 | patches/* | ||
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 | |||
4 | include config.mk | ||
5 | |||
6 | SRC = drw.c dwm.c util.c | ||
7 | OBJ = ${SRC:.c=.o} | ||
8 | |||
9 | all: options dwm | ||
10 | |||
11 | options: | ||
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 | |||
22 | config.h: | ||
23 | cp config.def.h $@ | ||
24 | |||
25 | dwm: ${OBJ} | ||
26 | ${CC} -o $@ ${OBJ} ${LDFLAGS} | ||
27 | |||
28 | clean: | ||
29 | rm -f dwm ${OBJ} dwm-${VERSION}.tar.gz | ||
30 | |||
31 | dist: 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 | |||
39 | install: 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 | |||
47 | uninstall: | ||
48 | rm -f ${DESTDIR}${PREFIX}/bin/dwm\ | ||
49 | ${DESTDIR}${MANPREFIX}/man1/dwm.1 | ||
50 | |||
51 | .PHONY: all options clean dist install uninstall | ||
@@ -0,0 +1,48 @@ | |||
1 | dwm - dynamic window manager | ||
2 | ============================ | ||
3 | dwm is an extremely fast, small, and dynamic window manager for X. | ||
4 | |||
5 | |||
6 | Requirements | ||
7 | ------------ | ||
8 | In order to build dwm you need the Xlib header files. | ||
9 | |||
10 | |||
11 | Installation | ||
12 | ------------ | ||
13 | Edit config.mk to match your local setup (dwm is installed into | ||
14 | the /usr/local namespace by default). | ||
15 | |||
16 | Afterwards enter the following command to build and install dwm (if | ||
17 | necessary as root): | ||
18 | |||
19 | make clean install | ||
20 | |||
21 | |||
22 | Running dwm | ||
23 | ----------- | ||
24 | Add the following line to your .xinitrc to start dwm using startx: | ||
25 | |||
26 | exec dwm | ||
27 | |||
28 | In order to connect dwm to a specific display, make sure that | ||
29 | the 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 | |||
35 | In order to display status info in the bar, you can do something | ||
36 | like 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 | |||
45 | Configuration | ||
46 | ------------- | ||
47 | The configuration of dwm is done by creating a custom config.h | ||
48 | and (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*/ | ||
2 | static const char ad_base[] = "#404552"; | ||
3 | static const char ad_fg[] = "#d3dae3"; | ||
4 | static const char ad_text[] = "#d3dae3"; | ||
5 | static const char ad_bg[] = "#383c4a"; | ||
6 | static const char ad_sel_fg[] = "#ffffff"; | ||
7 | static const char ad_sel_bg[] = "#5294e2"; | ||
8 | static const char ad_hlght[] = "#5294e2"; | ||
9 | |||
10 | /* Dwm Defaults */ | ||
11 | static const char col_gray1[] = "#222222"; | ||
12 | static const char col_gray2[] = "#444444"; | ||
13 | static const char col_gray3[] = "#bbbbbb"; | ||
14 | static const char col_gray4[] = "#eeeeee"; | ||
15 | static const char col_cyan[] = "#005577"; | ||
16 | |||
17 | /*Dark Blood Variables*/ | ||
18 | static const char db_base[] = "#0A0608"; | ||
19 | static const char db_fg[] = "#B9B9B9"; | ||
20 | static const char db_text[] = "#B9B9B9"; | ||
21 | static const char db_bg[] = "#232323"; | ||
22 | static const char db_sel_fg[] = "#FF9EA7"; | ||
23 | static const char db_sel_bg[] = "#3D1317"; | ||
24 | static const char db_hlght[] = "#3D1317"; | ||
25 | |||
26 | /*Dark Cold Variables*/ | ||
27 | static const char dc_base[] = "#060A0F"; | ||
28 | static const char dc_fg[] = "#6EBEFE"; | ||
29 | static const char dc_text[] = "#6EBEFE"; | ||
30 | static const char dc_bg[] = "#232323"; | ||
31 | static const char dc_sel_fg[] = "#FFFFFF"; | ||
32 | static const char dc_sel_bg[] = "#0D377C"; | ||
33 | static const char dc_hlght[] = "#0D377C"; | ||
34 | |||
35 | /*Dark Mint Variables*/ | ||
36 | static const char dm_base[] = "#060A08"; | ||
37 | static const char dm_fg[] = "#78B97E"; | ||
38 | static const char dm_text[] = "#78B97E"; | ||
39 | static const char dm_bg[] = "#232323"; | ||
40 | static const char dm_sel_fg[] = "#9EFFA7"; | ||
41 | static const char dm_sel_bg[] = "#133D17"; | ||
42 | static const char dm_hlght[] = "#133D17"; | ||
43 | |||
44 | /*Dark Fire Variables*/ | ||
45 | static const char df_base[] = "#0F0606"; | ||
46 | static const char df_fg[] = "#FEFEFE"; | ||
47 | static const char df_text[] = "#FEFEFE"; | ||
48 | static const char df_bg[] = "#232323"; | ||
49 | static const char df_sel_fg[] = "#FFFFFF"; | ||
50 | static const char df_sel_bg[] = "#7C0D0D"; | ||
51 | static 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 */ | ||
6 | static const unsigned int borderpx = 3; /* border pixel of windows */ | ||
7 | static const unsigned int snap = 32; /* snap pixel */ | ||
8 | static const unsigned int gappih = 20; /* horiz inner gap between windows */ | ||
9 | static const unsigned int gappiv = 10; /* vert inner gap between windows */ | ||
10 | static const unsigned int gappoh = 10; /* horiz outer gap between windows and screen edge */ | ||
11 | static const unsigned int gappov = 30; /* vert outer gap between windows and screen edge */ | ||
12 | static const int swallowfloating = 0; /* 1 means swallow floating windows by default */ | ||
13 | static int smartgaps = 0; /* 1 means no outer gap when there is only one window */ | ||
14 | static const int showbar = 1; /* 0 means no bar */ | ||
15 | static const int topbar = 1; /* 0 means bottom bar */ | ||
16 | static const char *fonts[] = { "monospace:size=10"}; | ||
17 | static const char dmenufont[] = "monospace:size=10"; | ||
18 | #include "colors.h" | ||
19 | static 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 */ | ||
26 | static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; | ||
27 | |||
28 | |||
29 | static 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) */ | ||
45 | static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */ | ||
46 | static const int nmaster = 1; /* number of clients in master area */ | ||
47 | static 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 | |||
52 | static 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" | ||
84 | static const char *termcmd[] = {"urxvt"}; | ||
85 | |||
86 | static 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 */ | ||
132 | static 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 | ||
2 | VERSION = 6.2 | ||
3 | |||
4 | # Customize below to fit your system | ||
5 | |||
6 | # paths | ||
7 | PREFIX = /usr/local | ||
8 | MANPREFIX = /usr/share/man | ||
9 | |||
10 | X11INC = /usr/X11R6/include | ||
11 | X11LIB = /usr/X11R6/lib | ||
12 | |||
13 | # Xinerama, comment if you don't want it | ||
14 | XINERAMALIBS = -lXinerama | ||
15 | XINERAMAFLAGS = -DXINERAMA | ||
16 | |||
17 | # freetype | ||
18 | FREETYPELIBS = -lfontconfig -lXft | ||
19 | FREETYPEINC = /usr/include/freetype2 | ||
20 | # OpenBSD (uncomment) | ||
21 | #FREETYPEINC = ${X11INC}/freetype2 | ||
22 | |||
23 | # includes and libs | ||
24 | INCS = -I${X11INC} -I${FREETYPEINC} | ||
25 | LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} -lX11-xcb -lxcb -lxcb-res | ||
26 | |||
27 | # flags | ||
28 | CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=2 -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} | ||
29 | #CFLAGS = -g -std=c99 -pedantic -Wall -O0 ${INCS} ${CPPFLAGS} | ||
30 | CFLAGS = -std=c99 -pedantic -Wall -Wno-deprecated-declarations -Os ${INCS} ${CPPFLAGS} | ||
31 | LDFLAGS = ${LIBS} | ||
32 | |||
33 | # Solaris | ||
34 | #CFLAGS = -fast ${INCS} -DVERSION=\"${VERSION}\" | ||
35 | #LDFLAGS = ${LIBS} | ||
36 | |||
37 | # compiler and linker | ||
38 | CC = cc | ||
@@ -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 | |||
14 | static const unsigned char utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; | ||
15 | static const unsigned char utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; | ||
16 | static const long utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000}; | ||
17 | static const long utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; | ||
18 | |||
19 | static long | ||
20 | utf8decodebyte(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 | |||
28 | static size_t | ||
29 | utf8validate(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 | |||
38 | static size_t | ||
39 | utf8decode(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 | |||
63 | Drw * | ||
64 | drw_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 | |||
80 | void | ||
81 | drw_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 | |||
93 | void | ||
94 | drw_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 | */ | ||
104 | static Fnt * | ||
105 | xfont_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 | |||
157 | static void | ||
158 | xfont_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 | |||
168 | Fnt* | ||
169 | drw_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 | |||
186 | void | ||
187 | drw_fontset_free(Fnt *font) | ||
188 | { | ||
189 | if (font) { | ||
190 | drw_fontset_free(font->next); | ||
191 | xfont_free(font); | ||
192 | } | ||
193 | } | ||
194 | |||
195 | void | ||
196 | drw_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. */ | ||
209 | Clr * | ||
210 | drw_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 | |||
224 | void | ||
225 | drw_setfontset(Drw *drw, Fnt *set) | ||
226 | { | ||
227 | if (drw) | ||
228 | drw->fonts = set; | ||
229 | } | ||
230 | |||
231 | void | ||
232 | drw_setscheme(Drw *drw, Clr *scm) | ||
233 | { | ||
234 | if (drw) | ||
235 | drw->scheme = scm; | ||
236 | } | ||
237 | |||
238 | void | ||
239 | drw_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 | |||
250 | int | ||
251 | drw_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 | |||
381 | void | ||
382 | drw_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 | |||
391 | unsigned int | ||
392 | drw_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 | |||
399 | void | ||
400 | drw_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 | |||
414 | Cur * | ||
415 | drw_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 | |||
427 | void | ||
428 | drw_cur_free(Drw *drw, Cur *cursor) | ||
429 | { | ||
430 | if (!cursor) | ||
431 | return; | ||
432 | |||
433 | XFreeCursor(drw->dpy, cursor->cursor); | ||
434 | free(cursor); | ||
435 | } | ||
@@ -0,0 +1,57 @@ | |||
1 | /* See LICENSE file for copyright and license details. */ | ||
2 | |||
3 | typedef struct { | ||
4 | Cursor cursor; | ||
5 | } Cur; | ||
6 | |||
7 | typedef struct Fnt { | ||
8 | Display *dpy; | ||
9 | unsigned int h; | ||
10 | XftFont *xfont; | ||
11 | FcPattern *pattern; | ||
12 | struct Fnt *next; | ||
13 | } Fnt; | ||
14 | |||
15 | enum { ColFg, ColBg, ColBorder }; /* Clr scheme index */ | ||
16 | typedef XftColor Clr; | ||
17 | |||
18 | typedef 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 */ | ||
30 | Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h); | ||
31 | void drw_resize(Drw *drw, unsigned int w, unsigned int h); | ||
32 | void drw_free(Drw *drw); | ||
33 | |||
34 | /* Fnt abstraction */ | ||
35 | Fnt *drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount); | ||
36 | void drw_fontset_free(Fnt* set); | ||
37 | unsigned int drw_fontset_getwidth(Drw *drw, const char *text); | ||
38 | void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h); | ||
39 | |||
40 | /* Colorscheme abstraction */ | ||
41 | void drw_clr_create(Drw *drw, Clr *dest, const char *clrname); | ||
42 | Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount); | ||
43 | |||
44 | /* Cursor abstraction */ | ||
45 | Cur *drw_cur_create(Drw *drw, int shape); | ||
46 | void drw_cur_free(Drw *drw, Cur *cursor); | ||
47 | |||
48 | /* Drawing context manipulation */ | ||
49 | void drw_setfontset(Drw *drw, Fnt *set); | ||
50 | void drw_setscheme(Drw *drw, Clr *scm); | ||
51 | |||
52 | /* Drawing functions */ | ||
53 | void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert); | ||
54 | int 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 */ | ||
57 | void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h); | ||
@@ -0,0 +1,176 @@ | |||
1 | .TH DWM 1 dwm\-VERSION | ||
2 | .SH NAME | ||
3 | dwm \- dynamic window manager | ||
4 | .SH SYNOPSIS | ||
5 | .B dwm | ||
6 | .RB [ \-v ] | ||
7 | .SH DESCRIPTION | ||
8 | dwm is a dynamic window manager for X. It manages windows in tiled, monocle | ||
9 | and floating layouts. Either layout can be applied dynamically, optimising the | ||
10 | environment for the application in use and the task performed. | ||
11 | .P | ||
12 | In tiled layouts windows are managed in a master and stacking area. The master | ||
13 | area on the left contains one window by default, and the stacking area on the | ||
14 | right contains all other windows. The number of master area windows can be | ||
15 | adjusted from zero to an arbitrary number. In monocle layout all windows are | ||
16 | maximised to the screen size. In floating layout windows can be resized and | ||
17 | moved freely. Dialog windows are always managed floating, regardless of the | ||
18 | layout applied. | ||
19 | .P | ||
20 | Windows are grouped by tags. Each window can be tagged with one or multiple | ||
21 | tags. Selecting certain tags displays all windows with these tags. | ||
22 | .P | ||
23 | Each screen contains a small status bar which displays all available tags, the | ||
24 | layout, the title of the focused window, and the text read from the root window | ||
25 | name property, if the screen is focused. A floating window is indicated with an | ||
26 | empty square and a maximised floating window is indicated with a filled square | ||
27 | before the windows title. The selected tags are indicated with a different | ||
28 | color. The tags of the focused window are indicated with a filled square in the | ||
29 | top left corner. The tags which are applied to one or more windows are | ||
30 | indicated with an empty square in the top left corner. | ||
31 | .P | ||
32 | dwm draws a small border around windows to indicate the focus state. | ||
33 | .SH OPTIONS | ||
34 | .TP | ||
35 | .B \-v | ||
36 | prints version information to standard output, then exits. | ||
37 | .SH USAGE | ||
38 | .SS Status bar | ||
39 | .TP | ||
40 | .B X root window name | ||
41 | is read and displayed in the status text area. It can be set with the | ||
42 | .BR xsetroot (1) | ||
43 | command. | ||
44 | .TP | ||
45 | .B Button1 | ||
46 | click on a tag label to display all windows with that tag, click on the layout | ||
47 | label toggles between tiled and floating layout. | ||
48 | .TP | ||
49 | .B Button3 | ||
50 | click on a tag label adds/removes all windows with that tag to/from the view. | ||
51 | .TP | ||
52 | .B Mod1\-Button1 | ||
53 | click on a tag label applies that tag to the focused window. | ||
54 | .TP | ||
55 | .B Mod1\-Button3 | ||
56 | click on a tag label adds/removes that tag to/from the focused window. | ||
57 | .SS Keyboard commands | ||
58 | .TP | ||
59 | .B Mod1\-Shift\-Return | ||
60 | Start | ||
61 | .BR st(1). | ||
62 | .TP | ||
63 | .B Mod1\-p | ||
64 | Spawn | ||
65 | .BR dmenu(1) | ||
66 | for launching other programs. | ||
67 | .TP | ||
68 | .B Mod1\-, | ||
69 | Focus previous screen, if any. | ||
70 | .TP | ||
71 | .B Mod1\-. | ||
72 | Focus next screen, if any. | ||
73 | .TP | ||
74 | .B Mod1\-Shift\-, | ||
75 | Send focused window to previous screen, if any. | ||
76 | .TP | ||
77 | .B Mod1\-Shift\-. | ||
78 | Send focused window to next screen, if any. | ||
79 | .TP | ||
80 | .B Mod1\-b | ||
81 | Toggles bar on and off. | ||
82 | .TP | ||
83 | .B Mod1\-t | ||
84 | Sets tiled layout. | ||
85 | .TP | ||
86 | .B Mod1\-f | ||
87 | Sets floating layout. | ||
88 | .TP | ||
89 | .B Mod1\-m | ||
90 | Sets monocle layout. | ||
91 | .TP | ||
92 | .B Mod1\-space | ||
93 | Toggles between current and previous layout. | ||
94 | .TP | ||
95 | .B Mod1\-j | ||
96 | Focus next window. | ||
97 | .TP | ||
98 | .B Mod1\-k | ||
99 | Focus previous window. | ||
100 | .TP | ||
101 | .B Mod1\-i | ||
102 | Increase number of windows in master area. | ||
103 | .TP | ||
104 | .B Mod1\-d | ||
105 | Decrease number of windows in master area. | ||
106 | .TP | ||
107 | .B Mod1\-l | ||
108 | Increase master area size. | ||
109 | .TP | ||
110 | .B Mod1\-h | ||
111 | Decrease master area size. | ||
112 | .TP | ||
113 | .B Mod1\-Return | ||
114 | Zooms/cycles focused window to/from master area (tiled layouts only). | ||
115 | .TP | ||
116 | .B Mod1\-Shift\-c | ||
117 | Close focused window. | ||
118 | .TP | ||
119 | .B Mod1\-Shift\-space | ||
120 | Toggle focused window between tiled and floating state. | ||
121 | .TP | ||
122 | .B Mod1\-Tab | ||
123 | Toggles to the previously selected tags. | ||
124 | .TP | ||
125 | .B Mod1\-Shift\-[1..n] | ||
126 | Apply nth tag to focused window. | ||
127 | .TP | ||
128 | .B Mod1\-Shift\-0 | ||
129 | Apply all tags to focused window. | ||
130 | .TP | ||
131 | .B Mod1\-Control\-Shift\-[1..n] | ||
132 | Add/remove nth tag to/from focused window. | ||
133 | .TP | ||
134 | .B Mod1\-[1..n] | ||
135 | View all windows with nth tag. | ||
136 | .TP | ||
137 | .B Mod1\-0 | ||
138 | View all windows with any tag. | ||
139 | .TP | ||
140 | .B Mod1\-Control\-[1..n] | ||
141 | Add/remove all windows with nth tag to/from the view. | ||
142 | .TP | ||
143 | .B Mod1\-Shift\-q | ||
144 | Quit dwm. | ||
145 | .SS Mouse commands | ||
146 | .TP | ||
147 | .B Mod1\-Button1 | ||
148 | Move focused window while dragging. Tiled windows will be toggled to the floating state. | ||
149 | .TP | ||
150 | .B Mod1\-Button2 | ||
151 | Toggles focused window between floating and tiled state. | ||
152 | .TP | ||
153 | .B Mod1\-Button3 | ||
154 | Resize focused window while dragging. Tiled windows will be toggled to the floating state. | ||
155 | .SH CUSTOMIZATION | ||
156 | dwm is customized by creating a custom config.h and (re)compiling the source | ||
157 | code. This keeps it fast, secure and simple. | ||
158 | .SH SEE ALSO | ||
159 | .BR dmenu (1), | ||
160 | .BR st (1) | ||
161 | .SH ISSUES | ||
162 | Java applications which use the XToolkit/XAWT backend may draw grey windows | ||
163 | only. The XToolkit/XAWT backend breaks ICCCM-compliance in recent JDK 1.5 and early | ||
164 | JDK 1.6 versions, because it assumes a reparenting window manager. Possible workarounds | ||
165 | are using JDK 1.4 (which doesn't contain the XToolkit/XAWT backend) or setting the | ||
166 | environment 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 | ||
170 | or | ||
171 | .B wmname LG3D | ||
172 | (to pretend that a non-reparenting window manager is running that the | ||
173 | XToolkit/XAWT backend can recognize) or when using OpenJDK setting the environment variable | ||
174 | .BR _JAVA_AWT_WM_NONREPARENTING=1 . | ||
175 | .SH BUGS | ||
176 | Send all bug reports with a patch to hackers@suckless.org. | ||
@@ -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 */ | ||
63 | enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ | ||
64 | enum { SchemeNorm, SchemeSel }; /* color schemes */ | ||
65 | enum { NetSupported, NetWMName, NetWMState, NetWMCheck, | ||
66 | NetWMFullscreen, NetActiveWindow, NetWMWindowType, | ||
67 | NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ | ||
68 | enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ | ||
69 | enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, | ||
70 | ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ | ||
71 | |||
72 | typedef union { | ||
73 | int i; | ||
74 | unsigned int ui; | ||
75 | float f; | ||
76 | const void *v; | ||
77 | } Arg; | ||
78 | |||
79 | typedef 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 | |||
87 | typedef struct Monitor Monitor; | ||
88 | typedef struct Client Client; | ||
89 | struct 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 | |||
107 | typedef struct { | ||
108 | unsigned int mod; | ||
109 | KeySym keysym; | ||
110 | void (*func)(const Arg *); | ||
111 | const Arg arg; | ||
112 | } Key; | ||
113 | |||
114 | typedef struct { | ||
115 | const char *symbol; | ||
116 | void (*arrange)(Monitor *); | ||
117 | } Layout; | ||
118 | |||
119 | struct 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 | |||
144 | typedef 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 */ | ||
156 | static void applyrules(Client *c); | ||
157 | static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact); | ||
158 | static void arrange(Monitor *m); | ||
159 | static void arrangemon(Monitor *m); | ||
160 | static void attach(Client *c); | ||
161 | static void attachstack(Client *c); | ||
162 | static void buttonpress(XEvent *e); | ||
163 | static void checkotherwm(void); | ||
164 | static void cleanup(void); | ||
165 | static void cleanupmon(Monitor *mon); | ||
166 | static void clientmessage(XEvent *e); | ||
167 | static void configure(Client *c); | ||
168 | static void configurenotify(XEvent *e); | ||
169 | static void configurerequest(XEvent *e); | ||
170 | static Monitor *createmon(void); | ||
171 | static void destroynotify(XEvent *e); | ||
172 | static void detach(Client *c); | ||
173 | static void detachstack(Client *c); | ||
174 | static Monitor *dirtomon(int dir); | ||
175 | static void drawbar(Monitor *m); | ||
176 | static void drawbars(void); | ||
177 | static void enternotify(XEvent *e); | ||
178 | static void expose(XEvent *e); | ||
179 | static void focus(Client *c); | ||
180 | static void focusin(XEvent *e); | ||
181 | static void focusmon(const Arg *arg); | ||
182 | static void focusstack(const Arg *arg); | ||
183 | static int getrootptr(int *x, int *y); | ||
184 | static long getstate(Window w); | ||
185 | static pid_t getstatusbarpid(); | ||
186 | static int gettextprop(Window w, Atom atom, char *text, unsigned int size); | ||
187 | static void grabbuttons(Client *c, int focused); | ||
188 | static void grabkeys(void); | ||
189 | static void incnmaster(const Arg *arg); | ||
190 | static void keypress(XEvent *e); | ||
191 | static void killclient(const Arg *arg); | ||
192 | static void manage(Window w, XWindowAttributes *wa); | ||
193 | static void mappingnotify(XEvent *e); | ||
194 | static void maprequest(XEvent *e); | ||
195 | static void monocle(Monitor *m); | ||
196 | static void motionnotify(XEvent *e); | ||
197 | static void movemouse(const Arg *arg); | ||
198 | static Client *nexttiled(Client *c); | ||
199 | static void pop(Client *); | ||
200 | static void propertynotify(XEvent *e); | ||
201 | static void quit(const Arg *arg); | ||
202 | static Monitor *recttomon(int x, int y, int w, int h); | ||
203 | static void resize(Client *c, int x, int y, int w, int h, int interact); | ||
204 | static void resizeclient(Client *c, int x, int y, int w, int h); | ||
205 | static void resizemouse(const Arg *arg); | ||
206 | static void restack(Monitor *m); | ||
207 | static void run(void); | ||
208 | static void scan(void); | ||
209 | static int sendevent(Client *c, Atom proto); | ||
210 | static void sendmon(Client *c, Monitor *m); | ||
211 | static void setclientstate(Client *c, long state); | ||
212 | static void setfocus(Client *c); | ||
213 | static void setfullscreen(Client *c, int fullscreen); | ||
214 | static void setlayout(const Arg *arg); | ||
215 | static void setmfact(const Arg *arg); | ||
216 | static void setup(void); | ||
217 | static void seturgent(Client *c, int urg); | ||
218 | static void showhide(Client *c); | ||
219 | static void sigchld(int unused); | ||
220 | static void sigstatusbar(const Arg *arg); | ||
221 | static void spawn(const Arg *arg); | ||
222 | static void tag(const Arg *arg); | ||
223 | static void tagmon(const Arg *arg); | ||
224 | static void togglebar(const Arg *arg); | ||
225 | static void togglefloating(const Arg *arg); | ||
226 | static void toggletag(const Arg *arg); | ||
227 | static void toggleview(const Arg *arg); | ||
228 | static void unfocus(Client *c, int setfocus); | ||
229 | static void unmanage(Client *c, int destroyed); | ||
230 | static void unmapnotify(XEvent *e); | ||
231 | static void updatebarpos(Monitor *m); | ||
232 | static void updatebars(void); | ||
233 | static void updateclientlist(void); | ||
234 | static int updategeom(void); | ||
235 | static void updatenumlockmask(void); | ||
236 | static void updatesizehints(Client *c); | ||
237 | static void updatestatus(void); | ||
238 | static void updatetitle(Client *c); | ||
239 | static void updatewindowtype(Client *c); | ||
240 | static void updatewmhints(Client *c); | ||
241 | static void view(const Arg *arg); | ||
242 | static Client *wintoclient(Window w); | ||
243 | static Monitor *wintomon(Window w); | ||
244 | static int xerror(Display *dpy, XErrorEvent *ee); | ||
245 | static int xerrordummy(Display *dpy, XErrorEvent *ee); | ||
246 | static int xerrorstart(Display *dpy, XErrorEvent *ee); | ||
247 | static void zoom(const Arg *arg); | ||
248 | |||
249 | static pid_t getparentprocess(pid_t p); | ||
250 | static int isdescprocess(pid_t p, pid_t c); | ||
251 | static Client *swallowingclient(Window w); | ||
252 | static Client *termforwin(const Client *c); | ||
253 | static pid_t winpid(Window w); | ||
254 | |||
255 | /* variables */ | ||
256 | static const char broken[] = "broken"; | ||
257 | static char stext[256]; | ||
258 | static int statusw; | ||
259 | static int statussig; | ||
260 | static pid_t statuspid = -1; | ||
261 | static int screen; | ||
262 | static int sw, sh; /* X display screen geometry width, height */ | ||
263 | static int bh, blw = 0; /* bar geometry */ | ||
264 | static int lrpad; /* sum of left and right padding for text */ | ||
265 | static int (*xerrorxlib)(Display *, XErrorEvent *); | ||
266 | static unsigned int numlockmask = 0; | ||
267 | static 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 | }; | ||
283 | static Atom wmatom[WMLast], netatom[NetLast]; | ||
284 | static int running = 1; | ||
285 | static Cur *cursor[CurLast]; | ||
286 | static Clr **scheme; | ||
287 | static Display *dpy; | ||
288 | static Drw *drw; | ||
289 | static Monitor *mons, *selmon; | ||
290 | static Window root, wmcheckwin; | ||
291 | |||
292 | static 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. */ | ||
298 | struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; | ||
299 | |||
300 | /* function implementations */ | ||
301 | void | ||
302 | applyrules(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 | |||
342 | int | ||
343 | applysizehints(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 | |||
408 | void | ||
409 | arrange(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 | |||
422 | void | ||
423 | arrangemon(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 | |||
430 | void | ||
431 | attach(Client *c) | ||
432 | { | ||
433 | c->next = c->mon->clients; | ||
434 | c->mon->clients = c; | ||
435 | } | ||
436 | |||
437 | void | ||
438 | attachstack(Client *c) | ||
439 | { | ||
440 | c->snext = c->mon->stack; | ||
441 | c->mon->stack = c; | ||
442 | } | ||
443 | |||
444 | void | ||
445 | swallow(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 | |||
472 | void | ||
473 | unswallow(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 | |||
491 | void | ||
492 | buttonpress(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 | |||
553 | void | ||
554 | checkotherwm(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 | |||
564 | void | ||
565 | cleanup(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 | |||
591 | void | ||
592 | cleanupmon(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 | |||
607 | void | ||
608 | clientmessage(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 | |||
626 | void | ||
627 | configure(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 | |||
645 | void | ||
646 | configurenotify(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 | |||
673 | void | ||
674 | configurerequest(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 | |||
727 | Monitor * | ||
728 | createmon(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 | |||
748 | void | ||
749 | destroynotify(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 | |||
761 | void | ||
762 | detach(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 | |||
770 | void | ||
771 | detachstack(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 | |||
784 | Monitor * | ||
785 | dirtomon(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 | |||
799 | void | ||
800 | drawbar(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 | |||
868 | void | ||
869 | drawbars(void) | ||
870 | { | ||
871 | Monitor *m; | ||
872 | |||
873 | for (m = mons; m; m = m->next) | ||
874 | drawbar(m); | ||
875 | } | ||
876 | |||
877 | void | ||
878 | enternotify(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 | |||
896 | void | ||
897 | expose(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 | |||
906 | void | ||
907 | focus(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 */ | ||
932 | void | ||
933 | focusin(XEvent *e) | ||
934 | { | ||
935 | XFocusChangeEvent *ev = &e->xfocus; | ||
936 | |||
937 | if (selmon->sel && ev->window != selmon->sel->win) | ||
938 | setfocus(selmon->sel); | ||
939 | } | ||
940 | |||
941 | void | ||
942 | focusmon(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 | |||
955 | void | ||
956 | focusstack(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 | |||
981 | Atom | ||
982 | getatomprop(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 | |||
997 | pid_t | ||
998 | getstatusbarpid() | ||
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 | |||
1021 | int | ||
1022 | getrootptr(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 | |||
1031 | long | ||
1032 | getstate(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 | |||
1049 | int | ||
1050 | gettextprop(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 | |||
1074 | void | ||
1075 | grabbuttons(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 | |||
1095 | void | ||
1096 | grabkeys(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 | |||
1113 | void | ||
1114 | incnmaster(const Arg *arg) | ||
1115 | { | ||
1116 | selmon->nmaster = MAX(selmon->nmaster + arg->i, 0); | ||
1117 | arrange(selmon); | ||
1118 | } | ||
1119 | |||
1120 | #ifdef XINERAMA | ||
1121 | static int | ||
1122 | isuniquegeom(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 | |||
1132 | void | ||
1133 | keypress(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 | |||
1148 | void | ||
1149 | killclient(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 | |||
1164 | void | ||
1165 | manage(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 | |||
1230 | void | ||
1231 | mappingnotify(XEvent *e) | ||
1232 | { | ||
1233 | XMappingEvent *ev = &e->xmapping; | ||
1234 | |||
1235 | XRefreshKeyboardMapping(ev); | ||
1236 | if (ev->request == MappingKeyboard) | ||
1237 | grabkeys(); | ||
1238 | } | ||
1239 | |||
1240 | void | ||
1241 | maprequest(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 | |||
1254 | void | ||
1255 | monocle(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 | |||
1269 | void | ||
1270 | motionnotify(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 | |||
1286 | void | ||
1287 | movemouse(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 | |||
1346 | Client * | ||
1347 | nexttiled(Client *c) | ||
1348 | { | ||
1349 | for (; c && (c->isfloating || !ISVISIBLE(c)); c = c->next); | ||
1350 | return c; | ||
1351 | } | ||
1352 | |||
1353 | void | ||
1354 | pop(Client *c) | ||
1355 | { | ||
1356 | detach(c); | ||
1357 | attach(c); | ||
1358 | focus(c); | ||
1359 | arrange(c->mon); | ||
1360 | } | ||
1361 | |||
1362 | void | ||
1363 | propertynotify(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 | |||
1399 | void | ||
1400 | quit(const Arg *arg) | ||
1401 | { | ||
1402 | running = 0; | ||
1403 | } | ||
1404 | |||
1405 | Monitor * | ||
1406 | recttomon(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 | |||
1419 | void | ||
1420 | resize(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 | |||
1426 | void | ||
1427 | resizeclient(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 | |||
1441 | void | ||
1442 | resizemouse(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 | |||
1498 | void | ||
1499 | restack(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 | |||
1523 | void | ||
1524 | run(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 | |||
1534 | void | ||
1535 | scan(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 | |||
1561 | void | ||
1562 | sendmon(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 | |||
1577 | void | ||
1578 | setclientstate(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 | |||
1586 | int | ||
1587 | sendevent(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 | |||
1611 | void | ||
1612 | setfocus(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 | |||
1623 | void | ||
1624 | setfullscreen(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 | |||
1651 | void | ||
1652 | setlayout(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 */ | ||
1666 | void | ||
1667 | setmfact(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 | |||
1680 | void | ||
1681 | setup(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 | |||
1751 | void | ||
1752 | seturgent(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 | |||
1764 | void | ||
1765 | showhide(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 | |||
1782 | void | ||
1783 | sigchld(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 | |||
1790 | void | ||
1791 | sigstatusbar(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 | |||
1804 | void | ||
1805 | spawn(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 | |||
1818 | void | ||
1819 | tag(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 | |||
1828 | void | ||
1829 | tagmon(const Arg *arg) | ||
1830 | { | ||
1831 | if (!selmon->sel || !mons->next) | ||
1832 | return; | ||
1833 | sendmon(selmon->sel, dirtomon(arg->i)); | ||
1834 | } | ||
1835 | |||
1836 | void | ||
1837 | togglebar(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 | |||
1845 | void | ||
1846 | togglefloating(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 | |||
1859 | void | ||
1860 | toggletag(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 | |||
1874 | void | ||
1875 | toggleview(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 | |||
1886 | void | ||
1887 | unfocus(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 | |||
1899 | void | ||
1900 | unmanage(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 | |||
1941 | void | ||
1942 | unmapnotify(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 | |||
1955 | void | ||
1956 | updatebars(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 | |||
1977 | void | ||
1978 | updatebarpos(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 | |||
1990 | void | ||
1991 | updateclientlist() | ||
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 | |||
2004 | int | ||
2005 | updategeom(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 | |||
2082 | void | ||
2083 | updatenumlockmask(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 | |||
2098 | void | ||
2099 | updatesizehints(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 | |||
2141 | void | ||
2142 | updatestatus(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 | |||
2166 | void | ||
2167 | updatetitle(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 | |||
2175 | void | ||
2176 | updatewindowtype(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 | |||
2187 | void | ||
2188 | updatewmhints(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 | |||
2206 | void | ||
2207 | view(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 | |||
2218 | pid_t | ||
2219 | winpid(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 | |||
2271 | pid_t | ||
2272 | getparentprocess(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 | |||
2304 | int | ||
2305 | isdescprocess(pid_t p, pid_t c) | ||
2306 | { | ||
2307 | while (p != c && c != 0) | ||
2308 | c = getparentprocess(c); | ||
2309 | |||
2310 | return (int)c; | ||
2311 | } | ||
2312 | |||
2313 | Client * | ||
2314 | termforwin(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 | |||
2332 | Client * | ||
2333 | swallowingclient(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 | |||
2348 | Client * | ||
2349 | wintoclient(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 | |||
2361 | Monitor * | ||
2362 | wintomon(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. */ | ||
2381 | int | ||
2382 | xerror(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 | |||
2399 | int | ||
2400 | xerrordummy(Display *dpy, XErrorEvent *ee) | ||
2401 | { | ||
2402 | return 0; | ||
2403 | } | ||
2404 | |||
2405 | /* Startup Error handler to check if another window manager | ||
2406 | * is already running. */ | ||
2407 | int | ||
2408 | xerrorstart(Display *dpy, XErrorEvent *ee) | ||
2409 | { | ||
2410 | die("dwm: another window manager is already running"); | ||
2411 | return -1; | ||
2412 | } | ||
2413 | |||
2414 | void | ||
2415 | zoom(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 | |||
2428 | int | ||
2429 | main(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 @@ | |||
1 | hidevacant | ||
2 | statuscmd | ||
3 | steam | ||
4 | swallow | ||
5 | vanitygaps | ||
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 | |||
8 | int 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 | } | ||
@@ -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 | |||
9 | void * | ||
10 | ecalloc(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 | |||
19 | void | ||
20 | die(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 | } | ||
@@ -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 | |||
7 | void die(const char *fmt, ...); | ||
8 | void *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 */ | ||
2 | static void defaultgaps(const Arg *arg); | ||
3 | static void incrgaps(const Arg *arg); | ||
4 | static void incrigaps(const Arg *arg); | ||
5 | static void incrogaps(const Arg *arg); | ||
6 | static void incrohgaps(const Arg *arg); | ||
7 | static void incrovgaps(const Arg *arg); | ||
8 | static void incrihgaps(const Arg *arg); | ||
9 | static void incrivgaps(const Arg *arg); | ||
10 | static void togglegaps(const Arg *arg); | ||
11 | /* Layouts (delete the ones you do not need) */ | ||
12 | static void bstack(Monitor *m); | ||
13 | static void bstackhoriz(Monitor *m); | ||
14 | static void centeredmaster(Monitor *m); | ||
15 | static void centeredfloatingmaster(Monitor *m); | ||
16 | static void deck(Monitor *m); | ||
17 | static void dwindle(Monitor *m); | ||
18 | static void fibonacci(Monitor *m, int s); | ||
19 | static void grid(Monitor *m); | ||
20 | static void nrowgrid(Monitor *m); | ||
21 | static void spiral(Monitor *m); | ||
22 | static void tile(Monitor *m); | ||
23 | /* Internals */ | ||
24 | static void getgaps(Monitor *m, int *oh, int *ov, int *ih, int *iv, unsigned int *nc); | ||
25 | static void getfacts(Monitor *m, int msize, int ssize, float *mf, float *sf, int *mr, int *sr); | ||
26 | static void setgaps(int oh, int ov, int ih, int iv); | ||
27 | |||
28 | /* Settings */ | ||
29 | #if !PERTAG_PATCH | ||
30 | static int enablegaps = 1; | ||
31 | #endif // PERTAG_PATCH | ||
32 | |||
33 | void | ||
34 | setgaps(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 | |||
48 | void | ||
49 | togglegaps(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 | |||
59 | void | ||
60 | defaultgaps(const Arg *arg) | ||
61 | { | ||
62 | setgaps(gappoh, gappov, gappih, gappiv); | ||
63 | } | ||
64 | |||
65 | void | ||
66 | incrgaps(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 | |||
76 | void | ||
77 | incrigaps(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 | |||
87 | void | ||
88 | incrogaps(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 | |||
98 | void | ||
99 | incrohgaps(const Arg *arg) | ||
100 | { | ||
101 | setgaps( | ||
102 | selmon->gappoh + arg->i, | ||
103 | selmon->gappov, | ||
104 | selmon->gappih, | ||
105 | selmon->gappiv | ||
106 | ); | ||
107 | } | ||
108 | |||
109 | void | ||
110 | incrovgaps(const Arg *arg) | ||
111 | { | ||
112 | setgaps( | ||
113 | selmon->gappoh, | ||
114 | selmon->gappov + arg->i, | ||
115 | selmon->gappih, | ||
116 | selmon->gappiv | ||
117 | ); | ||
118 | } | ||
119 | |||
120 | void | ||
121 | incrihgaps(const Arg *arg) | ||
122 | { | ||
123 | setgaps( | ||
124 | selmon->gappoh, | ||
125 | selmon->gappov, | ||
126 | selmon->gappih + arg->i, | ||
127 | selmon->gappiv | ||
128 | ); | ||
129 | } | ||
130 | |||
131 | void | ||
132 | incrivgaps(const Arg *arg) | ||
133 | { | ||
134 | setgaps( | ||
135 | selmon->gappoh, | ||
136 | selmon->gappov, | ||
137 | selmon->gappih, | ||
138 | selmon->gappiv + arg->i | ||
139 | ); | ||
140 | } | ||
141 | |||
142 | void | ||
143 | getgaps(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 | |||
165 | void | ||
166 | getfacts(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 | */ | ||
197 | static void | ||
198 | bstack(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 | |||
238 | static void | ||
239 | bstackhoriz(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 | */ | ||
284 | void | ||
285 | centeredmaster(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 | |||
369 | void | ||
370 | centeredfloatingmaster(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 | */ | ||
426 | void | ||
427 | deck(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 | */ | ||
471 | void | ||
472 | fibonacci(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 | |||
558 | void | ||
559 | dwindle(Monitor *m) | ||
560 | { | ||
561 | fibonacci(m, 1); | ||
562 | } | ||
563 | |||
564 | void | ||
565 | spiral(Monitor *m) | ||
566 | { | ||
567 | fibonacci(m, 0); | ||
568 | } | ||
569 | |||
570 | /* | ||
571 | * Gappless grid layout + gaps (ironically) | ||
572 | * https://dwm.suckless.org/patches/gaplessgrid/ | ||
573 | */ | ||
574 | void | ||
575 | gaplessgrid(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 | */ | ||
627 | void | ||
628 | grid(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 | */ | ||
661 | void | ||
662 | horizgrid(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 | */ | ||
715 | void | ||
716 | nrowgrid(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 | */ | ||
772 | static void | ||
773 | tile(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 | } | ||