├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── compile_flags.txt ├── config.def.h ├── config.mk ├── drw.c ├── drw.h ├── dwm-tag-preview-6.2.diff ├── dwm.c ├── dwm.png ├── patches ├── shiftview.c └── vanitygaps.c ├── transient.c ├── util.c └── util.h /.gitignore: -------------------------------------------------------------------------------- 1 | drw.o 2 | dwm 3 | dwm.o 4 | util.o 5 | config.h 6 | todo/ 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT/X Consortium License 2 | 3 | © 2006-2019 Anselm R Garbe 4 | © 2006-2009 Jukka Salmi 5 | © 2006-2007 Sander van Dijk 6 | © 2007-2011 Peter Hartlich 7 | © 2007-2009 Szabolcs Nagy 8 | © 2007-2009 Christof Musik 9 | © 2007-2009 Premysl Hruby 10 | © 2007-2008 Enno Gottox Boland 11 | © 2008 Martin Hurton 12 | © 2008 Neale Pickett 13 | © 2009 Mate Nagy 14 | © 2010-2016 Hiltjo Posthuma 15 | © 2010-2012 Connor Lane Smith 16 | © 2011 Christoph Lohmann <20h@r-36.net> 17 | © 2015-2016 Quentin Rameau 18 | © 2015-2016 Eric Pruitt 19 | © 2016-2017 Markus Teich 20 | 21 | Permission is hereby granted, free of charge, to any person obtaining a 22 | copy of this software and associated documentation files (the "Software"), 23 | to deal in the Software without restriction, including without limitation 24 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 25 | and/or sell copies of the Software, and to permit persons to whom the 26 | Software is furnished to do so, subject to the following conditions: 27 | 28 | The above copyright notice and this permission notice shall be included in 29 | all copies or substantial portions of the Software. 30 | 31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 32 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 33 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 34 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 35 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 36 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 37 | DEALINGS IN THE SOFTWARE. 38 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # dwm - dynamic window manager 2 | # See LICENSE file for copyright and license details. 3 | 4 | include config.mk 5 | 6 | SRC = drw.c dwm.c util.c 7 | OBJ = ${SRC:.c=.o} 8 | 9 | all: options dwm 10 | 11 | options: 12 | @echo dwm build options: 13 | @echo "CFLAGS = ${CFLAGS}" 14 | @echo "LDFLAGS = ${LDFLAGS}" 15 | @echo "CC = ${CC}" 16 | 17 | .c.o: 18 | ${CC} -c ${CFLAGS} $< 19 | 20 | ${OBJ}: config.h config.mk 21 | 22 | config.h: 23 | cp config.def.h $@ 24 | 25 | dwm: ${OBJ} 26 | ${CC} -o $@ ${OBJ} ${LDFLAGS} 27 | 28 | clean: 29 | rm -f dwm ${OBJ} dwm-${VERSION}.tar.gz 30 | 31 | dist: clean 32 | mkdir -p dwm-${VERSION} 33 | cp -R LICENSE Makefile README config.def.h config.mk\ 34 | # dwm.1 drw.h util.h ${SRC} dwm.png transient.c dwm-${VERSION} 35 | tar -cf dwm-${VERSION}.tar dwm-${VERSION} 36 | gzip dwm-${VERSION}.tar 37 | rm -rf dwm-${VERSION} 38 | 39 | install: all 40 | mkdir -p ${DESTDIR}${PREFIX}/bin 41 | cp -f dwm ${DESTDIR}${PREFIX}/bin 42 | chmod 755 ${DESTDIR}${PREFIX}/bin/dwm 43 | mkdir -p ${DESTDIR}${MANPREFIX}/man1 44 | # sed "s/VERSION/${VERSION}/g" < dwm.1 > ${DESTDIR}${MANPREFIX}/man1/dwm.1 45 | # chmod 644 ${DESTDIR}${MANPREFIX}/man1/dwm.1 46 | 47 | uninstall: 48 | rm -f ${DESTDIR}${PREFIX}/bin/dwm\ 49 | # ${DESTDIR}${MANPREFIX}/man1/dwm.1 50 | 51 | .PHONY: all options clean dist install uninstall 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # dwm - dynamic window manager 2 | 3 | dwm is an extremely fast, small, and dynamic window manager for X. 4 | 5 | ## My Patches 6 | 7 | This is in the order that I patched everything: 8 | 9 | - [shiftview](https://lists.suckless.org/dev/1104/7590.html) 10 | - [vanitygaps](https://dwm.suckless.org/patches/vanitygaps/) 11 | - [systray](https://dwm.suckless.org/patches/systray/) 12 | - [staticstatus](https://dwm.suckless.org/patches/staticstatus/) 13 | - [hide vacant tags](https://dwm.suckless.org/patches/hide_vacant_tags/) 14 | - [cycle layout](https://dwm.suckless.org/patches/cyclelayouts/) 15 | - [always center](https://dwm.suckless.org/patches/alwayscenter/) 16 | - [rotate stack](https://dwm.suckless.org/patches/rotatestack/) 17 | - [notify thing](https://dwm.suckless.org/patches/focusonnetactive/) 18 | - [warp](https://dwm.suckless.org/patches/warp/) 19 | - [no border](https://dwm.suckless.org/patches/noborder/) 20 | 21 | ## Exploding Windows 22 | 23 | With `skippy-xd` you can explode your windows and warp to whatever window you want. 24 | 25 | ## Media keys 26 | 27 | Here is a helpful [link](https://gist.github.com/palopezv/efd34059af6126ad970940bcc6a90f2e) 28 | 29 | **NOTE** (I had to change "0" to "@DEFAULT_SINK@") 30 | 31 | ## TODO 32 | 33 | - statuscmd patch 34 | - Add sticky patch 35 | - scratchpad (maybe named scratchpad) 36 | - alternative tags 37 | - maybe steam 38 | - Add resize corner patch 39 | - Add swallow patch 40 | 41 | ## Probably not 42 | 43 | - pertag 44 | - awesome bar 45 | - Add alpha patch 46 | -------------------------------------------------------------------------------- /compile_flags.txt: -------------------------------------------------------------------------------- 1 | -I 2 | /usr/include/freetype2 3 | -------------------------------------------------------------------------------- /config.def.h: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | 3 | 4 | /* appearance */ 5 | static const unsigned int borderpx = 3; /* border pixel of windows */ 6 | static const unsigned int snap = 32; /* snap pixel */ 7 | static const unsigned int systraypinning = 2; /* 0: sloppy systray follows selected monitor, >0: pin systray to monitor X */ 8 | static const unsigned int systrayspacing = 8; /* systray spacing */ 9 | static const int systraypinningfailfirst = 1; /* 1: if pinning fails, display systray on the first monitor, False: display systray on the last monitor*/ 10 | static const int showsystray = 1; /* 0 means no systray */ 11 | static const unsigned int gappih = 23; /* horiz inner gap between windows */ 12 | static const unsigned int gappiv = 23; /* vert inner gap between windows */ 13 | static const unsigned int gappoh = 23; /* horiz outer gap between windows and screen edge */ 14 | static const unsigned int gappov = 23; /* vert outer gap between windows and screen edge */ 15 | static int smartgaps = 0; /* 1 means no outer gap when there is only one window */ 16 | static const int showbar = 1; /* 0 means no bar */ 17 | static const int topbar = 1; /* 0 means bottom bar */ 18 | static const char *fonts[] = { "Source Code Pro Regular:size=13", "JoyPixels:pixelsize=14:antialias=true:autohint=true" }; 19 | static const char *upvol[] = { "/usr/bin/pactl", "set-sink-volume", "@DEFAULT_SINK@", "+5%", NULL }; 20 | static const char *downvol[] = { "/usr/bin/pactl", "set-sink-volume", "@DEFAULT_SINK@", "-5%", NULL }; 21 | static const char *mutevol[] = { "/usr/bin/pactl", "set-sink-mute", "@DEFAULT_SINK@", "toggle", NULL }; 22 | static const char col_gray1[] = "#1f2227"; 23 | static const char col_gray2[] = "#abb2bf"; 24 | static const char col_gray3[] = "#abb2bf"; 25 | static const char col_gray4[] = "#abb2bf"; 26 | static const char col_cyan[] = "#88c0d0"; 27 | static const char col_purple[] = "#B48EAD"; 28 | static const char col_red[] = "#d54646"; 29 | static const char col_green[] = "#23d18b"; 30 | static const char col_yellow[] = "#d7ba7d"; 31 | static const char col_blue[] = "#81a1c1"; 32 | static const char col_info_blue[] = "#4fc1ff"; 33 | static const char col_magenta[] = "#c586c0"; 34 | static const char col_white[] = "#abb2bf"; 35 | static const char col_bg_alt[] = "#212121"; 36 | 37 | static const char *colors[][3] = { 38 | /* fg bg border */ 39 | [SchemeNorm] = { col_gray4, col_gray1, col_gray2 }, 40 | [SchemeSel] = { col_gray4, col_blue, col_blue }, 41 | [SchemeStatus] = { col_gray4, col_gray1, "#000000" }, // Statusbar right {text,background,not used but cannot be empty} 42 | 43 | [SchemeTagsSel] = { col_info_blue, col_gray1, "#000000" }, // Tagbar left selected {text,background,not used but cannot be empty} 44 | [SchemeTagsNorm] = { col_gray3, col_gray1, "#000000" }, // Tagbar left unselected {text,background,not used but cannot be empty} 45 | 46 | [SchemeInfoSel] = { col_yellow, col_gray1, "#000000" }, // infobar middle selected {text,background,not used but cannot be empty} 47 | [SchemeInfoNorm] = { col_yellow, col_gray1, "#000000" }, // infobar middle unselected {text,background,not used but cannot be empty} 48 | }; 49 | 50 | /* helper for spawning shell commands in the pre dwm-5.0 fashion */ 51 | #define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } } 52 | 53 | /* staticstatus */ 54 | static const int statmonval = 0; 55 | 56 | /* tagging */ 57 | static const char *tags[] = { " 1 ", " 2 ", " 3 ", " 4 ", " 5 ", " 6 ", " 7 ", " 8 ", " 9 " }; 58 | 59 | static const Rule rules[] = { 60 | /* xprop(1): 61 | * WM_CLASS(STRING) = instance, class 62 | * WM_NAME(STRING) = title 63 | */ 64 | /* class instance title tags mask isfloating monitor */ 65 | { "Gimp", NULL, NULL, 0, 0, -1 }, 66 | { "Firefox", NULL, NULL, 1 << 8, 0, -1 }, 67 | }; 68 | 69 | /* layout(s) */ 70 | static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */ 71 | static const int nmaster = 1; /* number of clients in master area */ 72 | static const int resizehints = 1; /* 1 means respect size hints in tiled resizals */ 73 | 74 | #define FORCE_VSPLIT 1 /* nrowgrid layout: force two clients to always split vertically */ 75 | #include "./patches/vanitygaps.c" 76 | 77 | /* #include "./patches/fibonacci.c" */ 78 | static const Layout layouts[] = { 79 | /* symbol arrange function */ 80 | { "[]=", tile }, /* first entry is default */ 81 | { "[\\]", dwindle }, 82 | { "[M]", monocle }, 83 | { "><>", NULL }, /* no layout function means floating behavior */ 84 | { "|M|", centeredmaster }, 85 | { ">M>", centeredfloatingmaster }, 86 | { NULL, NULL }, 87 | /* { "H[]", deck },*/ 88 | /* { "TTT", bstack }, */ 89 | /* { "[@]", spiral }, */ 90 | }; 91 | 92 | /* key definitions */ 93 | #define MODKEY Mod4Mask 94 | #define MODKEY_ALT Mod1Mask 95 | #define TAGKEYS(KEY,TAG) \ 96 | { MODKEY, KEY, view, {.ui = 1 << TAG} }, \ 97 | { MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \ 98 | { MODKEY|ShiftMask, KEY, tag, {.ui = 1 << TAG} }, \ 99 | { MODKEY|ControlMask|ShiftMask, KEY, toggletag, {.ui = 1 << TAG} }, 100 | 101 | /* helper for spawning shell commands in the pre dwm-5.0 fashion */ 102 | #define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } } 103 | 104 | /* commands */ 105 | static const char *termcmd[] = { "st", NULL }; 106 | 107 | #include 108 | #include "./patches/shiftview.c" 109 | static Key keys[] = { 110 | /* modifier key function argument */ 111 | { MODKEY, XK_Return, spawn, {.v = termcmd } }, 112 | { MODKEY, XK_b, spawn, SHCMD("feh --bg-fill --randomize ~/Pictures/wallpapers/* &") }, 113 | { MODKEY|ShiftMask, XK_b, togglebar, {0} }, 114 | { MODKEY|ShiftMask, XK_j, rotatestack, {.i = +1 } }, 115 | { MODKEY|ShiftMask, XK_k, rotatestack, {.i = -1 } }, 116 | { MODKEY, XK_j, focusstack, {.i = +1 } }, 117 | { MODKEY, XK_k, focusstack, {.i = -1 } }, 118 | { MODKEY, XK_i, incnmaster, {.i = +1 } }, 119 | { MODKEY, XK_d, incnmaster, {.i = -1 } }, 120 | // { MODKEY_ALT, XK_h, setmfact, {.f = -0.05} }, 121 | // { MODKEY_ALT, XK_l, setmfact, {.f = +0.05} }, 122 | // { MODKEY_ALT, XK_j, setmfact, {.f = -0.05} }, 123 | // { MODKEY_ALT, XK_k, setmfact, {.f = +0.05} }, 124 | { MODKEY|ShiftMask, XK_Return, zoom, {0} }, 125 | { MODKEY, XK_q, killclient, {0} }, 126 | { MODKEY, XK_s, spawn, SHCMD("restream -p") }, 127 | { MODKEY, XK_space, spawn, SHCMD("dmenu_run") }, 128 | { MODKEY|ShiftMask, XK_space, setlayout, {0} }, 129 | { MODKEY, XK_e, spawn, SHCMD("nautilus") }, 130 | { MODKEY, XK_w, spawn, SHCMD("$BROWSER") }, 131 | { MODKEY, XK_r, spawn, SHCMD("$TERMINAL -e ranger") }, 132 | { MODKEY, XK_y, spawn, SHCMD("flameshot gui -p ~/Pictures/screenshots") }, 133 | { MODKEY|ShiftMask, XK_y, spawn, SHCMD("flameshot full -p ~/Pictures/screenshots") }, 134 | { MODKEY|ShiftMask, XK_v, spawn, SHCMD("mpv --profile=low-latency /dev/video0") }, 135 | { MODKEY, XK_semicolon, spawn, SHCMD("skippy-xd") }, 136 | { MODKEY, XK_comma, focusmon, {.i = -1 } }, 137 | { MODKEY, XK_period, focusmon, {.i = +1 } }, 138 | { MODKEY, XK_l, shiftview, {.i = +1 } }, 139 | { MODKEY, XK_h, shiftview, {.i = -1 } }, 140 | { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, // tile 141 | { MODKEY|ShiftMask, XK_t, setlayout, {.v = &layouts[1]} }, // tile 142 | { MODKEY, XK_f, setlayout, {.v = &layouts[2]} }, // float 143 | /* { MODKEY|ShiftMask, XK_f, setlayout, {.v = &layouts[3]} }, // float */ 144 | { MODKEY|ShiftMask, XK_f, togglefloating, {0} }, 145 | { MODKEY, XK_c, setlayout, {.v = &layouts[4]} }, // monocle 146 | { MODKEY|ShiftMask, XK_c, setlayout, {.v = &layouts[5]} }, // dwindle 147 | { MODKEY, XK_g, togglegaps, {0} }, 148 | { MODKEY|ShiftMask, XK_g, defaultgaps, {0} }, 149 | { MODKEY|ControlMask, XK_comma, cyclelayout, {.i = -1 } }, 150 | { MODKEY|ControlMask, XK_period, cyclelayout, {.i = +1 } }, 151 | { MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } }, 152 | { MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } }, 153 | { MODKEY|ShiftMask, XK_q, quit, {0} }, 154 | { MODKEY, XK_F2, spawn, SHCMD("screenkey -s small --scr 2 -p fixed -g 400x100+4700+25 --opacity .6 --font-color white") }, 155 | { MODKEY, XK_F3, spawn, SHCMD("killall screenkey") }, 156 | TAGKEYS( XK_1, 0) 157 | TAGKEYS( XK_2, 1) 158 | TAGKEYS( XK_3, 2) 159 | TAGKEYS( XK_4, 3) 160 | TAGKEYS( XK_5, 4) 161 | TAGKEYS( XK_6, 5) 162 | TAGKEYS( XK_7, 6) 163 | TAGKEYS( XK_8, 7) 164 | TAGKEYS( XK_9, 8) 165 | /* { MODKEY, XK_0, view, {.ui = ~0 } }, */ 166 | /* { MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } }, */ 167 | { 0, XF86XK_AudioLowerVolume, spawn, {.v = downvol } }, 168 | { 0, XF86XK_AudioRaiseVolume, spawn, {.v = upvol } }, 169 | { 0, XF86XK_AudioMute, spawn, {.v = mutevol } }, 170 | { MODKEY, XK_minus, spawn, {.v = downvol } }, 171 | { MODKEY, XK_equal, spawn, {.v = upvol } }, 172 | { MODKEY, XK_0, spawn, {.v = mutevol } }, 173 | /* { MODKEY, XK_minus, focusmon, {.i = -1 } }, */ 174 | /* { MODKEY, XK_equal, focusmon, {.i = +1 } }, */ 175 | /* { MODKEY, XK_0, focusmon, {.i = +1 } }, */ 176 | /* { MODKEY|ShiftMask, XK_minus, tagmon, {.i = -1 } }, */ 177 | /* { MODKEY|ShiftMask, XK_equal, tagmon, {.i = +1 } }, */ 178 | }; 179 | 180 | 181 | /* bindsym $mod+F1 restart */ 182 | /* bindsym $mod+F2 exec --no-startup-id screenkey -s small --scr 1 -p fixed -g 600x100+2573+1330 --opacity .9 --font-color white */ 183 | /* bindsym $mod+F3 exec --no-startup-id killall screenkey */ 184 | /* bindsym $mod+F12 exec $term -e nmtui */ 185 | 186 | /* button definitions */ 187 | /* click can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */ 188 | static Button buttons[] = { 189 | /* click event mask button function argument */ 190 | { ClkLtSymbol, 0, Button1, setlayout, {0} }, 191 | { ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} }, 192 | { ClkStatusText, 0, Button2, spawn, {.v = termcmd } }, 193 | { ClkClientWin, MODKEY, Button1, movemouse, {0} }, 194 | { ClkClientWin, MODKEY, Button2, togglefloating, {0} }, 195 | { ClkClientWin, MODKEY, Button3, resizemouse, {0} }, 196 | { ClkTagBar, 0, Button1, view, {0} }, 197 | { ClkTagBar, 0, Button3, toggleview, {0} }, 198 | { ClkTagBar, MODKEY, Button1, tag, {0} }, 199 | { ClkTagBar, MODKEY, Button3, toggletag, {0} }, 200 | }; 201 | 202 | -------------------------------------------------------------------------------- /config.mk: -------------------------------------------------------------------------------- 1 | # dwm version 2 | VERSION = 6.2 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 | 23 | # includes and libs 24 | INCS = -I${X11INC} -I${FREETYPEINC} 25 | LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} 26 | 27 | # flags 28 | CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} 29 | #CFLAGS = -g -std=c99 -pedantic -Wall -O0 ${INCS} ${CPPFLAGS} 30 | CFLAGS = -std=c99 -pedantic -Wall -Wno-deprecated-declarations -Os ${INCS} ${CPPFLAGS} 31 | LDFLAGS = ${LIBS} 32 | 33 | # Solaris 34 | #CFLAGS = -fast ${INCS} -DVERSION=\"${VERSION}\" 35 | #LDFLAGS = ${LIBS} 36 | 37 | # compiler and linker 38 | CC = cc 39 | -------------------------------------------------------------------------------- /drw.c: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "drw.h" 9 | #include "util.h" 10 | 11 | #define UTF_INVALID 0xFFFD 12 | #define UTF_SIZ 4 13 | 14 | static const unsigned char utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; 15 | static const unsigned char utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; 16 | static const long utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000}; 17 | static const long utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; 18 | 19 | static long 20 | utf8decodebyte(const char c, size_t *i) 21 | { 22 | for (*i = 0; *i < (UTF_SIZ + 1); ++(*i)) 23 | if (((unsigned char)c & utfmask[*i]) == utfbyte[*i]) 24 | return (unsigned char)c & ~utfmask[*i]; 25 | return 0; 26 | } 27 | 28 | static size_t 29 | utf8validate(long *u, size_t i) 30 | { 31 | if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF)) 32 | *u = UTF_INVALID; 33 | for (i = 1; *u > utfmax[i]; ++i) 34 | ; 35 | return i; 36 | } 37 | 38 | static size_t 39 | utf8decode(const char *c, long *u, size_t clen) 40 | { 41 | size_t i, j, len, type; 42 | long udecoded; 43 | 44 | *u = UTF_INVALID; 45 | if (!clen) 46 | return 0; 47 | udecoded = utf8decodebyte(c[0], &len); 48 | if (!BETWEEN(len, 1, UTF_SIZ)) 49 | return 1; 50 | for (i = 1, j = 1; i < clen && j < len; ++i, ++j) { 51 | udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type); 52 | if (type) 53 | return j; 54 | } 55 | if (j < len) 56 | return 0; 57 | *u = udecoded; 58 | utf8validate(u, len); 59 | 60 | return len; 61 | } 62 | 63 | Drw * 64 | drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h) 65 | { 66 | Drw *drw = ecalloc(1, sizeof(Drw)); 67 | 68 | drw->dpy = dpy; 69 | drw->screen = screen; 70 | drw->root = root; 71 | drw->w = w; 72 | drw->h = h; 73 | drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen)); 74 | drw->gc = XCreateGC(dpy, root, 0, NULL); 75 | XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter); 76 | 77 | return drw; 78 | } 79 | 80 | void 81 | drw_resize(Drw *drw, unsigned int w, unsigned int h) 82 | { 83 | if (!drw) 84 | return; 85 | 86 | drw->w = w; 87 | drw->h = h; 88 | if (drw->drawable) 89 | XFreePixmap(drw->dpy, drw->drawable); 90 | drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen)); 91 | } 92 | 93 | void 94 | drw_free(Drw *drw) 95 | { 96 | XFreePixmap(drw->dpy, drw->drawable); 97 | XFreeGC(drw->dpy, drw->gc); 98 | drw_fontset_free(drw->fonts); 99 | free(drw); 100 | } 101 | 102 | /* This function is an implementation detail. Library users should use 103 | * drw_fontset_create instead. 104 | */ 105 | static Fnt * 106 | xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern) 107 | { 108 | Fnt *font; 109 | XftFont *xfont = NULL; 110 | FcPattern *pattern = NULL; 111 | 112 | if (fontname) { 113 | /* Using the pattern found at font->xfont->pattern does not yield the 114 | * same substitution results as using the pattern returned by 115 | * FcNameParse; using the latter results in the desired fallback 116 | * behaviour whereas the former just results in missing-character 117 | * rectangles being drawn, at least with some fonts. */ 118 | if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fontname))) { 119 | fprintf(stderr, "error, cannot load font from name: '%s'\n", fontname); 120 | return NULL; 121 | } 122 | if (!(pattern = FcNameParse((FcChar8 *) fontname))) { 123 | fprintf(stderr, "error, cannot parse font name to pattern: '%s'\n", fontname); 124 | XftFontClose(drw->dpy, xfont); 125 | return NULL; 126 | } 127 | } else if (fontpattern) { 128 | if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) { 129 | fprintf(stderr, "error, cannot load font from pattern.\n"); 130 | return NULL; 131 | } 132 | } else { 133 | die("no font specified."); 134 | } 135 | 136 | /* Do not allow using color fonts. This is a workaround for a BadLength 137 | * error from Xft with color glyphs. Modelled on the Xterm workaround. See 138 | * https://bugzilla.redhat.com/show_bug.cgi?id=1498269 139 | * https://lists.suckless.org/dev/1701/30932.html 140 | * https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=916349 141 | * and lots more all over the internet. 142 | */ 143 | /* FcBool iscol; */ 144 | /* if(FcPatternGetBool(xfont->pattern, FC_COLOR, 0, &iscol) == FcResultMatch && iscol) { */ 145 | /* XftFontClose(drw->dpy, xfont); */ 146 | /* return NULL; */ 147 | /* } */ 148 | 149 | font = ecalloc(1, sizeof(Fnt)); 150 | font->xfont = xfont; 151 | font->pattern = pattern; 152 | font->h = xfont->ascent + xfont->descent; 153 | font->dpy = drw->dpy; 154 | 155 | return font; 156 | } 157 | 158 | static void 159 | xfont_free(Fnt *font) 160 | { 161 | if (!font) 162 | return; 163 | if (font->pattern) 164 | FcPatternDestroy(font->pattern); 165 | XftFontClose(font->dpy, font->xfont); 166 | free(font); 167 | } 168 | 169 | Fnt* 170 | drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount) 171 | { 172 | Fnt *cur, *ret = NULL; 173 | size_t i; 174 | 175 | if (!drw || !fonts) 176 | return NULL; 177 | 178 | for (i = 1; i <= fontcount; i++) { 179 | if ((cur = xfont_create(drw, fonts[fontcount - i], NULL))) { 180 | cur->next = ret; 181 | ret = cur; 182 | } 183 | } 184 | return (drw->fonts = ret); 185 | } 186 | 187 | void 188 | drw_fontset_free(Fnt *font) 189 | { 190 | if (font) { 191 | drw_fontset_free(font->next); 192 | xfont_free(font); 193 | } 194 | } 195 | 196 | void 197 | drw_clr_create(Drw *drw, Clr *dest, const char *clrname) 198 | { 199 | if (!drw || !dest || !clrname) 200 | return; 201 | 202 | if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen), 203 | DefaultColormap(drw->dpy, drw->screen), 204 | clrname, dest)) 205 | die("error, cannot allocate color '%s'", clrname); 206 | 207 | // TODO add this for borders not affected by transparency 208 | dest->pixel |= 0xff << 24; 209 | } 210 | 211 | /* Wrapper to create color schemes. The caller has to call free(3) on the 212 | * returned color scheme when done using it. */ 213 | Clr * 214 | drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount) 215 | { 216 | size_t i; 217 | Clr *ret; 218 | 219 | /* need at least two colors for a scheme */ 220 | if (!drw || !clrnames || clrcount < 2 || !(ret = ecalloc(clrcount, sizeof(XftColor)))) 221 | return NULL; 222 | 223 | for (i = 0; i < clrcount; i++) 224 | drw_clr_create(drw, &ret[i], clrnames[i]); 225 | return ret; 226 | } 227 | 228 | void 229 | drw_setfontset(Drw *drw, Fnt *set) 230 | { 231 | if (drw) 232 | drw->fonts = set; 233 | } 234 | 235 | void 236 | drw_setscheme(Drw *drw, Clr *scm) 237 | { 238 | if (drw) 239 | drw->scheme = scm; 240 | } 241 | 242 | void 243 | drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert) 244 | { 245 | if (!drw || !drw->scheme) 246 | return; 247 | XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme[ColBg].pixel : drw->scheme[ColFg].pixel); 248 | if (filled) 249 | XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); 250 | else 251 | XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w - 1, h - 1); 252 | } 253 | 254 | int 255 | drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert) 256 | { 257 | char buf[1024]; 258 | int ty; 259 | unsigned int ew; 260 | XftDraw *d = NULL; 261 | Fnt *usedfont, *curfont, *nextfont; 262 | size_t i, len; 263 | int utf8strlen, utf8charlen, render = x || y || w || h; 264 | long utf8codepoint = 0; 265 | const char *utf8str; 266 | FcCharSet *fccharset; 267 | FcPattern *fcpattern; 268 | FcPattern *match; 269 | XftResult result; 270 | int charexists = 0; 271 | 272 | if (!drw || (render && !drw->scheme) || !text || !drw->fonts) 273 | return 0; 274 | 275 | if (!render) { 276 | w = ~w; 277 | } else { 278 | XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel); 279 | XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); 280 | d = XftDrawCreate(drw->dpy, drw->drawable, 281 | DefaultVisual(drw->dpy, drw->screen), 282 | DefaultColormap(drw->dpy, drw->screen)); 283 | x += lpad; 284 | w -= lpad; 285 | } 286 | 287 | usedfont = drw->fonts; 288 | while (1) { 289 | utf8strlen = 0; 290 | utf8str = text; 291 | nextfont = NULL; 292 | while (*text) { 293 | utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ); 294 | for (curfont = drw->fonts; curfont; curfont = curfont->next) { 295 | charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint); 296 | if (charexists) { 297 | if (curfont == usedfont) { 298 | utf8strlen += utf8charlen; 299 | text += utf8charlen; 300 | } else { 301 | nextfont = curfont; 302 | } 303 | break; 304 | } 305 | } 306 | 307 | if (!charexists || nextfont) 308 | break; 309 | else 310 | charexists = 0; 311 | } 312 | 313 | if (utf8strlen) { 314 | drw_font_getexts(usedfont, utf8str, utf8strlen, &ew, NULL); 315 | /* shorten text if necessary */ 316 | for (len = MIN(utf8strlen, sizeof(buf) - 1); len && ew > w; len--) 317 | drw_font_getexts(usedfont, utf8str, len, &ew, NULL); 318 | 319 | if (len) { 320 | memcpy(buf, utf8str, len); 321 | buf[len] = '\0'; 322 | if (len < utf8strlen) 323 | for (i = len; i && i > len - 3; buf[--i] = '.') 324 | ; /* NOP */ 325 | 326 | if (render) { 327 | ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent; 328 | XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg], 329 | usedfont->xfont, x, ty, (XftChar8 *)buf, len); 330 | } 331 | x += ew; 332 | w -= ew; 333 | } 334 | } 335 | 336 | if (!*text) { 337 | break; 338 | } else if (nextfont) { 339 | charexists = 0; 340 | usedfont = nextfont; 341 | } else { 342 | /* Regardless of whether or not a fallback font is found, the 343 | * character must be drawn. */ 344 | charexists = 1; 345 | 346 | fccharset = FcCharSetCreate(); 347 | FcCharSetAddChar(fccharset, utf8codepoint); 348 | 349 | if (!drw->fonts->pattern) { 350 | /* Refer to the comment in xfont_create for more information. */ 351 | die("the first font in the cache must be loaded from a font string."); 352 | } 353 | 354 | fcpattern = FcPatternDuplicate(drw->fonts->pattern); 355 | FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset); 356 | FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue); 357 | FcPatternAddBool(fcpattern, FC_COLOR, FcFalse); 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 | usedfont = drw->fonts; 375 | } 376 | } 377 | } 378 | } 379 | if (d) 380 | XftDrawDestroy(d); 381 | 382 | return x + (render ? w : 0); 383 | } 384 | 385 | void 386 | drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h) 387 | { 388 | if (!drw) 389 | return; 390 | 391 | XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y); 392 | XSync(drw->dpy, False); 393 | } 394 | 395 | unsigned int 396 | drw_fontset_getwidth(Drw *drw, const char *text) 397 | { 398 | if (!drw || !drw->fonts || !text) 399 | return 0; 400 | return drw_text(drw, 0, 0, 0, 0, 0, text, 0); 401 | } 402 | 403 | void 404 | drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h) 405 | { 406 | XGlyphInfo ext; 407 | 408 | if (!font || !text) 409 | return; 410 | 411 | XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext); 412 | if (w) 413 | *w = ext.xOff; 414 | if (h) 415 | *h = font->h; 416 | } 417 | 418 | Cur * 419 | drw_cur_create(Drw *drw, int shape) 420 | { 421 | Cur *cur; 422 | 423 | if (!drw || !(cur = ecalloc(1, sizeof(Cur)))) 424 | return NULL; 425 | 426 | cur->cursor = XCreateFontCursor(drw->dpy, shape); 427 | 428 | return cur; 429 | } 430 | 431 | void 432 | drw_cur_free(Drw *drw, Cur *cursor) 433 | { 434 | if (!cursor) 435 | return; 436 | 437 | XFreeCursor(drw->dpy, cursor->cursor); 438 | free(cursor); 439 | } 440 | -------------------------------------------------------------------------------- /drw.h: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | 3 | typedef struct { 4 | Cursor cursor; 5 | } Cur; 6 | 7 | typedef struct Fnt { 8 | Display *dpy; 9 | unsigned int h; 10 | XftFont *xfont; 11 | FcPattern *pattern; 12 | struct Fnt *next; 13 | } Fnt; 14 | 15 | enum { ColFg, ColBg, ColBorder }; /* Clr scheme index */ 16 | typedef XftColor Clr; 17 | 18 | typedef struct { 19 | unsigned int w, h; 20 | Display *dpy; 21 | int screen; 22 | Window root; 23 | Drawable drawable; 24 | GC gc; 25 | Clr *scheme; 26 | Fnt *fonts; 27 | } Drw; 28 | 29 | /* Drawable abstraction */ 30 | Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h); 31 | void drw_resize(Drw *drw, unsigned int w, unsigned int h); 32 | void drw_free(Drw *drw); 33 | 34 | /* Fnt abstraction */ 35 | Fnt *drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount); 36 | void drw_fontset_free(Fnt* set); 37 | unsigned int drw_fontset_getwidth(Drw *drw, const char *text); 38 | void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h); 39 | 40 | /* Colorscheme abstraction */ 41 | void drw_clr_create(Drw *drw, Clr *dest, const char *clrname); 42 | Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount); 43 | 44 | /* Cursor abstraction */ 45 | Cur *drw_cur_create(Drw *drw, int shape); 46 | void drw_cur_free(Drw *drw, Cur *cursor); 47 | 48 | /* Drawing context manipulation */ 49 | void drw_setfontset(Drw *drw, Fnt *set); 50 | void drw_setscheme(Drw *drw, Clr *scm); 51 | 52 | /* Drawing functions */ 53 | void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert); 54 | int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert); 55 | 56 | /* Map functions */ 57 | void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h); 58 | -------------------------------------------------------------------------------- /dwm-tag-preview-6.2.diff: -------------------------------------------------------------------------------- 1 | From 64c79048e03345937c66fbee01b871e44cd579bc Mon Sep 17 00:00:00 2001 2 | From: explosion-mental 3 | Date: Tue, 12 Oct 2021 11:57:54 -0500 4 | Subject: [PATCH] [update] fixed scale preview on `XCreateWindow` and added 5 | some comments Allows you to see the contents of an already viewed tag. So a 6 | more accurate description would be to re-view a tag. 7 | 8 | --- 9 | config.def.h | 1 + 10 | config.mk | 5 +- 11 | dwm.c | 164 ++++++++++++++++++++++++++++++++++++++++++++------- 12 | 3 files changed, 147 insertions(+), 23 deletions(-) 13 | 14 | diff --git a/config.def.h b/config.def.h 15 | index 1c0b587..897bf0c 100644 16 | --- a/config.def.h 17 | +++ b/config.def.h 18 | @@ -3,6 +3,7 @@ 19 | /* appearance */ 20 | static const unsigned int borderpx = 1; /* border pixel of windows */ 21 | static const unsigned int snap = 32; /* snap pixel */ 22 | +static const int scalepreview = 4; /* tag preview scaling */ 23 | static const int showbar = 1; /* 0 means no bar */ 24 | static const int topbar = 1; /* 0 means bottom bar */ 25 | static const char *fonts[] = { "monospace:size=10" }; 26 | diff --git a/config.mk b/config.mk 27 | index 6d36cb7..699007f 100644 28 | --- a/config.mk 29 | +++ b/config.mk 30 | @@ -20,9 +20,12 @@ FREETYPEINC = /usr/include/freetype2 31 | # OpenBSD (uncomment) 32 | #FREETYPEINC = ${X11INC}/freetype2 33 | 34 | +# Imlib2 (tag previews) 35 | +IMLIB2LIBS = -lImlib2 36 | + 37 | # includes and libs 38 | INCS = -I${X11INC} -I${FREETYPEINC} 39 | -LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} 40 | +LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} ${IMLIB2LIBS} 41 | 42 | # flags 43 | CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=2 -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} 44 | diff --git a/dwm.c b/dwm.c 45 | index 4465af1..878abc1 100644 46 | --- a/dwm.c 47 | +++ b/dwm.c 48 | @@ -40,6 +40,7 @@ 49 | #include 50 | #endif /* XINERAMA */ 51 | #include 52 | +#include 53 | 54 | #include "drw.h" 55 | #include "util.h" 56 | @@ -111,27 +112,6 @@ typedef struct { 57 | void (*arrange)(Monitor *); 58 | } Layout; 59 | 60 | -struct Monitor { 61 | - char ltsymbol[16]; 62 | - float mfact; 63 | - int nmaster; 64 | - int num; 65 | - int by; /* bar geometry */ 66 | - int mx, my, mw, mh; /* screen size */ 67 | - int wx, wy, ww, wh; /* window area */ 68 | - unsigned int seltags; 69 | - unsigned int sellt; 70 | - unsigned int tagset[2]; 71 | - int showbar; 72 | - int topbar; 73 | - Client *clients; 74 | - Client *sel; 75 | - Client *stack; 76 | - Monitor *next; 77 | - Window barwin; 78 | - const Layout *lt[2]; 79 | -}; 80 | - 81 | typedef struct { 82 | const char *class; 83 | const char *instance; 84 | @@ -204,8 +184,10 @@ static void setmfact(const Arg *arg); 85 | static void setup(void); 86 | static void seturgent(Client *c, int urg); 87 | static void showhide(Client *c); 88 | +static void showtagpreview(int tag); 89 | static void sigchld(int unused); 90 | static void spawn(const Arg *arg); 91 | +static void switchtag(void); 92 | static void tag(const Arg *arg); 93 | static void tagmon(const Arg *arg); 94 | static void tile(Monitor *); 95 | @@ -224,6 +206,7 @@ static void updatenumlockmask(void); 96 | static void updatesizehints(Client *c); 97 | static void updatestatus(void); 98 | static void updatetitle(Client *c); 99 | +static void updatepreview(void); 100 | static void updatewindowtype(Client *c); 101 | static void updatewmhints(Client *c); 102 | static void view(const Arg *arg); 103 | @@ -271,6 +254,36 @@ static Window root, wmcheckwin; 104 | /* configuration, allows nested code to access above variables */ 105 | #include "config.h" 106 | 107 | +/* We only move this here to get the length of the `tags` array, which probably 108 | + * will generate compatibility issues with other patches. To avoid it, I 109 | + * reccomend patching this at the end or continue with the comment below */ 110 | +struct Monitor { 111 | + char ltsymbol[16]; 112 | + float mfact; 113 | + int nmaster; 114 | + int num; 115 | + int by; /* bar geometry */ 116 | + int mx, my, mw, mh; /* screen size */ 117 | + int wx, wy, ww, wh; /* window area */ 118 | + unsigned int seltags; 119 | + unsigned int sellt; 120 | + unsigned int tagset[2]; 121 | + int previewshow; 122 | + int showbar; 123 | + int topbar; 124 | + Client *clients; 125 | + Client *sel; 126 | + Client *stack; 127 | + Monitor *next; 128 | + Window barwin; 129 | + Window tagwin; 130 | + //change 'LENGTH(tags)' to the actual number of tags you have (9 by def) 131 | + //if you wish to move this below config.h 132 | + Pixmap tagmap[LENGTH(tags)]; 133 | + const Layout *lt[2]; 134 | +}; 135 | + 136 | + 137 | /* compile-time check if all tags fit into an unsigned int bit array. */ 138 | struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; 139 | 140 | @@ -430,6 +443,10 @@ buttonpress(XEvent *e) 141 | focus(NULL); 142 | } 143 | if (ev->window == selmon->barwin) { 144 | + if (selmon->previewshow) { 145 | + XUnmapWindow(dpy, selmon->tagwin); 146 | + selmon->previewshow = 0; 147 | + } 148 | i = x = 0; 149 | do 150 | x += TEXTW(tags[i]); 151 | @@ -497,6 +514,7 @@ void 152 | cleanupmon(Monitor *mon) 153 | { 154 | Monitor *m; 155 | + size_t i; 156 | 157 | if (mon == mons) 158 | mons = mons->next; 159 | @@ -504,8 +522,13 @@ cleanupmon(Monitor *mon) 160 | for (m = mons; m && m->next != mon; m = m->next); 161 | m->next = mon->next; 162 | } 163 | + for (i = 0; i < LENGTH(tags); i++) 164 | + if (mon->tagmap[i]) 165 | + XFreePixmap(dpy, mon->tagmap[i]); 166 | XUnmapWindow(dpy, mon->barwin); 167 | XDestroyWindow(dpy, mon->barwin); 168 | + XUnmapWindow(dpy, mon->tagwin); 169 | + XDestroyWindow(dpy, mon->tagwin); 170 | free(mon); 171 | } 172 | 173 | @@ -1121,7 +1144,30 @@ motionnotify(XEvent *e) 174 | static Monitor *mon = NULL; 175 | Monitor *m; 176 | XMotionEvent *ev = &e->xmotion; 177 | + unsigned int i, x; 178 | + 179 | + if (ev->window == selmon->barwin) { 180 | + i = x = 0; 181 | + do 182 | + x += TEXTW(tags[i]); 183 | + while (ev->x >= x && ++i < LENGTH(tags)); 184 | 185 | + if (i < LENGTH(tags)) { 186 | + if ((i + 1) != selmon->previewshow && !(selmon->tagset[selmon->seltags] & 1 << i)) { 187 | + selmon->previewshow = i + 1; 188 | + showtagpreview(i); 189 | + } else if (selmon->tagset[selmon->seltags] & 1 << i) { 190 | + selmon->previewshow = 0; 191 | + showtagpreview(0); 192 | + } 193 | + } else if (selmon->previewshow != 0) { 194 | + selmon->previewshow = 0; 195 | + showtagpreview(0); 196 | + } 197 | + } else if (selmon->previewshow != 0) { 198 | + selmon->previewshow = 0; 199 | + showtagpreview(0); 200 | + } 201 | if (ev->window != root) 202 | return; 203 | if ((m = recttomon(ev->x_root, ev->y_root, 1, 1)) != mon && mon) { 204 | @@ -1573,6 +1619,7 @@ setup(void) 205 | /* init bars */ 206 | updatebars(); 207 | updatestatus(); 208 | + updatepreview(); 209 | /* supporting window for NetWMCheck */ 210 | wmcheckwin = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 0, 0, 0); 211 | XChangeProperty(dpy, wmcheckwin, netatom[NetWMCheck], XA_WINDOW, 32, 212 | @@ -1628,6 +1675,23 @@ showhide(Client *c) 213 | } 214 | } 215 | 216 | +void 217 | +showtagpreview(int tag) 218 | +{ 219 | + if (!selmon->previewshow) { 220 | + XUnmapWindow(dpy, selmon->tagwin); 221 | + return; 222 | + } 223 | + 224 | + if (selmon->tagmap[tag]) { 225 | + XSetWindowBackgroundPixmap(dpy, selmon->tagwin, selmon->tagmap[tag]); 226 | + XCopyArea(dpy, selmon->tagmap[tag], selmon->tagwin, drw->gc, 0, 0, selmon->mw / scalepreview, selmon->mh / scalepreview, 0, 0); 227 | + XSync(dpy, False); 228 | + XMapWindow(dpy, selmon->tagwin); 229 | + } else 230 | + XUnmapWindow(dpy, selmon->tagwin); 231 | +} 232 | + 233 | void 234 | sigchld(int unused) 235 | { 236 | @@ -1652,6 +1716,40 @@ spawn(const Arg *arg) 237 | } 238 | } 239 | 240 | +void 241 | +switchtag(void) 242 | +{ 243 | + int i; 244 | + unsigned int occ = 0; 245 | + Client *c; 246 | + Imlib_Image image; 247 | + 248 | + for (c = selmon->clients; c; c = c->next) 249 | + occ |= c->tags; 250 | + for (i = 0; i < LENGTH(tags); i++) { 251 | + if (selmon->tagset[selmon->seltags] & 1 << i) { 252 | + if (selmon->tagmap[i] != 0) { 253 | + XFreePixmap(dpy, selmon->tagmap[i]); 254 | + selmon->tagmap[i] = 0; 255 | + } 256 | + if (occ & 1 << i) { 257 | + image = imlib_create_image(sw, sh); 258 | + imlib_context_set_image(image); 259 | + imlib_context_set_display(dpy); 260 | + imlib_context_set_visual(DefaultVisual(dpy, screen)); 261 | + imlib_context_set_drawable(RootWindow(dpy, screen)); 262 | + //uncomment the following line and comment the other imlin_copy.. line if you don't want the bar showing on the preview 263 | + //imlib_copy_drawable_to_image(0, selmon->wx, selmon->wy, selmon->ww ,selmon->wh, 0, 0, 1); 264 | + imlib_copy_drawable_to_image(0, selmon->mx, selmon->my, selmon->mw ,selmon->mh, 0, 0, 1); 265 | + selmon->tagmap[i] = XCreatePixmap(dpy, selmon->tagwin, selmon->mw / scalepreview, selmon->mh / scalepreview, DefaultDepth(dpy, screen)); 266 | + imlib_context_set_drawable(selmon->tagmap[i]); 267 | + imlib_render_image_part_on_drawable_at_size(0, 0, selmon->mw, selmon->mh, 0, 0, selmon->mw / scalepreview, selmon->mh / scalepreview); 268 | + imlib_free_image(); 269 | + } 270 | + } 271 | + } 272 | +} 273 | + 274 | void 275 | tag(const Arg *arg) 276 | { 277 | @@ -1740,6 +1838,7 @@ toggleview(const Arg *arg) 278 | unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK); 279 | 280 | if (newtagset) { 281 | + switchtag(); 282 | selmon->tagset[selmon->seltags] = newtagset; 283 | focus(NULL); 284 | arrange(selmon); 285 | @@ -1805,7 +1904,7 @@ updatebars(void) 286 | XSetWindowAttributes wa = { 287 | .override_redirect = True, 288 | .background_pixmap = ParentRelative, 289 | - .event_mask = ButtonPressMask|ExposureMask 290 | + .event_mask = ButtonPressMask|ExposureMask|PointerMotionMask 291 | }; 292 | XClassHint ch = {"dwm", "dwm"}; 293 | for (m = mons; m; m = m->next) { 294 | @@ -2001,6 +2100,26 @@ updatetitle(Client *c) 295 | strcpy(c->name, broken); 296 | } 297 | 298 | +void 299 | +updatepreview(void) 300 | +{ 301 | + Monitor *m; 302 | + 303 | + XSetWindowAttributes wa = { 304 | + .override_redirect = True, 305 | + .background_pixmap = ParentRelative, 306 | + .event_mask = ButtonPressMask|ExposureMask 307 | + }; 308 | + for (m = mons; m; m = m->next) { 309 | + m->tagwin = XCreateWindow(dpy, root, m->wx, m->by + bh, m->mw / scalepreview, m->mh / scalepreview, 0, 310 | + DefaultDepth(dpy, screen), CopyFromParent, DefaultVisual(dpy, screen), 311 | + CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); 312 | + XDefineCursor(dpy, m->tagwin, cursor[CurNormal]->cursor); 313 | + XMapRaised(dpy, m->tagwin); 314 | + XUnmapWindow(dpy, m->tagwin); 315 | + } 316 | +} 317 | + 318 | void 319 | updatewindowtype(Client *c) 320 | { 321 | @@ -2037,6 +2156,7 @@ view(const Arg *arg) 322 | { 323 | if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) 324 | return; 325 | + switchtag(); 326 | selmon->seltags ^= 1; /* toggle sel tagset */ 327 | if (arg->ui & TAGMASK) 328 | selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; 329 | -- 330 | 2.33.0 331 | 332 | -------------------------------------------------------------------------------- /dwm.c: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. 2 | * 3 | * dynamic window manager is designed like any other X client as well. It is 4 | * driven through handling X events. In contrast to other X clients, a window 5 | * manager selects for SubstructureRedirectMask on the root window, to receive 6 | * events about window (dis-)appearance. Only one X connection at a time is 7 | * allowed to select for this event mask. 8 | * 9 | * The event handlers of dwm are organized in an array which is accessed 10 | * whenever a new event has been fetched. This allows event dispatching 11 | * in O(1) time. 12 | * 13 | * Each child of the root window is called a client, except windows which have 14 | * set the override_redirect flag. Clients are organized in a linked client 15 | * list on each monitor, the focus history is remembered through a stack list 16 | * on each monitor. Each client contains a bit array to indicate the tags of a 17 | * client. 18 | * 19 | * Keys and tagging rules are organized as arrays and defined in config.h. 20 | * 21 | * To understand everything else, start reading main(). 22 | */ 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #ifdef XINERAMA 40 | #include 41 | #endif /* XINERAMA */ 42 | #include 43 | 44 | #include "drw.h" 45 | #include "util.h" 46 | 47 | /* macros */ 48 | #define BUTTONMASK (ButtonPressMask|ButtonReleaseMask) 49 | #define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)) 50 | #define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \ 51 | * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy))) 52 | #define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags])) 53 | #define LENGTH(X) (sizeof X / sizeof X[0]) 54 | #define MOUSEMASK (BUTTONMASK|PointerMotionMask) 55 | #define WIDTH(X) ((X)->w + 2 * (X)->bw) 56 | #define HEIGHT(X) ((X)->h + 2 * (X)->bw) 57 | #define TAGMASK ((1 << LENGTH(tags)) - 1) 58 | #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) 59 | 60 | #define SYSTEM_TRAY_REQUEST_DOCK 0 61 | 62 | /* XEMBED messages */ 63 | #define XEMBED_EMBEDDED_NOTIFY 0 64 | #define XEMBED_WINDOW_ACTIVATE 1 65 | #define XEMBED_FOCUS_IN 4 66 | #define XEMBED_MODALITY_ON 10 67 | 68 | #define XEMBED_MAPPED (1 << 0) 69 | #define XEMBED_WINDOW_ACTIVATE 1 70 | #define XEMBED_WINDOW_DEACTIVATE 2 71 | 72 | #define VERSION_MAJOR 0 73 | #define VERSION_MINOR 0 74 | #define XEMBED_EMBEDDED_VERSION (VERSION_MAJOR << 16) | VERSION_MINOR 75 | 76 | /* enums */ 77 | enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ 78 | enum { SchemeNorm, SchemeSel, SchemeStatus, SchemeTagsSel, SchemeTagsNorm, SchemeInfoSel, SchemeInfoNorm }; /* color schemes */ 79 | enum { NetSupported, NetWMName, NetWMState, NetWMCheck, 80 | NetSystemTray, NetSystemTrayOP, NetSystemTrayOrientation, NetSystemTrayOrientationHorz, 81 | NetWMFullscreen, NetActiveWindow, NetWMWindowType, 82 | NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ 83 | enum { Manager, Xembed, XembedInfo, XLast }; /* Xembed atoms */ 84 | enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ 85 | enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkClientWin, 86 | ClkRootWin, ClkLast }; /* clicks */ 87 | 88 | typedef union { 89 | int i; 90 | unsigned int ui; 91 | float f; 92 | const void *v; 93 | } Arg; 94 | 95 | typedef struct { 96 | unsigned int click; 97 | unsigned int mask; 98 | unsigned int button; 99 | void (*func)(const Arg *arg); 100 | const Arg arg; 101 | } Button; 102 | 103 | typedef struct Monitor Monitor; 104 | typedef struct Client Client; 105 | struct Client { 106 | char name[256]; 107 | float mina, maxa; 108 | int x, y, w, h; 109 | int oldx, oldy, oldw, oldh; 110 | int basew, baseh, incw, inch, maxw, maxh, minw, minh; 111 | int bw, oldbw; 112 | unsigned int tags; 113 | int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen; 114 | Client *next; 115 | Client *snext; 116 | Monitor *mon; 117 | Window win; 118 | }; 119 | 120 | typedef struct { 121 | unsigned int mod; 122 | KeySym keysym; 123 | void (*func)(const Arg *); 124 | const Arg arg; 125 | } Key; 126 | 127 | typedef struct { 128 | const char *symbol; 129 | void (*arrange)(Monitor *); 130 | } Layout; 131 | 132 | struct Monitor { 133 | char ltsymbol[16]; 134 | float mfact; 135 | int nmaster; 136 | int num; 137 | int by; /* bar geometry */ 138 | int mx, my, mw, mh; /* screen size */ 139 | int wx, wy, ww, wh; /* window area */ 140 | int gappih; /* horizontal gap between windows */ 141 | int gappiv; /* vertical gap between windows */ 142 | int gappoh; /* horizontal outer gaps */ 143 | int gappov; /* vertical outer gaps */ 144 | unsigned int seltags; 145 | unsigned int sellt; 146 | unsigned int tagset[2]; 147 | int showbar; 148 | int topbar; 149 | Client *clients; 150 | Client *sel; 151 | Client *stack; 152 | Monitor *next; 153 | Window barwin; 154 | const Layout *lt[2]; 155 | }; 156 | 157 | typedef struct { 158 | const char *class; 159 | const char *instance; 160 | const char *title; 161 | unsigned int tags; 162 | int isfloating; 163 | int monitor; 164 | } Rule; 165 | 166 | typedef struct Systray Systray; 167 | struct Systray { 168 | Window win; 169 | Client *icons; 170 | }; 171 | 172 | /* function declarations */ 173 | static void applyrules(Client *c); 174 | static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact); 175 | static void arrange(Monitor *m); 176 | static void arrangemon(Monitor *m); 177 | static void attach(Client *c); 178 | static void attachstack(Client *c); 179 | static void buttonpress(XEvent *e); 180 | static void checkotherwm(void); 181 | static void cleanup(void); 182 | static void cleanupmon(Monitor *mon); 183 | static void clientmessage(XEvent *e); 184 | static void configure(Client *c); 185 | static void configurenotify(XEvent *e); 186 | static void configurerequest(XEvent *e); 187 | static Monitor *createmon(void); 188 | static void cyclelayout(const Arg *arg); 189 | static void destroynotify(XEvent *e); 190 | static void detach(Client *c); 191 | static void detachstack(Client *c); 192 | static Monitor *dirtomon(int dir); 193 | static void drawbar(Monitor *m); 194 | static void drawbars(void); 195 | static void enqueue(Client *c); 196 | static void enqueuestack(Client *c); 197 | static void enternotify(XEvent *e); 198 | static void expose(XEvent *e); 199 | static void focus(Client *c); 200 | static void focusin(XEvent *e); 201 | static void focusmon(const Arg *arg); 202 | static void focusstack(const Arg *arg); 203 | static Atom getatomprop(Client *c, Atom prop); 204 | static Atom getatomprop(Client *c, Atom prop); 205 | static int getrootptr(int *x, int *y); 206 | static long getstate(Window w); 207 | static unsigned int getsystraywidth(); 208 | static int gettextprop(Window w, Atom atom, char *text, unsigned int size); 209 | static void grabbuttons(Client *c, int focused); 210 | static void grabkeys(void); 211 | static void incnmaster(const Arg *arg); 212 | static void keypress(XEvent *e); 213 | static void killclient(const Arg *arg); 214 | static void manage(Window w, XWindowAttributes *wa); 215 | static void mappingnotify(XEvent *e); 216 | static void maprequest(XEvent *e); 217 | static void monocle(Monitor *m); 218 | static void motionnotify(XEvent *e); 219 | static void movemouse(const Arg *arg); 220 | static Client *nexttiled(Client *c); 221 | static void pop(Client *); 222 | static void propertynotify(XEvent *e); 223 | static void quit(const Arg *arg); 224 | static Monitor *recttomon(int x, int y, int w, int h); 225 | static void removesystrayicon(Client *i); 226 | static void resize(Client *c, int x, int y, int w, int h, int interact); 227 | static void resizebarwin(Monitor *m); 228 | static void resizeclient(Client *c, int x, int y, int w, int h); 229 | static void resizemouse(const Arg *arg); 230 | static void resizerequest(XEvent *e); 231 | static void restack(Monitor *m); 232 | static void rotatestack(const Arg *arg); 233 | static void run(void); 234 | static void scan(void); 235 | static int sendevent(Window w, Atom proto, int m, long d0, long d1, long d2, long d3, long d4); 236 | static void sendmon(Client *c, Monitor *m); 237 | static void setclientstate(Client *c, long state); 238 | static void setfocus(Client *c); 239 | static void setfullscreen(Client *c, int fullscreen); 240 | static void setlayout(const Arg *arg); 241 | static void setmfact(const Arg *arg); 242 | static void setup(void); 243 | static void seturgent(Client *c, int urg); 244 | static void showhide(Client *c); 245 | static void sigchld(int unused); 246 | static void spawn(const Arg *arg); 247 | static Monitor *systraytomon(Monitor *m); 248 | static void tag(const Arg *arg); 249 | static void tagmon(const Arg *arg); 250 | static void togglebar(const Arg *arg); 251 | static void togglefloating(const Arg *arg); 252 | static void toggletag(const Arg *arg); 253 | static void toggleview(const Arg *arg); 254 | static void unfocus(Client *c, int setfocus); 255 | static void unmanage(Client *c, int destroyed); 256 | static void unmapnotify(XEvent *e); 257 | static void updatebarpos(Monitor *m); 258 | static void updatebars(void); 259 | static void updateclientlist(void); 260 | static int updategeom(void); 261 | static void updatenumlockmask(void); 262 | static void updatesizehints(Client *c); 263 | static void updatestatus(void); 264 | static void updatesystray(void); 265 | static void updatesystrayicongeom(Client *i, int w, int h); 266 | static void updatesystrayiconstate(Client *i, XPropertyEvent *ev); 267 | static void updatetitle(Client *c); 268 | static void updatewindowtype(Client *c); 269 | static void updatewmhints(Client *c); 270 | static void view(const Arg *arg); 271 | static void warp(const Client *c); 272 | static Client *wintoclient(Window w); 273 | static Monitor *wintomon(Window w); 274 | static Client *wintosystrayicon(Window w); 275 | static int xerror(Display *dpy, XErrorEvent *ee); 276 | static int xerrordummy(Display *dpy, XErrorEvent *ee); 277 | static int xerrorstart(Display *dpy, XErrorEvent *ee); 278 | static void zoom(const Arg *arg); 279 | 280 | /* variables */ 281 | static Systray *systray = NULL; 282 | static const char broken[] = "broken"; 283 | static char stext[256]; 284 | static int screen; 285 | static int sw, sh; /* X display screen geometry width, height */ 286 | static int bh, blw = 0; /* bar geometry */ 287 | static int lrpad; /* sum of left and right padding for text */ 288 | static int (*xerrorxlib)(Display *, XErrorEvent *); 289 | static unsigned int numlockmask = 0; 290 | static void (*handler[LASTEvent]) (XEvent *) = { 291 | [ButtonPress] = buttonpress, 292 | [ClientMessage] = clientmessage, 293 | [ConfigureRequest] = configurerequest, 294 | [ConfigureNotify] = configurenotify, 295 | [DestroyNotify] = destroynotify, 296 | [EnterNotify] = enternotify, 297 | [Expose] = expose, 298 | [FocusIn] = focusin, 299 | [KeyPress] = keypress, 300 | [MappingNotify] = mappingnotify, 301 | [MapRequest] = maprequest, 302 | [MotionNotify] = motionnotify, 303 | [PropertyNotify] = propertynotify, 304 | [ResizeRequest] = resizerequest, 305 | [UnmapNotify] = unmapnotify 306 | }; 307 | static Atom wmatom[WMLast], netatom[NetLast], xatom[XLast]; 308 | static int running = 1; 309 | static Cur *cursor[CurLast]; 310 | static Clr **scheme; 311 | static Display *dpy; 312 | static Drw *drw; 313 | static Monitor *mons, *selmon, *statmon; 314 | static Window root, wmcheckwin; 315 | 316 | /* configuration, allows nested code to access above variables */ 317 | #include "config.h" 318 | 319 | /* compile-time check if all tags fit into an unsigned int bit array. */ 320 | struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; 321 | 322 | /* function implementations */ 323 | void 324 | applyrules(Client *c) 325 | { 326 | const char *class, *instance; 327 | unsigned int i; 328 | const Rule *r; 329 | Monitor *m; 330 | XClassHint ch = { NULL, NULL }; 331 | 332 | /* rule matching */ 333 | c->isfloating = 0; 334 | c->tags = 0; 335 | XGetClassHint(dpy, c->win, &ch); 336 | class = ch.res_class ? ch.res_class : broken; 337 | instance = ch.res_name ? ch.res_name : broken; 338 | 339 | for (i = 0; i < LENGTH(rules); i++) { 340 | r = &rules[i]; 341 | if ((!r->title || strstr(c->name, r->title)) 342 | && (!r->class || strstr(class, r->class)) 343 | && (!r->instance || strstr(instance, r->instance))) 344 | { 345 | c->isfloating = r->isfloating; 346 | c->tags |= r->tags; 347 | for (m = mons; m && m->num != r->monitor; m = m->next); 348 | if (m) 349 | c->mon = m; 350 | } 351 | } 352 | if (ch.res_class) 353 | XFree(ch.res_class); 354 | if (ch.res_name) 355 | XFree(ch.res_name); 356 | c->tags = c->tags & TAGMASK ? c->tags & TAGMASK : c->mon->tagset[c->mon->seltags]; 357 | } 358 | 359 | int 360 | applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact) 361 | { 362 | int baseismin; 363 | Monitor *m = c->mon; 364 | 365 | /* set minimum possible */ 366 | *w = MAX(1, *w); 367 | *h = MAX(1, *h); 368 | if (interact) { 369 | if (*x > sw) 370 | *x = sw - WIDTH(c); 371 | if (*y > sh) 372 | *y = sh - HEIGHT(c); 373 | if (*x + *w + 2 * c->bw < 0) 374 | *x = 0; 375 | if (*y + *h + 2 * c->bw < 0) 376 | *y = 0; 377 | } else { 378 | if (*x >= m->wx + m->ww) 379 | *x = m->wx + m->ww - WIDTH(c); 380 | if (*y >= m->wy + m->wh) 381 | *y = m->wy + m->wh - HEIGHT(c); 382 | if (*x + *w + 2 * c->bw <= m->wx) 383 | *x = m->wx; 384 | if (*y + *h + 2 * c->bw <= m->wy) 385 | *y = m->wy; 386 | } 387 | if (*h < bh) 388 | *h = bh; 389 | if (*w < bh) 390 | *w = bh; 391 | if (resizehints || c->isfloating || !c->mon->lt[c->mon->sellt]->arrange) { 392 | /* see last two sentences in ICCCM 4.1.2.3 */ 393 | baseismin = c->basew == c->minw && c->baseh == c->minh; 394 | if (!baseismin) { /* temporarily remove base dimensions */ 395 | *w -= c->basew; 396 | *h -= c->baseh; 397 | } 398 | /* adjust for aspect limits */ 399 | if (c->mina > 0 && c->maxa > 0) { 400 | if (c->maxa < (float)*w / *h) 401 | *w = *h * c->maxa + 0.5; 402 | else if (c->mina < (float)*h / *w) 403 | *h = *w * c->mina + 0.5; 404 | } 405 | if (baseismin) { /* increment calculation requires this */ 406 | *w -= c->basew; 407 | *h -= c->baseh; 408 | } 409 | /* adjust for increment value */ 410 | if (c->incw) 411 | *w -= *w % c->incw; 412 | if (c->inch) 413 | *h -= *h % c->inch; 414 | /* restore base dimensions */ 415 | *w = MAX(*w + c->basew, c->minw); 416 | *h = MAX(*h + c->baseh, c->minh); 417 | if (c->maxw) 418 | *w = MIN(*w, c->maxw); 419 | if (c->maxh) 420 | *h = MIN(*h, c->maxh); 421 | } 422 | return *x != c->x || *y != c->y || *w != c->w || *h != c->h; 423 | } 424 | 425 | void 426 | arrange(Monitor *m) 427 | { 428 | if (m) 429 | showhide(m->stack); 430 | else for (m = mons; m; m = m->next) 431 | showhide(m->stack); 432 | if (m) { 433 | arrangemon(m); 434 | restack(m); 435 | } else for (m = mons; m; m = m->next) 436 | arrangemon(m); 437 | } 438 | 439 | void 440 | arrangemon(Monitor *m) 441 | { 442 | strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol); 443 | if (m->lt[m->sellt]->arrange) 444 | m->lt[m->sellt]->arrange(m); 445 | } 446 | 447 | void 448 | attach(Client *c) 449 | { 450 | c->next = c->mon->clients; 451 | c->mon->clients = c; 452 | } 453 | 454 | void 455 | attachstack(Client *c) 456 | { 457 | c->snext = c->mon->stack; 458 | c->mon->stack = c; 459 | } 460 | 461 | void 462 | buttonpress(XEvent *e) 463 | { 464 | unsigned int i, x, click; 465 | Arg arg = {0}; 466 | Client *c; 467 | Monitor *m; 468 | XButtonPressedEvent *ev = &e->xbutton; 469 | 470 | click = ClkRootWin; 471 | /* focus monitor if necessary */ 472 | if ((m = wintomon(ev->window)) && m != selmon) { 473 | unfocus(selmon->sel, 1); 474 | selmon = m; 475 | focus(NULL); 476 | } 477 | if (ev->window == selmon->barwin) { 478 | i = x = 0; 479 | unsigned int occ = 0; 480 | for(c = m->clients; c; c = c->next) 481 | occ |= c->tags; 482 | do { 483 | /* do not reserve space for vacant tags */ 484 | if(!(occ & 1 << i || m->tagset[m->seltags] & 1 << i)) 485 | continue; 486 | x += TEXTW(tags[i]); 487 | } while (ev->x >= x && ++i < LENGTH(tags)); 488 | if (i < LENGTH(tags)) { 489 | click = ClkTagBar; 490 | arg.ui = 1 << i; 491 | } else if (ev->x < x + blw) 492 | click = ClkLtSymbol; 493 | else if (ev->x > selmon->ww - TEXTW(stext) - getsystraywidth()) 494 | click = ClkStatusText; 495 | else 496 | click = ClkStatusText; 497 | } else if ((c = wintoclient(ev->window))) { 498 | focus(c); 499 | restack(selmon); 500 | XAllowEvents(dpy, ReplayPointer, CurrentTime); 501 | click = ClkClientWin; 502 | } 503 | for (i = 0; i < LENGTH(buttons); i++) 504 | if (click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button 505 | && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) 506 | buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg); 507 | } 508 | 509 | void 510 | checkotherwm(void) 511 | { 512 | xerrorxlib = XSetErrorHandler(xerrorstart); 513 | /* this causes an error if some other window manager is running */ 514 | XSelectInput(dpy, DefaultRootWindow(dpy), SubstructureRedirectMask); 515 | XSync(dpy, False); 516 | XSetErrorHandler(xerror); 517 | XSync(dpy, False); 518 | } 519 | 520 | void 521 | cleanup(void) 522 | { 523 | Arg a = {.ui = ~0}; 524 | Layout foo = { "", NULL }; 525 | Monitor *m; 526 | size_t i; 527 | 528 | view(&a); 529 | selmon->lt[selmon->sellt] = &foo; 530 | for (m = mons; m; m = m->next) 531 | while (m->stack) 532 | unmanage(m->stack, 0); 533 | XUngrabKey(dpy, AnyKey, AnyModifier, root); 534 | while (mons) 535 | cleanupmon(mons); 536 | if (showsystray) { 537 | XUnmapWindow(dpy, systray->win); 538 | XDestroyWindow(dpy, systray->win); 539 | free(systray); 540 | } 541 | for (i = 0; i < CurLast; i++) 542 | drw_cur_free(drw, cursor[i]); 543 | for (i = 0; i < LENGTH(colors); i++) 544 | free(scheme[i]); 545 | XDestroyWindow(dpy, wmcheckwin); 546 | drw_free(drw); 547 | XSync(dpy, False); 548 | XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); 549 | XDeleteProperty(dpy, root, netatom[NetActiveWindow]); 550 | } 551 | 552 | void 553 | cleanupmon(Monitor *mon) 554 | { 555 | Monitor *m; 556 | 557 | if (mon == mons) 558 | mons = mons->next; 559 | else { 560 | for (m = mons; m && m->next != mon; m = m->next); 561 | m->next = mon->next; 562 | } 563 | XUnmapWindow(dpy, mon->barwin); 564 | XDestroyWindow(dpy, mon->barwin); 565 | free(mon); 566 | } 567 | 568 | void 569 | clientmessage(XEvent *e) 570 | { 571 | XWindowAttributes wa; 572 | XSetWindowAttributes swa; 573 | XClientMessageEvent *cme = &e->xclient; 574 | Client *c = wintoclient(cme->window); 575 | unsigned int i; 576 | 577 | if (showsystray && cme->window == systray->win && cme->message_type == netatom[NetSystemTrayOP]) { 578 | /* add systray icons */ 579 | if (cme->data.l[1] == SYSTEM_TRAY_REQUEST_DOCK) { 580 | if (!(c = (Client *)calloc(1, sizeof(Client)))) 581 | die("fatal: could not malloc() %u bytes\n", sizeof(Client)); 582 | if (!(c->win = cme->data.l[2])) { 583 | free(c); 584 | return; 585 | } 586 | c->mon = selmon; 587 | c->next = systray->icons; 588 | systray->icons = c; 589 | if (!XGetWindowAttributes(dpy, c->win, &wa)) { 590 | /* use sane defaults */ 591 | wa.width = bh; 592 | wa.height = bh; 593 | wa.border_width = 0; 594 | } 595 | c->x = c->oldx = c->y = c->oldy = 0; 596 | c->w = c->oldw = wa.width; 597 | c->h = c->oldh = wa.height; 598 | c->oldbw = wa.border_width; 599 | c->bw = 0; 600 | c->isfloating = True; 601 | /* reuse tags field as mapped status */ 602 | c->tags = 1; 603 | updatesizehints(c); 604 | updatesystrayicongeom(c, wa.width, wa.height); 605 | XAddToSaveSet(dpy, c->win); 606 | XSelectInput(dpy, c->win, StructureNotifyMask | PropertyChangeMask | ResizeRedirectMask); 607 | 608 | // TODO added these two lines to give systray a class name to control through picom 609 | XClassHint ch = {"dwmsystray", "dwmsystray"}; 610 | XSetClassHint(dpy, c->win, &ch); 611 | 612 | XReparentWindow(dpy, c->win, systray->win, 0, 0); 613 | /* use parents background color */ 614 | swa.background_pixel = scheme[SchemeNorm][ColBg].pixel; 615 | XChangeWindowAttributes(dpy, c->win, CWBackPixel, &swa); 616 | sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_EMBEDDED_NOTIFY, 0 , systray->win, XEMBED_EMBEDDED_VERSION); 617 | /* FIXME not sure if I have to send these events, too */ 618 | sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_FOCUS_IN, 0 , systray->win, XEMBED_EMBEDDED_VERSION); 619 | sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0 , systray->win, XEMBED_EMBEDDED_VERSION); 620 | sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_MODALITY_ON, 0 , systray->win, XEMBED_EMBEDDED_VERSION); 621 | XSync(dpy, False); 622 | resizebarwin(selmon); 623 | updatesystray(); 624 | setclientstate(c, NormalState); 625 | } 626 | return; 627 | } 628 | if (!c) 629 | return; 630 | if (cme->message_type == netatom[NetWMState]) { 631 | if (cme->data.l[1] == netatom[NetWMFullscreen] 632 | || cme->data.l[2] == netatom[NetWMFullscreen]) 633 | setfullscreen(c, (cme->data.l[0] == 1 /* _NET_WM_STATE_ADD */ 634 | || (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */ && !c->isfullscreen))); 635 | } else if (cme->message_type == netatom[NetActiveWindow]) { 636 | for (i = 0; i < LENGTH(tags) && !((1 << i) & c->tags); i++); 637 | if (i < LENGTH(tags)) { 638 | const Arg a = {.ui = 1 << i}; 639 | selmon = c->mon; 640 | view(&a); 641 | focus(c); 642 | restack(selmon); 643 | } 644 | } 645 | } 646 | 647 | void 648 | configure(Client *c) 649 | { 650 | XConfigureEvent ce; 651 | 652 | ce.type = ConfigureNotify; 653 | ce.display = dpy; 654 | ce.event = c->win; 655 | ce.window = c->win; 656 | ce.x = c->x; 657 | ce.y = c->y; 658 | ce.width = c->w; 659 | ce.height = c->h; 660 | ce.border_width = c->bw; 661 | ce.above = None; 662 | ce.override_redirect = False; 663 | XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce); 664 | } 665 | 666 | void 667 | configurenotify(XEvent *e) 668 | { 669 | Monitor *m; 670 | Client *c; 671 | XConfigureEvent *ev = &e->xconfigure; 672 | int dirty; 673 | 674 | /* TODO: updategeom handling sucks, needs to be simplified */ 675 | if (ev->window == root) { 676 | dirty = (sw != ev->width || sh != ev->height); 677 | sw = ev->width; 678 | sh = ev->height; 679 | if (updategeom() || dirty) { 680 | drw_resize(drw, sw, bh); 681 | updatebars(); 682 | for (m = mons; m; m = m->next) { 683 | for (c = m->clients; c; c = c->next) 684 | if (c->isfullscreen) 685 | resizeclient(c, m->mx, m->my, m->mw, m->mh); 686 | resizebarwin(m); 687 | } 688 | focus(NULL); 689 | arrange(NULL); 690 | } 691 | } 692 | } 693 | 694 | void 695 | configurerequest(XEvent *e) 696 | { 697 | Client *c; 698 | Monitor *m; 699 | XConfigureRequestEvent *ev = &e->xconfigurerequest; 700 | XWindowChanges wc; 701 | 702 | if ((c = wintoclient(ev->window))) { 703 | if (ev->value_mask & CWBorderWidth) 704 | c->bw = ev->border_width; 705 | else if (c->isfloating || !selmon->lt[selmon->sellt]->arrange) { 706 | m = c->mon; 707 | if (ev->value_mask & CWX) { 708 | c->oldx = c->x; 709 | c->x = m->mx + ev->x; 710 | } 711 | if (ev->value_mask & CWY) { 712 | c->oldy = c->y; 713 | c->y = m->my + ev->y; 714 | } 715 | if (ev->value_mask & CWWidth) { 716 | c->oldw = c->w; 717 | c->w = ev->width; 718 | } 719 | if (ev->value_mask & CWHeight) { 720 | c->oldh = c->h; 721 | c->h = ev->height; 722 | } 723 | if ((c->x + c->w) > m->mx + m->mw && c->isfloating) 724 | c->x = m->mx + (m->mw / 2 - WIDTH(c) / 2); /* center in x direction */ 725 | if ((c->y + c->h) > m->my + m->mh && c->isfloating) 726 | c->y = m->my + (m->mh / 2 - HEIGHT(c) / 2); /* center in y direction */ 727 | if ((ev->value_mask & (CWX|CWY)) && !(ev->value_mask & (CWWidth|CWHeight))) 728 | configure(c); 729 | if (ISVISIBLE(c)) 730 | XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); 731 | } else 732 | configure(c); 733 | } else { 734 | wc.x = ev->x; 735 | wc.y = ev->y; 736 | wc.width = ev->width; 737 | wc.height = ev->height; 738 | wc.border_width = ev->border_width; 739 | wc.sibling = ev->above; 740 | wc.stack_mode = ev->detail; 741 | XConfigureWindow(dpy, ev->window, ev->value_mask, &wc); 742 | } 743 | XSync(dpy, False); 744 | } 745 | 746 | Monitor * 747 | createmon(void) 748 | { 749 | Monitor *m; 750 | 751 | m = ecalloc(1, sizeof(Monitor)); 752 | m->tagset[0] = m->tagset[1] = 1; 753 | m->mfact = mfact; 754 | m->nmaster = nmaster; 755 | m->showbar = showbar; 756 | m->topbar = topbar; 757 | m->gappih = gappih; 758 | m->gappiv = gappiv; 759 | m->gappoh = gappoh; 760 | m->gappov = gappov; 761 | m->lt[0] = &layouts[0]; 762 | m->lt[1] = &layouts[1 % LENGTH(layouts)]; 763 | strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); 764 | return m; 765 | } 766 | 767 | void 768 | cyclelayout(const Arg *arg) { 769 | Layout *l; 770 | for(l = (Layout *)layouts; l != selmon->lt[selmon->sellt]; l++); 771 | if(arg->i > 0) { 772 | if(l->symbol && (l + 1)->symbol) 773 | setlayout(&((Arg) { .v = (l + 1) })); 774 | else 775 | setlayout(&((Arg) { .v = layouts })); 776 | } else { 777 | if(l != layouts && (l - 1)->symbol) 778 | setlayout(&((Arg) { .v = (l - 1) })); 779 | else 780 | setlayout(&((Arg) { .v = &layouts[LENGTH(layouts) - 2] })); 781 | } 782 | } 783 | 784 | void 785 | destroynotify(XEvent *e) 786 | { 787 | Client *c; 788 | XDestroyWindowEvent *ev = &e->xdestroywindow; 789 | 790 | if ((c = wintoclient(ev->window))) 791 | unmanage(c, 1); 792 | else if ((c = wintosystrayicon(ev->window))) { 793 | removesystrayicon(c); 794 | resizebarwin(selmon); 795 | updatesystray(); 796 | } 797 | } 798 | 799 | void 800 | detach(Client *c) 801 | { 802 | Client **tc; 803 | 804 | for (tc = &c->mon->clients; *tc && *tc != c; tc = &(*tc)->next); 805 | *tc = c->next; 806 | } 807 | 808 | void 809 | detachstack(Client *c) 810 | { 811 | Client **tc, *t; 812 | 813 | for (tc = &c->mon->stack; *tc && *tc != c; tc = &(*tc)->snext); 814 | *tc = c->snext; 815 | 816 | if (c == c->mon->sel) { 817 | for (t = c->mon->stack; t && !ISVISIBLE(t); t = t->snext); 818 | c->mon->sel = t; 819 | } 820 | } 821 | 822 | Monitor * 823 | dirtomon(int dir) 824 | { 825 | Monitor *m = NULL; 826 | 827 | if (dir > 0) { 828 | if (!(m = selmon->next)) 829 | m = mons; 830 | } else if (selmon == mons) 831 | for (m = mons; m->next; m = m->next); 832 | else 833 | for (m = mons; m->next != selmon; m = m->next); 834 | return m; 835 | } 836 | 837 | void 838 | drawbar(Monitor *m) 839 | { 840 | int x, w, sw = 0, stw = 0; 841 | int boxs = drw->fonts->h / 9; 842 | int boxw = drw->fonts->h / 6 + 2; 843 | unsigned int i, occ = 0, urg = 0; 844 | Client *c; 845 | 846 | 847 | if(showsystray && m == systraytomon(m)) 848 | stw = getsystraywidth(); 849 | 850 | /* draw status first so it can be overdrawn by tags later */ 851 | if (m == statmon) { /* status is only drawn on user-defined status monitor */ 852 | drw_setscheme(drw, scheme[SchemeStatus]); 853 | sw = TEXTW(stext) - lrpad / 2 + 2; /* 2px right padding */ 854 | drw_text(drw, m->ww - sw - stw, 0, sw, bh, lrpad / 2 - 2, stext, 0); 855 | } 856 | 857 | resizebarwin(m); 858 | for (c = m->clients; c; c = c->next) { 859 | occ |= c->tags; 860 | if (c->isurgent) 861 | urg |= c->tags; 862 | } 863 | x = 0; 864 | for (i = 0; i < LENGTH(tags); i++) { 865 | /* do not draw vacant tags */ 866 | if(!(occ & 1 << i || m->tagset[m->seltags] & 1 << i)) 867 | continue; 868 | w = TEXTW(tags[i]); 869 | drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeTagsSel : SchemeTagsNorm]); 870 | drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i); 871 | if (occ & 1 << i) 872 | drw_rect(drw, x + boxs, boxs, boxw, boxw, 873 | m == selmon && selmon->sel && selmon->sel->tags & 1 << i, 874 | urg & 1 << i); 875 | x += w; 876 | } 877 | w = blw = TEXTW(m->ltsymbol); 878 | drw_setscheme(drw, scheme[SchemeTagsNorm]); 879 | x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); 880 | 881 | if ((w = m->ww - sw - stw - x) > bh) { 882 | /* if (m->sel) { */ 883 | /* drw_setscheme(drw, scheme[m == selmon ? SchemeInfoSel : SchemeInfoNorm]); */ 884 | /* drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); */ 885 | /* if (m->sel->isfloating) */ 886 | /* drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0); */ 887 | /* } else { */ 888 | drw_setscheme(drw, scheme[SchemeInfoNorm]); 889 | drw_rect(drw, x, 0, w, bh, 1, 1); 890 | /* } */ 891 | } 892 | drw_map(drw, m->barwin, 0, 0, m->ww - stw, bh); 893 | } 894 | 895 | void 896 | drawbars(void) 897 | { 898 | Monitor *m; 899 | 900 | for (m = mons; m; m = m->next) 901 | drawbar(m); 902 | } 903 | 904 | void 905 | enqueue(Client *c) 906 | { 907 | Client *l; 908 | for (l = c->mon->clients; l && l->next; l = l->next); 909 | if (l) { 910 | l->next = c; 911 | c->next = NULL; 912 | } 913 | } 914 | 915 | void 916 | enqueuestack(Client *c) 917 | { 918 | Client *l; 919 | for (l = c->mon->stack; l && l->snext; l = l->snext); 920 | if (l) { 921 | l->snext = c; 922 | c->snext = NULL; 923 | } 924 | } 925 | 926 | void 927 | enternotify(XEvent *e) 928 | { 929 | Client *c; 930 | Monitor *m; 931 | XCrossingEvent *ev = &e->xcrossing; 932 | 933 | if ((ev->mode != NotifyNormal || ev->detail == NotifyInferior) && ev->window != root) 934 | return; 935 | c = wintoclient(ev->window); 936 | m = c ? c->mon : wintomon(ev->window); 937 | if (m != selmon) { 938 | unfocus(selmon->sel, 1); 939 | selmon = m; 940 | } else if (!c || c == selmon->sel) 941 | return; 942 | focus(c); 943 | } 944 | 945 | void 946 | expose(XEvent *e) 947 | { 948 | Monitor *m; 949 | XExposeEvent *ev = &e->xexpose; 950 | 951 | if (ev->count == 0 && (m = wintomon(ev->window))) { 952 | drawbar(m); 953 | if (m == selmon) 954 | updatesystray(); 955 | } 956 | } 957 | 958 | void 959 | focus(Client *c) 960 | { 961 | if (!c || !ISVISIBLE(c)) 962 | for (c = selmon->stack; c && !ISVISIBLE(c); c = c->snext); 963 | if (selmon->sel && selmon->sel != c) 964 | unfocus(selmon->sel, 0); 965 | if (c) { 966 | if (c->mon != selmon) 967 | selmon = c->mon; 968 | if (c->isurgent) 969 | seturgent(c, 0); 970 | detachstack(c); 971 | attachstack(c); 972 | grabbuttons(c, 1); 973 | XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel); 974 | setfocus(c); 975 | } else { 976 | XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); 977 | XDeleteProperty(dpy, root, netatom[NetActiveWindow]); 978 | } 979 | selmon->sel = c; 980 | drawbars(); 981 | } 982 | 983 | /* there are some broken focus acquiring clients needing extra handling */ 984 | void 985 | focusin(XEvent *e) 986 | { 987 | XFocusChangeEvent *ev = &e->xfocus; 988 | 989 | if (selmon->sel && ev->window != selmon->sel->win) 990 | setfocus(selmon->sel); 991 | } 992 | 993 | void 994 | focusmon(const Arg *arg) 995 | { 996 | Monitor *m; 997 | 998 | if (!mons->next) 999 | return; 1000 | if ((m = dirtomon(arg->i)) == selmon) 1001 | return; 1002 | unfocus(selmon->sel, 0); 1003 | selmon = m; 1004 | focus(NULL); 1005 | warp(selmon->sel); 1006 | } 1007 | 1008 | void 1009 | focusstack(const Arg *arg) 1010 | { 1011 | Client *c = NULL, *i; 1012 | 1013 | if (!selmon->sel || selmon->sel->isfullscreen) 1014 | return; 1015 | if (arg->i > 0) { 1016 | for (c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next); 1017 | if (!c) 1018 | for (c = selmon->clients; c && !ISVISIBLE(c); c = c->next); 1019 | } else { 1020 | for (i = selmon->clients; i != selmon->sel; i = i->next) 1021 | if (ISVISIBLE(i)) 1022 | c = i; 1023 | if (!c) 1024 | for (; i; i = i->next) 1025 | if (ISVISIBLE(i)) 1026 | c = i; 1027 | } 1028 | if (c) { 1029 | focus(c); 1030 | restack(selmon); 1031 | } 1032 | } 1033 | 1034 | Atom 1035 | getatomprop(Client *c, Atom prop) 1036 | { 1037 | int di; 1038 | unsigned long dl; 1039 | unsigned char *p = NULL; 1040 | Atom da, atom = None; 1041 | /* FIXME getatomprop should return the number of items and a pointer to 1042 | * the stored data instead of this workaround */ 1043 | Atom req = XA_ATOM; 1044 | if (prop == xatom[XembedInfo]) 1045 | req = xatom[XembedInfo]; 1046 | 1047 | if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, req, 1048 | &da, &di, &dl, &dl, &p) == Success && p) { 1049 | atom = *(Atom *)p; 1050 | if (da == xatom[XembedInfo] && dl == 2) 1051 | atom = ((Atom *)p)[1]; 1052 | XFree(p); 1053 | } 1054 | return atom; 1055 | } 1056 | 1057 | int 1058 | getrootptr(int *x, int *y) 1059 | { 1060 | int di; 1061 | unsigned int dui; 1062 | Window dummy; 1063 | 1064 | return XQueryPointer(dpy, root, &dummy, &dummy, x, y, &di, &di, &dui); 1065 | } 1066 | 1067 | long 1068 | getstate(Window w) 1069 | { 1070 | int format; 1071 | long result = -1; 1072 | unsigned char *p = NULL; 1073 | unsigned long n, extra; 1074 | Atom real; 1075 | 1076 | if (XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState], 1077 | &real, &format, &n, &extra, (unsigned char **)&p) != Success) 1078 | return -1; 1079 | if (n != 0) 1080 | result = *p; 1081 | XFree(p); 1082 | return result; 1083 | } 1084 | 1085 | unsigned int 1086 | getsystraywidth() 1087 | { 1088 | unsigned int w = 0; 1089 | Client *i; 1090 | if(showsystray) 1091 | for(i = systray->icons; i; w += i->w + systrayspacing, i = i->next) ; 1092 | return w ? w + systrayspacing : 1; 1093 | } 1094 | 1095 | int 1096 | gettextprop(Window w, Atom atom, char *text, unsigned int size) 1097 | { 1098 | char **list = NULL; 1099 | int n; 1100 | XTextProperty name; 1101 | 1102 | if (!text || size == 0) 1103 | return 0; 1104 | text[0] = '\0'; 1105 | if (!XGetTextProperty(dpy, w, &name, atom) || !name.nitems) 1106 | return 0; 1107 | if (name.encoding == XA_STRING) 1108 | strncpy(text, (char *)name.value, size - 1); 1109 | else { 1110 | if (XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success && n > 0 && *list) { 1111 | strncpy(text, *list, size - 1); 1112 | XFreeStringList(list); 1113 | } 1114 | } 1115 | text[size - 1] = '\0'; 1116 | XFree(name.value); 1117 | return 1; 1118 | } 1119 | 1120 | void 1121 | grabbuttons(Client *c, int focused) 1122 | { 1123 | updatenumlockmask(); 1124 | { 1125 | unsigned int i, j; 1126 | unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; 1127 | XUngrabButton(dpy, AnyButton, AnyModifier, c->win); 1128 | if (!focused) 1129 | XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, 1130 | BUTTONMASK, GrabModeSync, GrabModeSync, None, None); 1131 | for (i = 0; i < LENGTH(buttons); i++) 1132 | if (buttons[i].click == ClkClientWin) 1133 | for (j = 0; j < LENGTH(modifiers); j++) 1134 | XGrabButton(dpy, buttons[i].button, 1135 | buttons[i].mask | modifiers[j], 1136 | c->win, False, BUTTONMASK, 1137 | GrabModeAsync, GrabModeSync, None, None); 1138 | } 1139 | } 1140 | 1141 | void 1142 | grabkeys(void) 1143 | { 1144 | updatenumlockmask(); 1145 | { 1146 | unsigned int i, j; 1147 | unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; 1148 | KeyCode code; 1149 | 1150 | XUngrabKey(dpy, AnyKey, AnyModifier, root); 1151 | for (i = 0; i < LENGTH(keys); i++) 1152 | if ((code = XKeysymToKeycode(dpy, keys[i].keysym))) 1153 | for (j = 0; j < LENGTH(modifiers); j++) 1154 | XGrabKey(dpy, code, keys[i].mod | modifiers[j], root, 1155 | True, GrabModeAsync, GrabModeAsync); 1156 | } 1157 | } 1158 | 1159 | void 1160 | incnmaster(const Arg *arg) 1161 | { 1162 | selmon->nmaster = MAX(selmon->nmaster + arg->i, 0); 1163 | arrange(selmon); 1164 | } 1165 | 1166 | #ifdef XINERAMA 1167 | static int 1168 | isuniquegeom(XineramaScreenInfo *unique, size_t n, XineramaScreenInfo *info) 1169 | { 1170 | while (n--) 1171 | if (unique[n].x_org == info->x_org && unique[n].y_org == info->y_org 1172 | && unique[n].width == info->width && unique[n].height == info->height) 1173 | return 0; 1174 | return 1; 1175 | } 1176 | #endif /* XINERAMA */ 1177 | 1178 | void 1179 | keypress(XEvent *e) 1180 | { 1181 | unsigned int i; 1182 | KeySym keysym; 1183 | XKeyEvent *ev; 1184 | 1185 | ev = &e->xkey; 1186 | keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0); 1187 | for (i = 0; i < LENGTH(keys); i++) 1188 | if (keysym == keys[i].keysym 1189 | && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state) 1190 | && keys[i].func) 1191 | keys[i].func(&(keys[i].arg)); 1192 | } 1193 | 1194 | void 1195 | killclient(const Arg *arg) 1196 | { 1197 | if (!selmon->sel) 1198 | return; 1199 | if (!sendevent(selmon->sel->win, wmatom[WMDelete], NoEventMask, wmatom[WMDelete], CurrentTime, 0 , 0, 0)) { 1200 | XGrabServer(dpy); 1201 | XSetErrorHandler(xerrordummy); 1202 | XSetCloseDownMode(dpy, DestroyAll); 1203 | XKillClient(dpy, selmon->sel->win); 1204 | XSync(dpy, False); 1205 | XSetErrorHandler(xerror); 1206 | XUngrabServer(dpy); 1207 | } 1208 | } 1209 | 1210 | void 1211 | manage(Window w, XWindowAttributes *wa) 1212 | { 1213 | Client *c, *t = NULL; 1214 | Window trans = None; 1215 | XWindowChanges wc; 1216 | 1217 | c = ecalloc(1, sizeof(Client)); 1218 | c->win = w; 1219 | /* geometry */ 1220 | c->x = c->oldx = wa->x; 1221 | c->y = c->oldy = wa->y; 1222 | c->w = c->oldw = wa->width; 1223 | c->h = c->oldh = wa->height; 1224 | c->oldbw = wa->border_width; 1225 | 1226 | updatetitle(c); 1227 | if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) { 1228 | c->mon = t->mon; 1229 | c->tags = t->tags; 1230 | } else { 1231 | c->mon = selmon; 1232 | applyrules(c); 1233 | } 1234 | 1235 | if (c->x + WIDTH(c) > c->mon->mx + c->mon->mw) 1236 | c->x = c->mon->mx + c->mon->mw - WIDTH(c); 1237 | if (c->y + HEIGHT(c) > c->mon->my + c->mon->mh) 1238 | c->y = c->mon->my + c->mon->mh - HEIGHT(c); 1239 | c->x = MAX(c->x, c->mon->mx); 1240 | /* only fix client y-offset, if the client center might cover the bar */ 1241 | c->y = MAX(c->y, ((c->mon->by == c->mon->my) && (c->x + (c->w / 2) >= c->mon->wx) 1242 | && (c->x + (c->w / 2) < c->mon->wx + c->mon->ww)) ? bh : c->mon->my); 1243 | c->bw = borderpx; 1244 | 1245 | wc.border_width = c->bw; 1246 | XConfigureWindow(dpy, w, CWBorderWidth, &wc); 1247 | XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel); 1248 | configure(c); /* propagates border_width, if size doesn't change */ 1249 | updatewindowtype(c); 1250 | updatesizehints(c); 1251 | updatewmhints(c); 1252 | c->x = c->mon->mx + (c->mon->mw - WIDTH(c)) / 2; 1253 | c->y = c->mon->my + (c->mon->mh - HEIGHT(c)) / 2; 1254 | XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask); 1255 | grabbuttons(c, 0); 1256 | if (!c->isfloating) 1257 | c->isfloating = c->oldstate = trans != None || c->isfixed; 1258 | if (c->isfloating) 1259 | XRaiseWindow(dpy, c->win); 1260 | attach(c); 1261 | attachstack(c); 1262 | XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, 1263 | (unsigned char *) &(c->win), 1); 1264 | XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */ 1265 | setclientstate(c, NormalState); 1266 | if (c->mon == selmon) 1267 | unfocus(selmon->sel, 0); 1268 | c->mon->sel = c; 1269 | arrange(c->mon); 1270 | XMapWindow(dpy, c->win); 1271 | focus(NULL); 1272 | } 1273 | 1274 | void 1275 | mappingnotify(XEvent *e) 1276 | { 1277 | XMappingEvent *ev = &e->xmapping; 1278 | 1279 | XRefreshKeyboardMapping(ev); 1280 | if (ev->request == MappingKeyboard) 1281 | grabkeys(); 1282 | } 1283 | 1284 | void 1285 | maprequest(XEvent *e) 1286 | { 1287 | static XWindowAttributes wa; 1288 | XMapRequestEvent *ev = &e->xmaprequest; 1289 | Client *i; 1290 | if ((i = wintosystrayicon(ev->window))) { 1291 | sendevent(i->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0, systray->win, XEMBED_EMBEDDED_VERSION); 1292 | resizebarwin(selmon); 1293 | updatesystray(); 1294 | } 1295 | 1296 | if (!XGetWindowAttributes(dpy, ev->window, &wa)) 1297 | return; 1298 | if (wa.override_redirect) 1299 | return; 1300 | if (!wintoclient(ev->window)) 1301 | manage(ev->window, &wa); 1302 | } 1303 | 1304 | void 1305 | monocle(Monitor *m) 1306 | { 1307 | unsigned int n = 0; 1308 | Client *c; 1309 | 1310 | for (c = m->clients; c; c = c->next) 1311 | if (ISVISIBLE(c)) 1312 | n++; 1313 | if (n > 0) /* override layout symbol */ 1314 | snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n); 1315 | for (c = nexttiled(m->clients); c; c = nexttiled(c->next)) 1316 | resize(c, m->wx, m->wy, m->ww - 2 * c->bw, m->wh - 2 * c->bw, 0); 1317 | } 1318 | 1319 | void 1320 | motionnotify(XEvent *e) 1321 | { 1322 | static Monitor *mon = NULL; 1323 | Monitor *m; 1324 | XMotionEvent *ev = &e->xmotion; 1325 | 1326 | if (ev->window != root) 1327 | return; 1328 | if ((m = recttomon(ev->x_root, ev->y_root, 1, 1)) != mon && mon) { 1329 | unfocus(selmon->sel, 1); 1330 | selmon = m; 1331 | focus(NULL); 1332 | } 1333 | mon = m; 1334 | } 1335 | 1336 | void 1337 | movemouse(const Arg *arg) 1338 | { 1339 | int x, y, ocx, ocy, nx, ny; 1340 | Client *c; 1341 | Monitor *m; 1342 | XEvent ev; 1343 | Time lasttime = 0; 1344 | 1345 | if (!(c = selmon->sel)) 1346 | return; 1347 | if (c->isfullscreen) /* no support moving fullscreen windows by mouse */ 1348 | return; 1349 | restack(selmon); 1350 | ocx = c->x; 1351 | ocy = c->y; 1352 | if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, 1353 | None, cursor[CurMove]->cursor, CurrentTime) != GrabSuccess) 1354 | return; 1355 | if (!getrootptr(&x, &y)) 1356 | return; 1357 | do { 1358 | XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); 1359 | switch(ev.type) { 1360 | case ConfigureRequest: 1361 | case Expose: 1362 | case MapRequest: 1363 | handler[ev.type](&ev); 1364 | break; 1365 | 1366 | case MotionNotify: 1367 | if ((ev.xmotion.time - lasttime) <= (1000 / 60)) 1368 | continue; 1369 | lasttime = ev.xmotion.time; 1370 | 1371 | nx = ocx + (ev.xmotion.x - x); 1372 | ny = ocy + (ev.xmotion.y - y); 1373 | if (abs(selmon->wx - nx) < snap) 1374 | nx = selmon->wx; 1375 | else if (abs((selmon->wx + selmon->ww) - (nx + WIDTH(c))) < snap) 1376 | nx = selmon->wx + selmon->ww - WIDTH(c); 1377 | if (abs(selmon->wy - ny) < snap) 1378 | ny = selmon->wy; 1379 | else if (abs((selmon->wy + selmon->wh) - (ny + HEIGHT(c))) < snap) 1380 | ny = selmon->wy + selmon->wh - HEIGHT(c); 1381 | if (!c->isfloating && selmon->lt[selmon->sellt]->arrange 1382 | && (abs(nx - c->x) > snap || abs(ny - c->y) > snap)) 1383 | togglefloating(NULL); 1384 | if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) 1385 | resize(c, nx, ny, c->w, c->h, 1); 1386 | break; 1387 | } 1388 | } while (ev.type != ButtonRelease); 1389 | XUngrabPointer(dpy, CurrentTime); 1390 | if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { 1391 | sendmon(c, m); 1392 | selmon = m; 1393 | focus(NULL); 1394 | } 1395 | } 1396 | 1397 | Client * 1398 | nexttiled(Client *c) 1399 | { 1400 | for (; c && (c->isfloating || !ISVISIBLE(c)); c = c->next); 1401 | return c; 1402 | } 1403 | 1404 | void 1405 | pop(Client *c) 1406 | { 1407 | detach(c); 1408 | attach(c); 1409 | focus(c); 1410 | arrange(c->mon); 1411 | } 1412 | 1413 | void 1414 | propertynotify(XEvent *e) 1415 | { 1416 | Client *c; 1417 | Window trans; 1418 | XPropertyEvent *ev = &e->xproperty; 1419 | 1420 | if ((c = wintosystrayicon(ev->window))) { 1421 | if (ev->atom == XA_WM_NORMAL_HINTS) { 1422 | updatesizehints(c); 1423 | updatesystrayicongeom(c, c->w, c->h); 1424 | } 1425 | else 1426 | updatesystrayiconstate(c, ev); 1427 | resizebarwin(selmon); 1428 | updatesystray(); 1429 | } 1430 | if ((ev->window == root) && (ev->atom == XA_WM_NAME)) 1431 | updatestatus(); 1432 | else if (ev->state == PropertyDelete) 1433 | return; /* ignore */ 1434 | else if ((c = wintoclient(ev->window))) { 1435 | switch(ev->atom) { 1436 | default: break; 1437 | case XA_WM_TRANSIENT_FOR: 1438 | if (!c->isfloating && (XGetTransientForHint(dpy, c->win, &trans)) && 1439 | (c->isfloating = (wintoclient(trans)) != NULL)) 1440 | arrange(c->mon); 1441 | break; 1442 | case XA_WM_NORMAL_HINTS: 1443 | updatesizehints(c); 1444 | break; 1445 | case XA_WM_HINTS: 1446 | updatewmhints(c); 1447 | drawbars(); 1448 | break; 1449 | } 1450 | if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) 1451 | updatetitle(c); 1452 | if (ev->atom == netatom[NetWMWindowType]) 1453 | updatewindowtype(c); 1454 | } 1455 | } 1456 | 1457 | void 1458 | quit(const Arg *arg) 1459 | { 1460 | running = 0; 1461 | } 1462 | 1463 | Monitor * 1464 | recttomon(int x, int y, int w, int h) 1465 | { 1466 | Monitor *m, *r = selmon; 1467 | int a, area = 0; 1468 | 1469 | for (m = mons; m; m = m->next) 1470 | if ((a = INTERSECT(x, y, w, h, m)) > area) { 1471 | area = a; 1472 | r = m; 1473 | } 1474 | return r; 1475 | } 1476 | 1477 | void 1478 | removesystrayicon(Client *i) 1479 | { 1480 | Client **ii; 1481 | 1482 | if (!showsystray || !i) 1483 | return; 1484 | for (ii = &systray->icons; *ii && *ii != i; ii = &(*ii)->next); 1485 | if (ii) 1486 | *ii = i->next; 1487 | free(i); 1488 | } 1489 | 1490 | 1491 | void 1492 | resize(Client *c, int x, int y, int w, int h, int interact) 1493 | { 1494 | if (applysizehints(c, &x, &y, &w, &h, interact)) 1495 | resizeclient(c, x, y, w, h); 1496 | } 1497 | 1498 | void 1499 | resizebarwin(Monitor *m) { 1500 | unsigned int w = m->ww; 1501 | if (showsystray && m == systraytomon(m)) 1502 | w -= getsystraywidth(); 1503 | XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, w, bh); 1504 | } 1505 | 1506 | void 1507 | resizeclient(Client *c, int x, int y, int w, int h) 1508 | { 1509 | XWindowChanges wc; 1510 | 1511 | c->oldx = c->x; c->x = wc.x = x; 1512 | c->oldy = c->y; c->y = wc.y = y; 1513 | c->oldw = c->w; c->w = wc.width = w; 1514 | c->oldh = c->h; c->h = wc.height = h; 1515 | wc.border_width = c->bw; 1516 | if (((nexttiled(c->mon->clients) == c && !nexttiled(c->next)) 1517 | || &monocle == c->mon->lt[c->mon->sellt]->arrange) 1518 | && !c->isfullscreen && !c->isfloating 1519 | && NULL != c->mon->lt[c->mon->sellt]->arrange) { 1520 | c->w = wc.width += c->bw * 2; 1521 | c->h = wc.height += c->bw * 2; 1522 | wc.border_width = 0; 1523 | } 1524 | XConfigureWindow(dpy, c->win, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc); 1525 | configure(c); 1526 | XSync(dpy, False); 1527 | } 1528 | 1529 | void 1530 | resizemouse(const Arg *arg) 1531 | { 1532 | int ocx, ocy, nw, nh; 1533 | Client *c; 1534 | Monitor *m; 1535 | XEvent ev; 1536 | Time lasttime = 0; 1537 | 1538 | if (!(c = selmon->sel)) 1539 | return; 1540 | if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */ 1541 | return; 1542 | restack(selmon); 1543 | ocx = c->x; 1544 | ocy = c->y; 1545 | if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, 1546 | None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess) 1547 | return; 1548 | XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); 1549 | do { 1550 | XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); 1551 | switch(ev.type) { 1552 | case ConfigureRequest: 1553 | case Expose: 1554 | case MapRequest: 1555 | handler[ev.type](&ev); 1556 | break; 1557 | case MotionNotify: 1558 | if ((ev.xmotion.time - lasttime) <= (1000 / 60)) 1559 | continue; 1560 | lasttime = ev.xmotion.time; 1561 | 1562 | nw = MAX(ev.xmotion.x - ocx - 2 * c->bw + 1, 1); 1563 | nh = MAX(ev.xmotion.y - ocy - 2 * c->bw + 1, 1); 1564 | if (c->mon->wx + nw >= selmon->wx && c->mon->wx + nw <= selmon->wx + selmon->ww 1565 | && c->mon->wy + nh >= selmon->wy && c->mon->wy + nh <= selmon->wy + selmon->wh) 1566 | { 1567 | if (!c->isfloating && selmon->lt[selmon->sellt]->arrange 1568 | && (abs(nw - c->w) > snap || abs(nh - c->h) > snap)) 1569 | togglefloating(NULL); 1570 | } 1571 | if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) 1572 | resize(c, c->x, c->y, nw, nh, 1); 1573 | break; 1574 | } 1575 | } while (ev.type != ButtonRelease); 1576 | XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); 1577 | XUngrabPointer(dpy, CurrentTime); 1578 | while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); 1579 | if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { 1580 | sendmon(c, m); 1581 | selmon = m; 1582 | focus(NULL); 1583 | } 1584 | } 1585 | 1586 | void 1587 | resizerequest(XEvent *e) 1588 | { 1589 | XResizeRequestEvent *ev = &e->xresizerequest; 1590 | Client *i; 1591 | 1592 | if ((i = wintosystrayicon(ev->window))) { 1593 | updatesystrayicongeom(i, ev->width, ev->height); 1594 | resizebarwin(selmon); 1595 | updatesystray(); 1596 | } 1597 | } 1598 | 1599 | void 1600 | restack(Monitor *m) 1601 | { 1602 | Client *c; 1603 | XEvent ev; 1604 | XWindowChanges wc; 1605 | 1606 | drawbar(m); 1607 | if (!m->sel) 1608 | return; 1609 | if (m->sel->isfloating || !m->lt[m->sellt]->arrange) 1610 | XRaiseWindow(dpy, m->sel->win); 1611 | if (m->lt[m->sellt]->arrange) { 1612 | wc.stack_mode = Below; 1613 | wc.sibling = m->barwin; 1614 | for (c = m->stack; c; c = c->snext) 1615 | if (!c->isfloating && ISVISIBLE(c)) { 1616 | XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc); 1617 | wc.sibling = c->win; 1618 | } 1619 | } 1620 | XSync(dpy, False); 1621 | while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); 1622 | if (m == selmon && (m->tagset[m->seltags] & m->sel->tags) && selmon->lt[selmon->sellt] != &layouts[2]) 1623 | warp(m->sel); 1624 | } 1625 | 1626 | void 1627 | rotatestack(const Arg *arg) 1628 | { 1629 | Client *c = NULL, *f; 1630 | 1631 | if (!selmon->sel) 1632 | return; 1633 | f = selmon->sel; 1634 | if (arg->i > 0) { 1635 | for (c = nexttiled(selmon->clients); c && nexttiled(c->next); c = nexttiled(c->next)); 1636 | if (c){ 1637 | detach(c); 1638 | attach(c); 1639 | detachstack(c); 1640 | attachstack(c); 1641 | } 1642 | } else { 1643 | if ((c = nexttiled(selmon->clients))){ 1644 | detach(c); 1645 | enqueue(c); 1646 | detachstack(c); 1647 | enqueuestack(c); 1648 | } 1649 | } 1650 | if (c){ 1651 | arrange(selmon); 1652 | //unfocus(f, 1); 1653 | focus(f); 1654 | restack(selmon); 1655 | } 1656 | } 1657 | 1658 | void 1659 | run(void) 1660 | { 1661 | XEvent ev; 1662 | /* main event loop */ 1663 | XSync(dpy, False); 1664 | while (running && !XNextEvent(dpy, &ev)) 1665 | if (handler[ev.type]) 1666 | handler[ev.type](&ev); /* call handler */ 1667 | } 1668 | 1669 | void 1670 | scan(void) 1671 | { 1672 | unsigned int i, num; 1673 | Window d1, d2, *wins = NULL; 1674 | XWindowAttributes wa; 1675 | 1676 | if (XQueryTree(dpy, root, &d1, &d2, &wins, &num)) { 1677 | for (i = 0; i < num; i++) { 1678 | if (!XGetWindowAttributes(dpy, wins[i], &wa) 1679 | || wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1)) 1680 | continue; 1681 | if (wa.map_state == IsViewable || getstate(wins[i]) == IconicState) 1682 | manage(wins[i], &wa); 1683 | } 1684 | for (i = 0; i < num; i++) { /* now the transients */ 1685 | if (!XGetWindowAttributes(dpy, wins[i], &wa)) 1686 | continue; 1687 | if (XGetTransientForHint(dpy, wins[i], &d1) 1688 | && (wa.map_state == IsViewable || getstate(wins[i]) == IconicState)) 1689 | manage(wins[i], &wa); 1690 | } 1691 | if (wins) 1692 | XFree(wins); 1693 | } 1694 | } 1695 | 1696 | void 1697 | sendmon(Client *c, Monitor *m) 1698 | { 1699 | if (c->mon == m) 1700 | return; 1701 | unfocus(c, 1); 1702 | detach(c); 1703 | detachstack(c); 1704 | c->mon = m; 1705 | c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ 1706 | attach(c); 1707 | attachstack(c); 1708 | focus(NULL); 1709 | arrange(NULL); 1710 | } 1711 | 1712 | void 1713 | setclientstate(Client *c, long state) 1714 | { 1715 | long data[] = { state, None }; 1716 | 1717 | XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32, 1718 | PropModeReplace, (unsigned char *)data, 2); 1719 | } 1720 | 1721 | int 1722 | sendevent(Window w, Atom proto, int mask, long d0, long d1, long d2, long d3, long d4) 1723 | { 1724 | int n; 1725 | Atom *protocols, mt; 1726 | int exists = 0; 1727 | XEvent ev; 1728 | 1729 | if (proto == wmatom[WMTakeFocus] || proto == wmatom[WMDelete]) { 1730 | mt = wmatom[WMProtocols]; 1731 | if (XGetWMProtocols(dpy, w, &protocols, &n)) { 1732 | while (!exists && n--) 1733 | exists = protocols[n] == proto; 1734 | XFree(protocols); 1735 | } 1736 | } 1737 | else { 1738 | exists = True; 1739 | mt = proto; 1740 | } 1741 | if (exists) { 1742 | ev.type = ClientMessage; 1743 | ev.xclient.window = w; 1744 | ev.xclient.message_type = mt; 1745 | ev.xclient.format = 32; 1746 | ev.xclient.data.l[0] = d0; 1747 | ev.xclient.data.l[1] = d1; 1748 | ev.xclient.data.l[2] = d2; 1749 | ev.xclient.data.l[3] = d3; 1750 | ev.xclient.data.l[4] = d4; 1751 | XSendEvent(dpy, w, False, mask, &ev); 1752 | } 1753 | return exists; 1754 | } 1755 | 1756 | void 1757 | setfocus(Client *c) 1758 | { 1759 | if (!c->neverfocus) { 1760 | XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); 1761 | XChangeProperty(dpy, root, netatom[NetActiveWindow], 1762 | XA_WINDOW, 32, PropModeReplace, 1763 | (unsigned char *) &(c->win), 1); 1764 | } 1765 | sendevent(c->win, wmatom[WMTakeFocus], NoEventMask, wmatom[WMTakeFocus], CurrentTime, 0, 0, 0); 1766 | } 1767 | 1768 | void 1769 | setfullscreen(Client *c, int fullscreen) 1770 | { 1771 | if (fullscreen && !c->isfullscreen) { 1772 | XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, 1773 | PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1); 1774 | c->isfullscreen = 1; 1775 | c->oldstate = c->isfloating; 1776 | c->oldbw = c->bw; 1777 | c->bw = 0; 1778 | c->isfloating = 1; 1779 | resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); 1780 | XRaiseWindow(dpy, c->win); 1781 | } else if (!fullscreen && c->isfullscreen){ 1782 | XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, 1783 | PropModeReplace, (unsigned char*)0, 0); 1784 | c->isfullscreen = 0; 1785 | c->isfloating = c->oldstate; 1786 | c->bw = c->oldbw; 1787 | c->x = c->oldx; 1788 | c->y = c->oldy; 1789 | c->w = c->oldw; 1790 | c->h = c->oldh; 1791 | resizeclient(c, c->x, c->y, c->w, c->h); 1792 | arrange(c->mon); 1793 | } 1794 | } 1795 | 1796 | void 1797 | setlayout(const Arg *arg) 1798 | { 1799 | if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) 1800 | selmon->sellt ^= 1; 1801 | if (arg && arg->v) 1802 | selmon->lt[selmon->sellt] = (Layout *)arg->v; 1803 | strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol); 1804 | if (selmon->sel) 1805 | arrange(selmon); 1806 | else 1807 | drawbar(selmon); 1808 | } 1809 | 1810 | /* arg > 1.0 will set mfact absolutely */ 1811 | void 1812 | setmfact(const Arg *arg) 1813 | { 1814 | float f; 1815 | 1816 | if (!arg || !selmon->lt[selmon->sellt]->arrange) 1817 | return; 1818 | f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0; 1819 | if (f < 0.05 || f > 0.95) 1820 | return; 1821 | selmon->mfact = f; 1822 | arrange(selmon); 1823 | } 1824 | 1825 | void 1826 | setup(void) 1827 | { 1828 | int i; 1829 | XSetWindowAttributes wa; 1830 | Atom utf8string; 1831 | 1832 | /* clean up any zombies immediately */ 1833 | sigchld(0); 1834 | 1835 | /* init screen */ 1836 | screen = DefaultScreen(dpy); 1837 | sw = DisplayWidth(dpy, screen); 1838 | sh = DisplayHeight(dpy, screen); 1839 | root = RootWindow(dpy, screen); 1840 | drw = drw_create(dpy, screen, root, sw, sh); 1841 | if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) 1842 | die("no fonts could be loaded."); 1843 | lrpad = drw->fonts->h; 1844 | bh = drw->fonts->h + 2; 1845 | updategeom(); 1846 | /* init atoms */ 1847 | utf8string = XInternAtom(dpy, "UTF8_STRING", False); 1848 | wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); 1849 | wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); 1850 | wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False); 1851 | wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False); 1852 | netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); 1853 | netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); 1854 | netatom[NetSystemTray] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_S0", False); 1855 | netatom[NetSystemTrayOP] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_OPCODE", False); 1856 | netatom[NetSystemTrayOrientation] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION", False); 1857 | netatom[NetSystemTrayOrientationHorz] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION_HORZ", False); 1858 | netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); 1859 | netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False); 1860 | netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False); 1861 | netatom[NetWMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); 1862 | netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); 1863 | netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); 1864 | netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False); 1865 | xatom[Manager] = XInternAtom(dpy, "MANAGER", False); 1866 | xatom[Xembed] = XInternAtom(dpy, "_XEMBED", False); 1867 | xatom[XembedInfo] = XInternAtom(dpy, "_XEMBED_INFO", False); 1868 | /* init cursors */ 1869 | cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr); 1870 | cursor[CurResize] = drw_cur_create(drw, XC_sizing); 1871 | cursor[CurMove] = drw_cur_create(drw, XC_fleur); 1872 | /* init appearance */ 1873 | scheme = ecalloc(LENGTH(colors), sizeof(Clr *)); 1874 | for (i = 0; i < LENGTH(colors); i++) 1875 | scheme[i] = drw_scm_create(drw, colors[i], 3); 1876 | /* init system tray */ 1877 | updatesystray(); 1878 | /* init bars */ 1879 | updatebars(); 1880 | updatestatus(); 1881 | /* supporting window for NetWMCheck */ 1882 | wmcheckwin = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 0, 0, 0); 1883 | XChangeProperty(dpy, wmcheckwin, netatom[NetWMCheck], XA_WINDOW, 32, 1884 | PropModeReplace, (unsigned char *) &wmcheckwin, 1); 1885 | XChangeProperty(dpy, wmcheckwin, netatom[NetWMName], utf8string, 8, 1886 | PropModeReplace, (unsigned char *) "dwm", 3); 1887 | XChangeProperty(dpy, root, netatom[NetWMCheck], XA_WINDOW, 32, 1888 | PropModeReplace, (unsigned char *) &wmcheckwin, 1); 1889 | /* EWMH support per view */ 1890 | XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32, 1891 | PropModeReplace, (unsigned char *) netatom, NetLast); 1892 | XDeleteProperty(dpy, root, netatom[NetClientList]); 1893 | /* select events */ 1894 | wa.cursor = cursor[CurNormal]->cursor; 1895 | wa.event_mask = SubstructureRedirectMask|SubstructureNotifyMask 1896 | |ButtonPressMask|PointerMotionMask|EnterWindowMask 1897 | |LeaveWindowMask|StructureNotifyMask|PropertyChangeMask; 1898 | XChangeWindowAttributes(dpy, root, CWEventMask|CWCursor, &wa); 1899 | XSelectInput(dpy, root, wa.event_mask); 1900 | grabkeys(); 1901 | focus(NULL); 1902 | } 1903 | 1904 | 1905 | void 1906 | seturgent(Client *c, int urg) 1907 | { 1908 | XWMHints *wmh; 1909 | 1910 | c->isurgent = urg; 1911 | if (!(wmh = XGetWMHints(dpy, c->win))) 1912 | return; 1913 | wmh->flags = urg ? (wmh->flags | XUrgencyHint) : (wmh->flags & ~XUrgencyHint); 1914 | XSetWMHints(dpy, c->win, wmh); 1915 | XFree(wmh); 1916 | } 1917 | 1918 | void 1919 | showhide(Client *c) 1920 | { 1921 | if (!c) 1922 | return; 1923 | if (ISVISIBLE(c)) { 1924 | /* show clients top down */ 1925 | XMoveWindow(dpy, c->win, c->x, c->y); 1926 | if ((!c->mon->lt[c->mon->sellt]->arrange || c->isfloating) && !c->isfullscreen) 1927 | resize(c, c->x, c->y, c->w, c->h, 0); 1928 | showhide(c->snext); 1929 | } else { 1930 | /* hide clients bottom up */ 1931 | showhide(c->snext); 1932 | XMoveWindow(dpy, c->win, WIDTH(c) * -2, c->y); 1933 | } 1934 | } 1935 | 1936 | void 1937 | sigchld(int unused) 1938 | { 1939 | if (signal(SIGCHLD, sigchld) == SIG_ERR) 1940 | die("can't install SIGCHLD handler:"); 1941 | while (0 < waitpid(-1, NULL, WNOHANG)); 1942 | } 1943 | 1944 | void 1945 | spawn(const Arg *arg) 1946 | { 1947 | if (fork() == 0) { 1948 | if (dpy) 1949 | close(ConnectionNumber(dpy)); 1950 | setsid(); 1951 | execvp(((char **)arg->v)[0], (char **)arg->v); 1952 | fprintf(stderr, "dwm: execvp %s", ((char **)arg->v)[0]); 1953 | perror(" failed"); 1954 | exit(EXIT_SUCCESS); 1955 | } 1956 | } 1957 | 1958 | void 1959 | tag(const Arg *arg) 1960 | { 1961 | if (selmon->sel && arg->ui & TAGMASK) { 1962 | selmon->sel->tags = arg->ui & TAGMASK; 1963 | focus(NULL); 1964 | arrange(selmon); 1965 | } 1966 | } 1967 | 1968 | void 1969 | tagmon(const Arg *arg) 1970 | { 1971 | if (!selmon->sel || !mons->next) 1972 | return; 1973 | sendmon(selmon->sel, dirtomon(arg->i)); 1974 | } 1975 | 1976 | /*void 1977 | tile(Monitor *m) 1978 | { 1979 | unsigned int i, n, h, mw, my, ty; 1980 | Client *c; 1981 | 1982 | for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); 1983 | if (n == 0) 1984 | return; 1985 | 1986 | if (n > m->nmaster) 1987 | mw = m->nmaster ? m->ww * m->mfact : 0; 1988 | else 1989 | mw = m->ww; 1990 | for (i = my = ty = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) 1991 | if (i < m->nmaster) { 1992 | h = (m->wh - my) / (MIN(n, m->nmaster) - i); 1993 | resize(c, m->wx, m->wy + my, mw - (2*c->bw), h - (2*c->bw), 0); 1994 | if (my + HEIGHT(c) < m->wh) 1995 | my += HEIGHT(c); 1996 | } else { 1997 | h = (m->wh - ty) / (n - i); 1998 | resize(c, m->wx + mw, m->wy + ty, m->ww - mw - (2*c->bw), h - (2*c->bw), 0); 1999 | if (ty + HEIGHT(c) < m->wh) 2000 | ty += HEIGHT(c); 2001 | } 2002 | }*/ 2003 | 2004 | void 2005 | togglebar(const Arg *arg) 2006 | { 2007 | selmon->showbar = !selmon->showbar; 2008 | updatebarpos(selmon); 2009 | resizebarwin(selmon); 2010 | if (showsystray) { 2011 | XWindowChanges wc; 2012 | if (!selmon->showbar) 2013 | wc.y = -bh; 2014 | else if (selmon->showbar) { 2015 | wc.y = 0; 2016 | if (!selmon->topbar) 2017 | wc.y = selmon->mh - bh; 2018 | } 2019 | XConfigureWindow(dpy, systray->win, CWY, &wc); 2020 | } 2021 | arrange(selmon); 2022 | } 2023 | 2024 | void 2025 | togglefloating(const Arg *arg) 2026 | { 2027 | if (!selmon->sel) 2028 | return; 2029 | if (selmon->sel->isfullscreen) /* no support for fullscreen windows */ 2030 | return; 2031 | selmon->sel->isfloating = !selmon->sel->isfloating || selmon->sel->isfixed; 2032 | if (selmon->sel->isfloating) 2033 | resize(selmon->sel, selmon->sel->x, selmon->sel->y, 2034 | selmon->sel->w, selmon->sel->h, 0); 2035 | arrange(selmon); 2036 | } 2037 | 2038 | void 2039 | toggletag(const Arg *arg) 2040 | { 2041 | unsigned int newtags; 2042 | 2043 | if (!selmon->sel) 2044 | return; 2045 | newtags = selmon->sel->tags ^ (arg->ui & TAGMASK); 2046 | if (newtags) { 2047 | selmon->sel->tags = newtags; 2048 | focus(NULL); 2049 | arrange(selmon); 2050 | } 2051 | } 2052 | 2053 | void 2054 | toggleview(const Arg *arg) 2055 | { 2056 | unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK); 2057 | 2058 | if (newtagset) { 2059 | selmon->tagset[selmon->seltags] = newtagset; 2060 | focus(NULL); 2061 | arrange(selmon); 2062 | } 2063 | } 2064 | 2065 | void 2066 | unfocus(Client *c, int setfocus) 2067 | { 2068 | if (!c) 2069 | return; 2070 | grabbuttons(c, 0); 2071 | XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel); 2072 | if (setfocus) { 2073 | XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); 2074 | XDeleteProperty(dpy, root, netatom[NetActiveWindow]); 2075 | } 2076 | } 2077 | 2078 | void 2079 | unmanage(Client *c, int destroyed) 2080 | { 2081 | Monitor *m = c->mon; 2082 | XWindowChanges wc; 2083 | 2084 | detach(c); 2085 | detachstack(c); 2086 | if (!destroyed) { 2087 | wc.border_width = c->oldbw; 2088 | XGrabServer(dpy); /* avoid race conditions */ 2089 | XSetErrorHandler(xerrordummy); 2090 | XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */ 2091 | XUngrabButton(dpy, AnyButton, AnyModifier, c->win); 2092 | setclientstate(c, WithdrawnState); 2093 | XSync(dpy, False); 2094 | XSetErrorHandler(xerror); 2095 | XUngrabServer(dpy); 2096 | } 2097 | free(c); 2098 | focus(NULL); 2099 | updateclientlist(); 2100 | arrange(m); 2101 | } 2102 | 2103 | void 2104 | unmapnotify(XEvent *e) 2105 | { 2106 | Client *c; 2107 | XUnmapEvent *ev = &e->xunmap; 2108 | 2109 | if ((c = wintoclient(ev->window))) { 2110 | if (ev->send_event) 2111 | setclientstate(c, WithdrawnState); 2112 | else 2113 | unmanage(c, 0); 2114 | } 2115 | else if ((c = wintosystrayicon(ev->window))) { 2116 | /* KLUDGE! sometimes icons occasionally unmap their windows, but do 2117 | * _not_ destroy them. We map those windows back */ 2118 | XMapRaised(dpy, c->win); 2119 | updatesystray(); 2120 | } 2121 | } 2122 | 2123 | void 2124 | updatebars(void) 2125 | { 2126 | unsigned int w; 2127 | Monitor *m; 2128 | XSetWindowAttributes wa = { 2129 | .override_redirect = True, 2130 | .background_pixmap = ParentRelative, 2131 | .event_mask = ButtonPressMask|ExposureMask 2132 | }; 2133 | XClassHint ch = {"dwm", "dwm"}; 2134 | for (m = mons; m; m = m->next) { 2135 | if (m->barwin) 2136 | continue; 2137 | w = m->ww; 2138 | if (showsystray && m == systraytomon(m)) 2139 | w -= getsystraywidth(); 2140 | m->barwin = XCreateWindow(dpy, root, m->wx, m->by, w, bh, 0, DefaultDepth(dpy, screen), 2141 | CopyFromParent, DefaultVisual(dpy, screen), 2142 | CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); 2143 | XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); 2144 | if (showsystray && m == systraytomon(m)) 2145 | XMapRaised(dpy, systray->win); 2146 | XMapRaised(dpy, m->barwin); 2147 | XSetClassHint(dpy, m->barwin, &ch); 2148 | } 2149 | } 2150 | 2151 | void 2152 | updatebarpos(Monitor *m) 2153 | { 2154 | m->wy = m->my; 2155 | m->wh = m->mh; 2156 | if (m->showbar) { 2157 | m->wh -= bh; 2158 | m->by = m->topbar ? m->wy : m->wy + m->wh; 2159 | m->wy = m->topbar ? m->wy + bh : m->wy; 2160 | } else 2161 | m->by = -bh; 2162 | } 2163 | 2164 | void 2165 | updateclientlist() 2166 | { 2167 | Client *c; 2168 | Monitor *m; 2169 | 2170 | XDeleteProperty(dpy, root, netatom[NetClientList]); 2171 | for (m = mons; m; m = m->next) 2172 | for (c = m->clients; c; c = c->next) 2173 | XChangeProperty(dpy, root, netatom[NetClientList], 2174 | XA_WINDOW, 32, PropModeAppend, 2175 | (unsigned char *) &(c->win), 1); 2176 | } 2177 | 2178 | int 2179 | updategeom(void) 2180 | { 2181 | int dirty = 0; 2182 | 2183 | #ifdef XINERAMA 2184 | if (XineramaIsActive(dpy)) { 2185 | int i, j, n, nn; 2186 | Client *c; 2187 | Monitor *m; 2188 | XineramaScreenInfo *info = XineramaQueryScreens(dpy, &nn); 2189 | XineramaScreenInfo *unique = NULL; 2190 | 2191 | for (n = 0, m = mons; m; m = m->next, n++); 2192 | /* only consider unique geometries as separate screens */ 2193 | unique = ecalloc(nn, sizeof(XineramaScreenInfo)); 2194 | for (i = 0, j = 0; i < nn; i++) 2195 | if (isuniquegeom(unique, j, &info[i])) 2196 | memcpy(&unique[j++], &info[i], sizeof(XineramaScreenInfo)); 2197 | XFree(info); 2198 | nn = j; 2199 | if (n <= nn) { /* new monitors available */ 2200 | for (i = 0; i < (nn - n); i++) { 2201 | for (m = mons; m && m->next; m = m->next); 2202 | if (m) 2203 | m->next = createmon(); 2204 | else 2205 | mons = createmon(); 2206 | } 2207 | for (i = 0, m = mons; i < nn && m; m = m->next, i++){ 2208 | if (i >= n 2209 | || unique[i].x_org != m->mx || unique[i].y_org != m->my 2210 | || unique[i].width != m->mw || unique[i].height != m->mh) 2211 | { 2212 | dirty = 1; 2213 | m->num = i; 2214 | m->mx = m->wx = unique[i].x_org; 2215 | m->my = m->wy = unique[i].y_org; 2216 | m->mw = m->ww = unique[i].width; 2217 | m->mh = m->wh = unique[i].height; 2218 | updatebarpos(m); 2219 | } 2220 | if(i == statmonval) 2221 | statmon = m; 2222 | } 2223 | 2224 | } else { /* less monitors available nn < n */ 2225 | for (i = nn; i < n; i++) { 2226 | for (m = mons; m && m->next; m = m->next); 2227 | while ((c = m->clients)) { 2228 | dirty = 1; 2229 | m->clients = c->next; 2230 | detachstack(c); 2231 | c->mon = mons; 2232 | attach(c); 2233 | attachstack(c); 2234 | } 2235 | if (m == selmon) 2236 | selmon = mons; 2237 | if (m == statmon) 2238 | statmon = mons; 2239 | cleanupmon(m); 2240 | } 2241 | } 2242 | free(unique); 2243 | } else 2244 | #endif /* XINERAMA */ 2245 | { /* default monitor setup */ 2246 | if (!mons) 2247 | mons = createmon(); 2248 | if (mons->mw != sw || mons->mh != sh) { 2249 | dirty = 1; 2250 | mons->mw = mons->ww = sw; 2251 | mons->mh = mons->wh = sh; 2252 | updatebarpos(mons); 2253 | } 2254 | } 2255 | if (dirty) { 2256 | selmon = mons; 2257 | selmon = wintomon(root); 2258 | } 2259 | return dirty; 2260 | } 2261 | 2262 | void 2263 | updatenumlockmask(void) 2264 | { 2265 | unsigned int i, j; 2266 | XModifierKeymap *modmap; 2267 | 2268 | numlockmask = 0; 2269 | modmap = XGetModifierMapping(dpy); 2270 | for (i = 0; i < 8; i++) 2271 | for (j = 0; j < modmap->max_keypermod; j++) 2272 | if (modmap->modifiermap[i * modmap->max_keypermod + j] 2273 | == XKeysymToKeycode(dpy, XK_Num_Lock)) 2274 | numlockmask = (1 << i); 2275 | XFreeModifiermap(modmap); 2276 | } 2277 | 2278 | void 2279 | updatesizehints(Client *c) 2280 | { 2281 | long msize; 2282 | XSizeHints size; 2283 | 2284 | if (!XGetWMNormalHints(dpy, c->win, &size, &msize)) 2285 | /* size is uninitialized, ensure that size.flags aren't used */ 2286 | size.flags = PSize; 2287 | if (size.flags & PBaseSize) { 2288 | c->basew = size.base_width; 2289 | c->baseh = size.base_height; 2290 | } else if (size.flags & PMinSize) { 2291 | c->basew = size.min_width; 2292 | c->baseh = size.min_height; 2293 | } else 2294 | c->basew = c->baseh = 0; 2295 | if (size.flags & PResizeInc) { 2296 | c->incw = size.width_inc; 2297 | c->inch = size.height_inc; 2298 | } else 2299 | c->incw = c->inch = 0; 2300 | if (size.flags & PMaxSize) { 2301 | c->maxw = size.max_width; 2302 | c->maxh = size.max_height; 2303 | } else 2304 | c->maxw = c->maxh = 0; 2305 | if (size.flags & PMinSize) { 2306 | c->minw = size.min_width; 2307 | c->minh = size.min_height; 2308 | } else if (size.flags & PBaseSize) { 2309 | c->minw = size.base_width; 2310 | c->minh = size.base_height; 2311 | } else 2312 | c->minw = c->minh = 0; 2313 | if (size.flags & PAspect) { 2314 | c->mina = (float)size.min_aspect.y / size.min_aspect.x; 2315 | c->maxa = (float)size.max_aspect.x / size.max_aspect.y; 2316 | } else 2317 | c->maxa = c->mina = 0.0; 2318 | c->isfixed = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh); 2319 | } 2320 | 2321 | void 2322 | updatestatus(void) 2323 | { 2324 | if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) 2325 | strcpy(stext, "dwm-"VERSION); 2326 | drawbar(statmon); 2327 | updatesystray(); 2328 | } 2329 | 2330 | void 2331 | updatesystrayicongeom(Client *i, int w, int h) 2332 | { 2333 | if (i) { 2334 | i->h = bh; 2335 | if (w == h) 2336 | i->w = bh; 2337 | else if (h == bh) 2338 | i->w = w; 2339 | else 2340 | i->w = (int) ((float)bh * ((float)w / (float)h)); 2341 | applysizehints(i, &(i->x), &(i->y), &(i->w), &(i->h), False); 2342 | /* force icons into the systray dimensions if they don't want to */ 2343 | if (i->h > bh) { 2344 | if (i->w == i->h) 2345 | i->w = bh; 2346 | else 2347 | i->w = (int) ((float)bh * ((float)i->w / (float)i->h)); 2348 | i->h = bh; 2349 | } 2350 | } 2351 | } 2352 | 2353 | void 2354 | updatesystrayiconstate(Client *i, XPropertyEvent *ev) 2355 | { 2356 | long flags; 2357 | int code = 0; 2358 | 2359 | if (!showsystray || !i || ev->atom != xatom[XembedInfo] || 2360 | !(flags = getatomprop(i, xatom[XembedInfo]))) 2361 | return; 2362 | 2363 | if (flags & XEMBED_MAPPED && !i->tags) { 2364 | i->tags = 1; 2365 | code = XEMBED_WINDOW_ACTIVATE; 2366 | XMapRaised(dpy, i->win); 2367 | setclientstate(i, NormalState); 2368 | } 2369 | else if (!(flags & XEMBED_MAPPED) && i->tags) { 2370 | i->tags = 0; 2371 | code = XEMBED_WINDOW_DEACTIVATE; 2372 | XUnmapWindow(dpy, i->win); 2373 | setclientstate(i, WithdrawnState); 2374 | } 2375 | else 2376 | return; 2377 | sendevent(i->win, xatom[Xembed], StructureNotifyMask, CurrentTime, code, 0, 2378 | systray->win, XEMBED_EMBEDDED_VERSION); 2379 | } 2380 | 2381 | void 2382 | updatesystray(void) 2383 | { 2384 | XSetWindowAttributes wa; 2385 | XWindowChanges wc; 2386 | Client *i; 2387 | Monitor *m = systraytomon(NULL); 2388 | unsigned int x = m->mx + m->mw; 2389 | unsigned int w = 1; 2390 | 2391 | if (!showsystray) 2392 | return; 2393 | if (!systray) { 2394 | /* init systray */ 2395 | if (!(systray = (Systray *)calloc(1, sizeof(Systray)))) 2396 | die("fatal: could not malloc() %u bytes\n", sizeof(Systray)); 2397 | systray->win = XCreateSimpleWindow(dpy, root, x, m->by, w, bh, 0, 0, scheme[SchemeSel][ColBg].pixel); 2398 | wa.event_mask = ButtonPressMask | ExposureMask; 2399 | wa.override_redirect = True; 2400 | wa.background_pixel = scheme[SchemeNorm][ColBg].pixel; 2401 | XSelectInput(dpy, systray->win, SubstructureNotifyMask); 2402 | XChangeProperty(dpy, systray->win, netatom[NetSystemTrayOrientation], XA_CARDINAL, 32, 2403 | PropModeReplace, (unsigned char *)&netatom[NetSystemTrayOrientationHorz], 1); 2404 | XChangeWindowAttributes(dpy, systray->win, CWEventMask|CWOverrideRedirect|CWBackPixel, &wa); 2405 | XMapRaised(dpy, systray->win); 2406 | XSetSelectionOwner(dpy, netatom[NetSystemTray], systray->win, CurrentTime); 2407 | if (XGetSelectionOwner(dpy, netatom[NetSystemTray]) == systray->win) { 2408 | sendevent(root, xatom[Manager], StructureNotifyMask, CurrentTime, netatom[NetSystemTray], systray->win, 0, 0); 2409 | XSync(dpy, False); 2410 | } 2411 | else { 2412 | fprintf(stderr, "dwm: unable to obtain system tray.\n"); 2413 | free(systray); 2414 | systray = NULL; 2415 | return; 2416 | } 2417 | } 2418 | for (w = 0, i = systray->icons; i; i = i->next) { 2419 | /* make sure the background color stays the same */ 2420 | wa.background_pixel = scheme[SchemeNorm][ColBg].pixel; 2421 | XChangeWindowAttributes(dpy, i->win, CWBackPixel, &wa); 2422 | XMapRaised(dpy, i->win); 2423 | w += systrayspacing; 2424 | i->x = w; 2425 | XMoveResizeWindow(dpy, i->win, i->x, 0, i->w, i->h); 2426 | w += i->w; 2427 | if (i->mon != m) 2428 | i->mon = m; 2429 | } 2430 | w = w ? w + systrayspacing : 1; 2431 | x -= w; 2432 | XMoveResizeWindow(dpy, systray->win, x, m->by, w, bh); 2433 | wc.x = x; wc.y = m->by; wc.width = w; wc.height = bh; 2434 | wc.stack_mode = Above; wc.sibling = m->barwin; 2435 | XConfigureWindow(dpy, systray->win, CWX|CWY|CWWidth|CWHeight|CWSibling|CWStackMode, &wc); 2436 | XMapWindow(dpy, systray->win); 2437 | XMapSubwindows(dpy, systray->win); 2438 | /* redraw background */ 2439 | XSetForeground(dpy, drw->gc, scheme[SchemeNorm][ColBg].pixel); 2440 | XFillRectangle(dpy, systray->win, drw->gc, 0, 0, w, bh); 2441 | XSync(dpy, False); 2442 | } 2443 | 2444 | void 2445 | updatetitle(Client *c) 2446 | { 2447 | if (!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name)) 2448 | gettextprop(c->win, XA_WM_NAME, c->name, sizeof c->name); 2449 | if (c->name[0] == '\0') /* hack to mark broken clients */ 2450 | strcpy(c->name, broken); 2451 | } 2452 | 2453 | void 2454 | updatewindowtype(Client *c) 2455 | { 2456 | Atom state = getatomprop(c, netatom[NetWMState]); 2457 | Atom wtype = getatomprop(c, netatom[NetWMWindowType]); 2458 | 2459 | if (state == netatom[NetWMFullscreen]) 2460 | setfullscreen(c, 1); 2461 | if (wtype == netatom[NetWMWindowTypeDialog]) 2462 | c->isfloating = 1; 2463 | } 2464 | 2465 | void 2466 | updatewmhints(Client *c) 2467 | { 2468 | XWMHints *wmh; 2469 | 2470 | if ((wmh = XGetWMHints(dpy, c->win))) { 2471 | if (c == selmon->sel && wmh->flags & XUrgencyHint) { 2472 | wmh->flags &= ~XUrgencyHint; 2473 | XSetWMHints(dpy, c->win, wmh); 2474 | } else 2475 | c->isurgent = (wmh->flags & XUrgencyHint) ? 1 : 0; 2476 | if (wmh->flags & InputHint) 2477 | c->neverfocus = !wmh->input; 2478 | else 2479 | c->neverfocus = 0; 2480 | XFree(wmh); 2481 | } 2482 | } 2483 | 2484 | void 2485 | view(const Arg *arg) 2486 | { 2487 | if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) 2488 | return; 2489 | selmon->seltags ^= 1; /* toggle sel tagset */ 2490 | if (arg->ui & TAGMASK) 2491 | selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; 2492 | focus(NULL); 2493 | arrange(selmon); 2494 | } 2495 | 2496 | void 2497 | warp(const Client *c) 2498 | { 2499 | int x, y; 2500 | 2501 | if (!c) { 2502 | XWarpPointer(dpy, None, root, 0, 0, 0, 0, selmon->wx + selmon->ww/2, selmon->wy + selmon->wh/2); 2503 | return; 2504 | } 2505 | 2506 | if (!getrootptr(&x, &y) || 2507 | (x > c->x - c->bw && 2508 | y > c->y - c->bw && 2509 | x < c->x + c->w + c->bw*2 && 2510 | y < c->y + c->h + c->bw*2) || 2511 | (y > c->mon->by && y < c->mon->by + bh) || 2512 | (c->mon->topbar && !y)) 2513 | return; 2514 | 2515 | XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w / 2, c->h / 2); 2516 | } 2517 | 2518 | Client * 2519 | wintoclient(Window w) 2520 | { 2521 | Client *c; 2522 | Monitor *m; 2523 | 2524 | for (m = mons; m; m = m->next) 2525 | for (c = m->clients; c; c = c->next) 2526 | if (c->win == w) 2527 | return c; 2528 | return NULL; 2529 | } 2530 | 2531 | Client * 2532 | wintosystrayicon(Window w) { 2533 | Client *i = NULL; 2534 | 2535 | if (!showsystray || !w) 2536 | return i; 2537 | for (i = systray->icons; i && i->win != w; i = i->next) ; 2538 | return i; 2539 | } 2540 | 2541 | Monitor * 2542 | wintomon(Window w) 2543 | { 2544 | int x, y; 2545 | Client *c; 2546 | Monitor *m; 2547 | 2548 | if (w == root && getrootptr(&x, &y)) 2549 | return recttomon(x, y, 1, 1); 2550 | for (m = mons; m; m = m->next) 2551 | if (w == m->barwin) 2552 | return m; 2553 | if ((c = wintoclient(w))) 2554 | return c->mon; 2555 | return selmon; 2556 | } 2557 | 2558 | /* There's no way to check accesses to destroyed windows, thus those cases are 2559 | * ignored (especially on UnmapNotify's). Other types of errors call Xlibs 2560 | * default error handler, which may call exit. */ 2561 | int 2562 | xerror(Display *dpy, XErrorEvent *ee) 2563 | { 2564 | if (ee->error_code == BadWindow 2565 | || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch) 2566 | || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable) 2567 | || (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable) 2568 | || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable) 2569 | || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch) 2570 | || (ee->request_code == X_GrabButton && ee->error_code == BadAccess) 2571 | || (ee->request_code == X_GrabKey && ee->error_code == BadAccess) 2572 | || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable)) 2573 | return 0; 2574 | fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n", 2575 | ee->request_code, ee->error_code); 2576 | return xerrorxlib(dpy, ee); /* may call exit */ 2577 | } 2578 | 2579 | int 2580 | xerrordummy(Display *dpy, XErrorEvent *ee) 2581 | { 2582 | return 0; 2583 | } 2584 | 2585 | /* Startup Error handler to check if another window manager 2586 | * is already running. */ 2587 | int 2588 | xerrorstart(Display *dpy, XErrorEvent *ee) 2589 | { 2590 | die("dwm: another window manager is already running"); 2591 | return -1; 2592 | } 2593 | 2594 | Monitor * 2595 | systraytomon(Monitor *m) { 2596 | Monitor *t; 2597 | int i, n; 2598 | if(!systraypinning) { 2599 | if(!m) 2600 | return selmon; 2601 | return m == selmon ? m : NULL; 2602 | } 2603 | for(n = 1, t = mons; t && t->next; n++, t = t->next) ; 2604 | for(i = 1, t = mons; t && t->next && i < systraypinning; i++, t = t->next) ; 2605 | if(systraypinningfailfirst && n < systraypinning) 2606 | return mons; 2607 | return t; 2608 | } 2609 | 2610 | void 2611 | zoom(const Arg *arg) 2612 | { 2613 | Client *c = selmon->sel; 2614 | 2615 | if (!selmon->lt[selmon->sellt]->arrange 2616 | || (selmon->sel && selmon->sel->isfloating)) 2617 | return; 2618 | if (c == nexttiled(selmon->clients)) 2619 | if (!c || !(c = nexttiled(c->next))) 2620 | return; 2621 | pop(c); 2622 | } 2623 | 2624 | int 2625 | main(int argc, char *argv[]) 2626 | { 2627 | if (argc == 2 && !strcmp("-v", argv[1])) 2628 | die("dwm-"VERSION); 2629 | else if (argc != 1) 2630 | die("usage: dwm [-v]"); 2631 | if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) 2632 | fputs("warning: no locale support\n", stderr); 2633 | if (!(dpy = XOpenDisplay(NULL))) 2634 | die("dwm: cannot open display"); 2635 | checkotherwm(); 2636 | setup(); 2637 | #ifdef __OpenBSD__ 2638 | if (pledge("stdio rpath proc exec", NULL) == -1) 2639 | die("pledge"); 2640 | #endif /* __OpenBSD__ */ 2641 | scan(); 2642 | run(); 2643 | cleanup(); 2644 | XCloseDisplay(dpy); 2645 | return EXIT_SUCCESS; 2646 | } 2647 | -------------------------------------------------------------------------------- /dwm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChristianChiarulli/dwm/7d3cc233f1aa16584bf4e9d0bed68d51e16135e3/dwm.png -------------------------------------------------------------------------------- /patches/shiftview.c: -------------------------------------------------------------------------------- 1 | /** Function to shift the current view to the left/right 2 | * 3 | * @param: "arg->i" stores the number of tags to shift right (positive value) 4 | * or left (negative value) 5 | */ 6 | void 7 | shiftview(const Arg *arg) { 8 | Arg shifted; 9 | 10 | if(arg->i > 0) // left circular shift 11 | shifted.ui = (selmon->tagset[selmon->seltags] << arg->i) 12 | | (selmon->tagset[selmon->seltags] >> (LENGTH(tags) - arg->i)); 13 | 14 | else // right circular shift 15 | shifted.ui = selmon->tagset[selmon->seltags] >> (- arg->i) 16 | | selmon->tagset[selmon->seltags] << (LENGTH(tags) + arg->i); 17 | 18 | view(&shifted); 19 | } 20 | -------------------------------------------------------------------------------- /patches/vanitygaps.c: -------------------------------------------------------------------------------- 1 | /* Key binding functions */ 2 | static void defaultgaps(const Arg *arg); 3 | static void incrgaps(const Arg *arg); 4 | static void incrigaps(const Arg *arg); 5 | static void incrogaps(const Arg *arg); 6 | static void incrohgaps(const Arg *arg); 7 | static void incrovgaps(const Arg *arg); 8 | static void incrihgaps(const Arg *arg); 9 | static void incrivgaps(const Arg *arg); 10 | static void togglegaps(const Arg *arg); 11 | /* Layouts (delete the ones you do not need) */ 12 | static void bstack(Monitor *m); 13 | static void bstackhoriz(Monitor *m); 14 | static void centeredmaster(Monitor *m); 15 | static void centeredfloatingmaster(Monitor *m); 16 | static void deck(Monitor *m); 17 | static void dwindle(Monitor *m); 18 | static void fibonacci(Monitor *m, int s); 19 | static void grid(Monitor *m); 20 | static void nrowgrid(Monitor *m); 21 | static void spiral(Monitor *m); 22 | static void tile(Monitor *m); 23 | /* Internals */ 24 | static void getgaps(Monitor *m, int *oh, int *ov, int *ih, int *iv, unsigned int *nc); 25 | static void getfacts(Monitor *m, int msize, int ssize, float *mf, float *sf, int *mr, int *sr); 26 | static void setgaps(int oh, int ov, int ih, int iv); 27 | 28 | /* Settings */ 29 | #if !PERTAG_PATCH 30 | static int enablegaps = 1; 31 | #endif // PERTAG_PATCH 32 | 33 | void 34 | setgaps(int oh, int ov, int ih, int iv) 35 | { 36 | if (oh < 0) oh = 0; 37 | if (ov < 0) ov = 0; 38 | if (ih < 0) ih = 0; 39 | if (iv < 0) iv = 0; 40 | 41 | selmon->gappoh = oh; 42 | selmon->gappov = ov; 43 | selmon->gappih = ih; 44 | selmon->gappiv = iv; 45 | arrange(selmon); 46 | } 47 | 48 | void 49 | togglegaps(const Arg *arg) 50 | { 51 | #if PERTAG_PATCH 52 | selmon->pertag->enablegaps[selmon->pertag->curtag] = !selmon->pertag->enablegaps[selmon->pertag->curtag]; 53 | #else 54 | enablegaps = !enablegaps; 55 | #endif // PERTAG_PATCH 56 | arrange(NULL); 57 | } 58 | 59 | void 60 | defaultgaps(const Arg *arg) 61 | { 62 | setgaps(gappoh, gappov, gappih, gappiv); 63 | } 64 | 65 | void 66 | incrgaps(const Arg *arg) 67 | { 68 | setgaps( 69 | selmon->gappoh + arg->i, 70 | selmon->gappov + arg->i, 71 | selmon->gappih + arg->i, 72 | selmon->gappiv + arg->i 73 | ); 74 | } 75 | 76 | void 77 | incrigaps(const Arg *arg) 78 | { 79 | setgaps( 80 | selmon->gappoh, 81 | selmon->gappov, 82 | selmon->gappih + arg->i, 83 | selmon->gappiv + arg->i 84 | ); 85 | } 86 | 87 | void 88 | incrogaps(const Arg *arg) 89 | { 90 | setgaps( 91 | selmon->gappoh + arg->i, 92 | selmon->gappov + arg->i, 93 | selmon->gappih, 94 | selmon->gappiv 95 | ); 96 | } 97 | 98 | void 99 | incrohgaps(const Arg *arg) 100 | { 101 | setgaps( 102 | selmon->gappoh + arg->i, 103 | selmon->gappov, 104 | selmon->gappih, 105 | selmon->gappiv 106 | ); 107 | } 108 | 109 | void 110 | incrovgaps(const Arg *arg) 111 | { 112 | setgaps( 113 | selmon->gappoh, 114 | selmon->gappov + arg->i, 115 | selmon->gappih, 116 | selmon->gappiv 117 | ); 118 | } 119 | 120 | void 121 | incrihgaps(const Arg *arg) 122 | { 123 | setgaps( 124 | selmon->gappoh, 125 | selmon->gappov, 126 | selmon->gappih + arg->i, 127 | selmon->gappiv 128 | ); 129 | } 130 | 131 | void 132 | incrivgaps(const Arg *arg) 133 | { 134 | setgaps( 135 | selmon->gappoh, 136 | selmon->gappov, 137 | selmon->gappih, 138 | selmon->gappiv + arg->i 139 | ); 140 | } 141 | 142 | void 143 | getgaps(Monitor *m, int *oh, int *ov, int *ih, int *iv, unsigned int *nc) 144 | { 145 | unsigned int n, oe, ie; 146 | #if PERTAG_PATCH 147 | oe = ie = selmon->pertag->enablegaps[selmon->pertag->curtag]; 148 | #else 149 | oe = ie = enablegaps; 150 | #endif // PERTAG_PATCH 151 | Client *c; 152 | 153 | for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); 154 | if (smartgaps && n == 1) { 155 | oe = 0; // outer gaps disabled when only one client 156 | } 157 | 158 | *oh = m->gappoh*oe; // outer horizontal gap 159 | *ov = m->gappov*oe; // outer vertical gap 160 | *ih = m->gappih*ie; // inner horizontal gap 161 | *iv = m->gappiv*ie; // inner vertical gap 162 | *nc = n; // number of clients 163 | } 164 | 165 | void 166 | getfacts(Monitor *m, int msize, int ssize, float *mf, float *sf, int *mr, int *sr) 167 | { 168 | unsigned int n; 169 | float mfacts, sfacts; 170 | int mtotal = 0, stotal = 0; 171 | Client *c; 172 | 173 | for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); 174 | mfacts = MIN(n, m->nmaster); 175 | sfacts = n - m->nmaster; 176 | 177 | for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) 178 | if (n < m->nmaster) 179 | mtotal += msize / mfacts; 180 | else 181 | stotal += ssize / sfacts; 182 | 183 | *mf = mfacts; // total factor of master area 184 | *sf = sfacts; // total factor of stack area 185 | *mr = msize - mtotal; // the remainder (rest) of pixels after an even master split 186 | *sr = ssize - stotal; // the remainder (rest) of pixels after an even stack split 187 | } 188 | 189 | /*** 190 | * Layouts 191 | */ 192 | 193 | /* 194 | * Bottomstack layout + gaps 195 | * https://dwm.suckless.org/patches/bottomstack/ 196 | */ 197 | static void 198 | bstack(Monitor *m) 199 | { 200 | unsigned int i, n; 201 | int oh, ov, ih, iv; 202 | int mx = 0, my = 0, mh = 0, mw = 0; 203 | int sx = 0, sy = 0, sh = 0, sw = 0; 204 | float mfacts, sfacts; 205 | int mrest, srest; 206 | Client *c; 207 | 208 | getgaps(m, &oh, &ov, &ih, &iv, &n); 209 | if (n == 0) 210 | return; 211 | 212 | sx = mx = m->wx + ov; 213 | sy = my = m->wy + oh; 214 | sh = mh = m->wh - 2*oh; 215 | mw = m->ww - 2*ov - iv * (MIN(n, m->nmaster) - 1); 216 | sw = m->ww - 2*ov - iv * (n - m->nmaster - 1); 217 | 218 | if (m->nmaster && n > m->nmaster) { 219 | sh = (mh - ih) * (1 - m->mfact); 220 | mh = mh - ih - sh; 221 | sx = mx; 222 | sy = my + mh + ih; 223 | } 224 | 225 | getfacts(m, mw, sw, &mfacts, &sfacts, &mrest, &srest); 226 | 227 | for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) { 228 | if (i < m->nmaster) { 229 | resize(c, mx, my, (mw / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), mh - (2*c->bw), 0); 230 | mx += WIDTH(c) + iv; 231 | } else { 232 | resize(c, sx, sy, (sw / sfacts) + ((i - m->nmaster) < srest ? 1 : 0) - (2*c->bw), sh - (2*c->bw), 0); 233 | sx += WIDTH(c) + iv; 234 | } 235 | } 236 | } 237 | 238 | static void 239 | bstackhoriz(Monitor *m) 240 | { 241 | unsigned int i, n; 242 | int oh, ov, ih, iv; 243 | int mx = 0, my = 0, mh = 0, mw = 0; 244 | int sx = 0, sy = 0, sh = 0, sw = 0; 245 | float mfacts, sfacts; 246 | int mrest, srest; 247 | Client *c; 248 | 249 | getgaps(m, &oh, &ov, &ih, &iv, &n); 250 | if (n == 0) 251 | return; 252 | 253 | sx = mx = m->wx + ov; 254 | sy = my = m->wy + oh; 255 | mh = m->wh - 2*oh; 256 | sh = m->wh - 2*oh - ih * (n - m->nmaster - 1); 257 | mw = m->ww - 2*ov - iv * (MIN(n, m->nmaster) - 1); 258 | sw = m->ww - 2*ov; 259 | 260 | if (m->nmaster && n > m->nmaster) { 261 | sh = (mh - ih) * (1 - m->mfact); 262 | mh = mh - ih - sh; 263 | sy = my + mh + ih; 264 | sh = m->wh - mh - 2*oh - ih * (n - m->nmaster); 265 | } 266 | 267 | getfacts(m, mw, sh, &mfacts, &sfacts, &mrest, &srest); 268 | 269 | for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) { 270 | if (i < m->nmaster) { 271 | resize(c, mx, my, (mw / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), mh - (2*c->bw), 0); 272 | mx += WIDTH(c) + iv; 273 | } else { 274 | resize(c, sx, sy, sw - (2*c->bw), (sh / sfacts) + ((i - m->nmaster) < srest ? 1 : 0) - (2*c->bw), 0); 275 | sy += HEIGHT(c) + ih; 276 | } 277 | } 278 | } 279 | 280 | /* 281 | * Centred master layout + gaps 282 | * https://dwm.suckless.org/patches/centeredmaster/ 283 | */ 284 | void 285 | centeredmaster(Monitor *m) 286 | { 287 | unsigned int i, n; 288 | int oh, ov, ih, iv; 289 | int mx = 0, my = 0, mh = 0, mw = 0; 290 | int lx = 0, ly = 0, lw = 0, lh = 0; 291 | int rx = 0, ry = 0, rw = 0, rh = 0; 292 | float mfacts = 0, lfacts = 0, rfacts = 0; 293 | int mtotal = 0, ltotal = 0, rtotal = 0; 294 | int mrest = 0, lrest = 0, rrest = 0; 295 | Client *c; 296 | 297 | getgaps(m, &oh, &ov, &ih, &iv, &n); 298 | if (n == 0) 299 | return; 300 | 301 | /* initialize areas */ 302 | mx = m->wx + ov; 303 | my = m->wy + oh; 304 | mh = m->wh - 2*oh - ih * ((!m->nmaster ? n : MIN(n, m->nmaster)) - 1); 305 | mw = m->ww - 2*ov; 306 | lh = m->wh - 2*oh - ih * (((n - m->nmaster) / 2) - 1); 307 | rh = m->wh - 2*oh - ih * (((n - m->nmaster) / 2) - ((n - m->nmaster) % 2 ? 0 : 1)); 308 | 309 | if (m->nmaster && n > m->nmaster) { 310 | /* go mfact box in the center if more than nmaster clients */ 311 | if (n - m->nmaster > 1) { 312 | /* ||<-S->|<---M--->|<-S->|| */ 313 | mw = (m->ww - 2*ov - 2*iv) * m->mfact; 314 | lw = (m->ww - mw - 2*ov - 2*iv) / 2; 315 | rw = (m->ww - mw - 2*ov - 2*iv) - lw; 316 | mx += lw + iv; 317 | } else { 318 | /* ||<---M--->|<-S->|| */ 319 | mw = (mw - iv) * m->mfact; 320 | lw = 0; 321 | rw = m->ww - mw - iv - 2*ov; 322 | } 323 | lx = m->wx + ov; 324 | ly = m->wy + oh; 325 | rx = mx + mw + iv; 326 | ry = m->wy + oh; 327 | } 328 | 329 | /* calculate facts */ 330 | for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) { 331 | if (!m->nmaster || n < m->nmaster) 332 | mfacts += 1; 333 | else if ((n - m->nmaster) % 2) 334 | lfacts += 1; // total factor of left hand stack area 335 | else 336 | rfacts += 1; // total factor of right hand stack area 337 | } 338 | 339 | for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) 340 | if (!m->nmaster || n < m->nmaster) 341 | mtotal += mh / mfacts; 342 | else if ((n - m->nmaster) % 2) 343 | ltotal += lh / lfacts; 344 | else 345 | rtotal += rh / rfacts; 346 | 347 | mrest = mh - mtotal; 348 | lrest = lh - ltotal; 349 | rrest = rh - rtotal; 350 | 351 | for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) { 352 | if (!m->nmaster || i < m->nmaster) { 353 | /* nmaster clients are stacked vertically, in the center of the screen */ 354 | resize(c, mx, my, mw - (2*c->bw), (mh / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), 0); 355 | my += HEIGHT(c) + ih; 356 | } else { 357 | /* stack clients are stacked vertically */ 358 | if ((i - m->nmaster) % 2 ) { 359 | resize(c, lx, ly, lw - (2*c->bw), (lh / lfacts) + ((i - 2*m->nmaster) < 2*lrest ? 1 : 0) - (2*c->bw), 0); 360 | ly += HEIGHT(c) + ih; 361 | } else { 362 | resize(c, rx, ry, rw - (2*c->bw), (rh / rfacts) + ((i - 2*m->nmaster) < 2*rrest ? 1 : 0) - (2*c->bw), 0); 363 | ry += HEIGHT(c) + ih; 364 | } 365 | } 366 | } 367 | } 368 | 369 | void 370 | centeredfloatingmaster(Monitor *m) 371 | { 372 | unsigned int i, n; 373 | float mfacts, sfacts; 374 | float mivf = 1.0; // master inner vertical gap factor 375 | int oh, ov, ih, iv, mrest, srest; 376 | int mx = 0, my = 0, mh = 0, mw = 0; 377 | int sx = 0, sy = 0, sh = 0, sw = 0; 378 | Client *c; 379 | 380 | getgaps(m, &oh, &ov, &ih, &iv, &n); 381 | if (n == 0) 382 | return; 383 | 384 | sx = mx = m->wx + ov; 385 | sy = my = m->wy + oh; 386 | sh = mh = m->wh - 2*oh; 387 | mw = m->ww - 2*ov - iv*(n - 1); 388 | sw = m->ww - 2*ov - iv*(n - m->nmaster - 1); 389 | 390 | if (m->nmaster && n > m->nmaster) { 391 | mivf = 0.8; 392 | /* go mfact box in the center if more than nmaster clients */ 393 | if (m->ww > m->wh) { 394 | mw = m->ww * m->mfact - iv*mivf*(MIN(n, m->nmaster) - 1); 395 | mh = m->wh * 0.9; 396 | } else { 397 | mw = m->ww * 0.9 - iv*mivf*(MIN(n, m->nmaster) - 1); 398 | mh = m->wh * m->mfact; 399 | } 400 | mx = m->wx + (m->ww - mw) / 2; 401 | my = m->wy + (m->wh - mh - 2*oh) / 2; 402 | 403 | sx = m->wx + ov; 404 | sy = m->wy + oh; 405 | sh = m->wh - 2*oh; 406 | } 407 | 408 | getfacts(m, mw, sw, &mfacts, &sfacts, &mrest, &srest); 409 | 410 | for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) 411 | if (i < m->nmaster) { 412 | /* nmaster clients are stacked horizontally, in the center of the screen */ 413 | resize(c, mx, my, (mw / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), mh - (2*c->bw), 0); 414 | mx += WIDTH(c) + iv*mivf; 415 | } else { 416 | /* stack clients are stacked horizontally */ 417 | resize(c, sx, sy, (sw / sfacts) + ((i - m->nmaster) < srest ? 1 : 0) - (2*c->bw), sh - (2*c->bw), 0); 418 | sx += WIDTH(c) + iv; 419 | } 420 | } 421 | 422 | /* 423 | * Deck layout + gaps 424 | * https://dwm.suckless.org/patches/deck/ 425 | */ 426 | void 427 | deck(Monitor *m) 428 | { 429 | unsigned int i, n; 430 | int oh, ov, ih, iv; 431 | int mx = 0, my = 0, mh = 0, mw = 0; 432 | int sx = 0, sy = 0, sh = 0, sw = 0; 433 | float mfacts, sfacts; 434 | int mrest, srest; 435 | Client *c; 436 | 437 | getgaps(m, &oh, &ov, &ih, &iv, &n); 438 | if (n == 0) 439 | return; 440 | 441 | sx = mx = m->wx + ov; 442 | sy = my = m->wy + oh; 443 | sh = mh = m->wh - 2*oh - ih * (MIN(n, m->nmaster) - 1); 444 | sw = mw = m->ww - 2*ov; 445 | 446 | if (m->nmaster && n > m->nmaster) { 447 | sw = (mw - iv) * (1 - m->mfact); 448 | mw = mw - iv - sw; 449 | sx = mx + mw + iv; 450 | sh = m->wh - 2*oh; 451 | } 452 | 453 | getfacts(m, mh, sh, &mfacts, &sfacts, &mrest, &srest); 454 | 455 | if (n - m->nmaster > 0) /* override layout symbol */ 456 | snprintf(m->ltsymbol, sizeof m->ltsymbol, "D %d", n - m->nmaster); 457 | 458 | for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) 459 | if (i < m->nmaster) { 460 | resize(c, mx, my, mw - (2*c->bw), (mh / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), 0); 461 | my += HEIGHT(c) + ih; 462 | } else { 463 | resize(c, sx, sy, sw - (2*c->bw), sh - (2*c->bw), 0); 464 | } 465 | } 466 | 467 | /* 468 | * Fibonacci layout + gaps 469 | * https://dwm.suckless.org/patches/fibonacci/ 470 | */ 471 | void 472 | fibonacci(Monitor *m, int s) 473 | { 474 | unsigned int i, n; 475 | int nx, ny, nw, nh; 476 | int oh, ov, ih, iv; 477 | int nv, hrest = 0, wrest = 0, r = 1; 478 | Client *c; 479 | 480 | getgaps(m, &oh, &ov, &ih, &iv, &n); 481 | if (n == 0) 482 | return; 483 | 484 | nx = m->wx + ov; 485 | ny = m->wy + oh; 486 | nw = m->ww - 2*ov; 487 | nh = m->wh - 2*oh; 488 | 489 | for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next)) { 490 | if (r) { 491 | if ((i % 2 && (nh - ih) / 2 <= (bh + 2*c->bw)) 492 | || (!(i % 2) && (nw - iv) / 2 <= (bh + 2*c->bw))) { 493 | r = 0; 494 | } 495 | if (r && i < n - 1) { 496 | if (i % 2) { 497 | nv = (nh - ih) / 2; 498 | hrest = nh - 2*nv - ih; 499 | nh = nv; 500 | } else { 501 | nv = (nw - iv) / 2; 502 | wrest = nw - 2*nv - iv; 503 | nw = nv; 504 | } 505 | 506 | if ((i % 4) == 2 && !s) 507 | nx += nw + iv; 508 | else if ((i % 4) == 3 && !s) 509 | ny += nh + ih; 510 | } 511 | 512 | if ((i % 4) == 0) { 513 | if (s) { 514 | ny += nh + ih; 515 | nh += hrest; 516 | } 517 | else { 518 | nh -= hrest; 519 | ny -= nh + ih; 520 | } 521 | } 522 | else if ((i % 4) == 1) { 523 | nx += nw + iv; 524 | nw += wrest; 525 | } 526 | else if ((i % 4) == 2) { 527 | ny += nh + ih; 528 | nh += hrest; 529 | if (i < n - 1) 530 | nw += wrest; 531 | } 532 | else if ((i % 4) == 3) { 533 | if (s) { 534 | nx += nw + iv; 535 | nw -= wrest; 536 | } else { 537 | nw -= wrest; 538 | nx -= nw + iv; 539 | nh += hrest; 540 | } 541 | } 542 | if (i == 0) { 543 | if (n != 1) { 544 | nw = (m->ww - iv - 2*ov) - (m->ww - iv - 2*ov) * (1 - m->mfact); 545 | wrest = 0; 546 | } 547 | ny = m->wy + oh; 548 | } 549 | else if (i == 1) 550 | nw = m->ww - nw - iv - 2*ov; 551 | i++; 552 | } 553 | 554 | resize(c, nx, ny, nw - (2*c->bw), nh - (2*c->bw), False); 555 | } 556 | } 557 | 558 | void 559 | dwindle(Monitor *m) 560 | { 561 | fibonacci(m, 1); 562 | } 563 | 564 | void 565 | spiral(Monitor *m) 566 | { 567 | fibonacci(m, 0); 568 | } 569 | 570 | /* 571 | * Gappless grid layout + gaps (ironically) 572 | * https://dwm.suckless.org/patches/gaplessgrid/ 573 | */ 574 | void 575 | gaplessgrid(Monitor *m) 576 | { 577 | unsigned int i, n; 578 | int x, y, cols, rows, ch, cw, cn, rn, rrest, crest; // counters 579 | int oh, ov, ih, iv; 580 | Client *c; 581 | 582 | getgaps(m, &oh, &ov, &ih, &iv, &n); 583 | if (n == 0) 584 | return; 585 | 586 | /* grid dimensions */ 587 | for (cols = 0; cols <= n/2; cols++) 588 | if (cols*cols >= n) 589 | break; 590 | if (n == 5) /* set layout against the general calculation: not 1:2:2, but 2:3 */ 591 | cols = 2; 592 | rows = n/cols; 593 | cn = rn = 0; // reset column no, row no, client count 594 | 595 | ch = (m->wh - 2*oh - ih * (rows - 1)) / rows; 596 | cw = (m->ww - 2*ov - iv * (cols - 1)) / cols; 597 | rrest = (m->wh - 2*oh - ih * (rows - 1)) - ch * rows; 598 | crest = (m->ww - 2*ov - iv * (cols - 1)) - cw * cols; 599 | x = m->wx + ov; 600 | y = m->wy + oh; 601 | 602 | for (i = 0, c = nexttiled(m->clients); c; i++, c = nexttiled(c->next)) { 603 | if (i/rows + 1 > cols - n%cols) { 604 | rows = n/cols + 1; 605 | ch = (m->wh - 2*oh - ih * (rows - 1)) / rows; 606 | rrest = (m->wh - 2*oh - ih * (rows - 1)) - ch * rows; 607 | } 608 | resize(c, 609 | x, 610 | y + rn*(ch + ih) + MIN(rn, rrest), 611 | cw + (cn < crest ? 1 : 0) - 2*c->bw, 612 | ch + (rn < rrest ? 1 : 0) - 2*c->bw, 613 | 0); 614 | rn++; 615 | if (rn >= rows) { 616 | rn = 0; 617 | x += cw + ih + (cn < crest ? 1 : 0); 618 | cn++; 619 | } 620 | } 621 | } 622 | 623 | /* 624 | * Gridmode layout + gaps 625 | * https://dwm.suckless.org/patches/gridmode/ 626 | */ 627 | void 628 | grid(Monitor *m) 629 | { 630 | unsigned int i, n; 631 | int cx, cy, cw, ch, cc, cr, chrest, cwrest, cols, rows; 632 | int oh, ov, ih, iv; 633 | Client *c; 634 | 635 | getgaps(m, &oh, &ov, &ih, &iv, &n); 636 | 637 | /* grid dimensions */ 638 | for (rows = 0; rows <= n/2; rows++) 639 | if (rows*rows >= n) 640 | break; 641 | cols = (rows && (rows - 1) * rows >= n) ? rows - 1 : rows; 642 | 643 | /* window geoms (cell height/width) */ 644 | ch = (m->wh - 2*oh - ih * (rows - 1)) / (rows ? rows : 1); 645 | cw = (m->ww - 2*ov - iv * (cols - 1)) / (cols ? cols : 1); 646 | chrest = (m->wh - 2*oh - ih * (rows - 1)) - ch * rows; 647 | cwrest = (m->ww - 2*ov - iv * (cols - 1)) - cw * cols; 648 | for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) { 649 | cc = i / rows; 650 | cr = i % rows; 651 | cx = m->wx + ov + cc * (cw + iv) + MIN(cc, cwrest); 652 | cy = m->wy + oh + cr * (ch + ih) + MIN(cr, chrest); 653 | resize(c, cx, cy, cw + (cc < cwrest ? 1 : 0) - 2*c->bw, ch + (cr < chrest ? 1 : 0) - 2*c->bw, False); 654 | } 655 | } 656 | 657 | /* 658 | * Horizontal grid layout + gaps 659 | * https://dwm.suckless.org/patches/horizgrid/ 660 | */ 661 | void 662 | horizgrid(Monitor *m) { 663 | Client *c; 664 | unsigned int n, i; 665 | int oh, ov, ih, iv; 666 | int mx = 0, my = 0, mh = 0, mw = 0; 667 | int sx = 0, sy = 0, sh = 0, sw = 0; 668 | int ntop, nbottom = 1; 669 | float mfacts, sfacts; 670 | int mrest, srest; 671 | 672 | /* Count windows */ 673 | getgaps(m, &oh, &ov, &ih, &iv, &n); 674 | if (n == 0) 675 | return; 676 | 677 | if (n <= 2) 678 | ntop = n; 679 | else { 680 | ntop = n / 2; 681 | nbottom = n - ntop; 682 | } 683 | sx = mx = m->wx + ov; 684 | sy = my = m->wy + oh; 685 | sh = mh = m->wh - 2*oh; 686 | sw = mw = m->ww - 2*ov; 687 | 688 | if (n > ntop) { 689 | sh = (mh - ih) / 2; 690 | mh = mh - ih - sh; 691 | sy = my + mh + ih; 692 | mw = m->ww - 2*ov - iv * (ntop - 1); 693 | sw = m->ww - 2*ov - iv * (nbottom - 1); 694 | } 695 | 696 | mfacts = ntop; 697 | sfacts = nbottom; 698 | mrest = mw - (mw / ntop) * ntop; 699 | srest = sw - (sw / nbottom) * nbottom; 700 | 701 | for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) 702 | if (i < ntop) { 703 | resize(c, mx, my, (mw / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), mh - (2*c->bw), 0); 704 | mx += WIDTH(c) + iv; 705 | } else { 706 | resize(c, sx, sy, (sw / sfacts) + ((i - ntop) < srest ? 1 : 0) - (2*c->bw), sh - (2*c->bw), 0); 707 | sx += WIDTH(c) + iv; 708 | } 709 | } 710 | 711 | /* 712 | * nrowgrid layout + gaps 713 | * https://dwm.suckless.org/patches/nrowgrid/ 714 | */ 715 | void 716 | nrowgrid(Monitor *m) 717 | { 718 | unsigned int n; 719 | int ri = 0, ci = 0; /* counters */ 720 | int oh, ov, ih, iv; /* vanitygap settings */ 721 | unsigned int cx, cy, cw, ch; /* client geometry */ 722 | unsigned int uw = 0, uh = 0, uc = 0; /* utilization trackers */ 723 | unsigned int cols, rows = m->nmaster + 1; 724 | Client *c; 725 | 726 | /* count clients */ 727 | getgaps(m, &oh, &ov, &ih, &iv, &n); 728 | 729 | /* nothing to do here */ 730 | if (n == 0) 731 | return; 732 | 733 | /* force 2 clients to always split vertically */ 734 | if (FORCE_VSPLIT && n == 2) 735 | rows = 1; 736 | 737 | /* never allow empty rows */ 738 | if (n < rows) 739 | rows = n; 740 | 741 | /* define first row */ 742 | cols = n / rows; 743 | uc = cols; 744 | cy = m->wy + oh; 745 | ch = (m->wh - 2*oh - ih*(rows - 1)) / rows; 746 | uh = ch; 747 | 748 | for (c = nexttiled(m->clients); c; c = nexttiled(c->next), ci++) { 749 | if (ci == cols) { 750 | uw = 0; 751 | ci = 0; 752 | ri++; 753 | 754 | /* next row */ 755 | cols = (n - uc) / (rows - ri); 756 | uc += cols; 757 | cy = m->wy + oh + uh + ih; 758 | uh += ch + ih; 759 | } 760 | 761 | cx = m->wx + ov + uw; 762 | cw = (m->ww - 2*ov - uw) / (cols - ci); 763 | uw += cw + iv; 764 | 765 | resize(c, cx, cy, cw - (2*c->bw), ch - (2*c->bw), 0); 766 | } 767 | } 768 | 769 | /* 770 | * Default tile layout + gaps 771 | */ 772 | static void 773 | tile(Monitor *m) 774 | { 775 | unsigned int i, n; 776 | int oh, ov, ih, iv; 777 | int mx = 0, my = 0, mh = 0, mw = 0; 778 | int sx = 0, sy = 0, sh = 0, sw = 0; 779 | float mfacts, sfacts; 780 | int mrest, srest; 781 | Client *c; 782 | 783 | getgaps(m, &oh, &ov, &ih, &iv, &n); 784 | if (n == 0) 785 | return; 786 | 787 | sx = mx = m->wx + ov; 788 | sy = my = m->wy + oh; 789 | mh = m->wh - 2*oh - ih * (MIN(n, m->nmaster) - 1); 790 | sh = m->wh - 2*oh - ih * (n - m->nmaster - 1); 791 | sw = mw = m->ww - 2*ov; 792 | 793 | if (m->nmaster && n > m->nmaster) { 794 | sw = (mw - iv) * (1 - m->mfact); 795 | mw = mw - iv - sw; 796 | sx = mx + mw + iv; 797 | } 798 | 799 | getfacts(m, mh, sh, &mfacts, &sfacts, &mrest, &srest); 800 | 801 | for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) 802 | if (i < m->nmaster) { 803 | resize(c, mx, my, mw - (2*c->bw), (mh / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), 0); 804 | my += HEIGHT(c) + ih; 805 | } else { 806 | resize(c, sx, sy, sw - (2*c->bw), (sh / sfacts) + ((i - m->nmaster) < srest ? 1 : 0) - (2*c->bw), 0); 807 | sy += HEIGHT(c) + ih; 808 | } 809 | } -------------------------------------------------------------------------------- /transient.c: -------------------------------------------------------------------------------- 1 | /* cc transient.c -o transient -lX11 */ 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int main(void) { 9 | Display *d; 10 | Window r, f, t = None; 11 | XSizeHints h; 12 | XEvent e; 13 | 14 | d = XOpenDisplay(NULL); 15 | if (!d) 16 | exit(1); 17 | r = DefaultRootWindow(d); 18 | 19 | f = XCreateSimpleWindow(d, r, 100, 100, 400, 400, 0, 0, 0); 20 | h.min_width = h.max_width = h.min_height = h.max_height = 400; 21 | h.flags = PMinSize | PMaxSize; 22 | XSetWMNormalHints(d, f, &h); 23 | XStoreName(d, f, "floating"); 24 | XMapWindow(d, f); 25 | 26 | XSelectInput(d, f, ExposureMask); 27 | while (1) { 28 | XNextEvent(d, &e); 29 | 30 | if (t == None) { 31 | sleep(5); 32 | t = XCreateSimpleWindow(d, r, 50, 50, 100, 100, 0, 0, 0); 33 | XSetTransientForHint(d, t, f); 34 | XStoreName(d, t, "transient"); 35 | XMapWindow(d, t); 36 | XSelectInput(d, t, ExposureMask); 37 | } 38 | } 39 | 40 | XCloseDisplay(d); 41 | exit(0); 42 | } 43 | -------------------------------------------------------------------------------- /util.c: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "util.h" 8 | 9 | void * 10 | ecalloc(size_t nmemb, size_t size) 11 | { 12 | void *p; 13 | 14 | if (!(p = calloc(nmemb, size))) 15 | die("calloc:"); 16 | return p; 17 | } 18 | 19 | void 20 | die(const char *fmt, ...) { 21 | va_list ap; 22 | 23 | va_start(ap, fmt); 24 | vfprintf(stderr, fmt, ap); 25 | va_end(ap); 26 | 27 | if (fmt[0] && fmt[strlen(fmt)-1] == ':') { 28 | fputc(' ', stderr); 29 | perror(NULL); 30 | } else { 31 | fputc('\n', stderr); 32 | } 33 | 34 | exit(1); 35 | } 36 | -------------------------------------------------------------------------------- /util.h: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | 3 | #define MAX(A, B) ((A) > (B) ? (A) : (B)) 4 | #define MIN(A, B) ((A) < (B) ? (A) : (B)) 5 | #define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B)) 6 | 7 | void die(const char *fmt, ...); 8 | void *ecalloc(size_t nmemb, size_t size); 9 | --------------------------------------------------------------------------------