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