├── .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 | ![Screenshot1](https://raw.githubusercontent.com/dark-Jedi2108/bedwm/main/.github/screenshots/n1.png) 8 | 9 | ![Screenshot1](https://raw.githubusercontent.com/dark-Jedi2108/bedwm/main/.github/screenshots/n2.png) 10 | 11 | ![Screenshot1](https://raw.githubusercontent.com/dark-Jedi2108/bedwm/main/.github/screenshots/n3.png) 12 | 13 | ![Screenshot1](https://raw.githubusercontent.com/dark-Jedi2108/bedwm/main/.github/screenshots/n4.png) 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 | --------------------------------------------------------------------------------