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