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