├── .gitignore ├── LICENSE ├── Makefile ├── README ├── README.md ├── config.def.h ├── config.mk ├── drw.c ├── drw.h ├── dwm.1 ├── dwm.c ├── dwm.png ├── transient.c ├── util.c └── util.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.sh 3 | *.txt 4 | *.out 5 | dwm 6 | config.h 7 | transient 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT/X Consortium License 2 | 3 | © 2006-2019 Anselm R Garbe 4 | © 2006-2009 Jukka Salmi 5 | © 2006-2007 Sander van Dijk 6 | © 2007-2011 Peter Hartlich 7 | © 2007-2009 Szabolcs Nagy 8 | © 2007-2009 Christof Musik 9 | © 2007-2009 Premysl Hruby 10 | © 2007-2008 Enno Gottox Boland 11 | © 2008 Martin Hurton 12 | © 2008 Neale Pickett 13 | © 2009 Mate Nagy 14 | © 2010-2016 Hiltjo Posthuma 15 | © 2010-2012 Connor Lane Smith 16 | © 2011 Christoph Lohmann <20h@r-36.net> 17 | © 2015-2016 Quentin Rameau 18 | © 2015-2016 Eric Pruitt 19 | © 2016-2017 Markus Teich 20 | © 2020-2022 Chris Down 21 | 22 | Permission is hereby granted, free of charge, to any person obtaining a 23 | copy of this software and associated documentation files (the "Software"), 24 | to deal in the Software without restriction, including without limitation 25 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 26 | and/or sell copies of the Software, and to permit persons to whom the 27 | Software is furnished to do so, subject to the following conditions: 28 | 29 | The above copyright notice and this permission notice shall be included in 30 | all copies or substantial portions of the Software. 31 | 32 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 33 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 34 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 35 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 36 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 37 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 38 | DEALINGS IN THE SOFTWARE. 39 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # dwm - dynamic window manager 2 | # See LICENSE file for copyright and license details. 3 | 4 | include config.mk 5 | 6 | SRC = drw.c dwm.c util.c 7 | OBJ = ${SRC:.c=.o} 8 | 9 | all: dwm 10 | 11 | .c.o: 12 | ${CC} -c ${CFLAGS} $< 13 | 14 | ${OBJ}: config.h config.mk 15 | 16 | config.h: 17 | cp config.def.h $@ 18 | 19 | dwm: ${OBJ} 20 | ${CC} -o $@ ${OBJ} ${LDFLAGS} 21 | 22 | clean: 23 | rm -f dwm ${OBJ} dwm-${VERSION}.tar.gz 24 | 25 | dist: clean 26 | mkdir -p dwm-${VERSION} 27 | cp -R LICENSE Makefile README config.def.h config.mk\ 28 | dwm.1 drw.h util.h ${SRC} dwm.png transient.c dwm-${VERSION} 29 | tar -cf dwm-${VERSION}.tar dwm-${VERSION} 30 | gzip dwm-${VERSION}.tar 31 | rm -rf dwm-${VERSION} 32 | 33 | install: all 34 | mkdir -p ${DESTDIR}${PREFIX}/bin 35 | cp -f dwm ${DESTDIR}${PREFIX}/bin 36 | chmod 755 ${DESTDIR}${PREFIX}/bin/dwm 37 | mkdir -p ${DESTDIR}${MANPREFIX}/man1 38 | sed "s/VERSION/${VERSION}/g" < dwm.1 > ${DESTDIR}${MANPREFIX}/man1/dwm.1 39 | chmod 644 ${DESTDIR}${MANPREFIX}/man1/dwm.1 40 | 41 | uninstall: 42 | rm -f ${DESTDIR}${PREFIX}/bin/dwm\ 43 | ${DESTDIR}${MANPREFIX}/man1/dwm.1 44 | 45 | .PHONY: all clean dist install uninstall 46 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | dwm - dynamic window manager 2 | ============================ 3 | dwm is an extremely fast, small, and dynamic window manager for X. 4 | 5 | 6 | Requirements 7 | ------------ 8 | In order to build dwm you need the Xlib header files. 9 | 10 | 11 | Installation 12 | ------------ 13 | Edit config.mk to match your local setup (dwm is installed into 14 | the /usr/local namespace by default). 15 | 16 | Afterwards enter the following command to build and install dwm (if 17 | necessary as root): 18 | 19 | make clean install 20 | 21 | 22 | Running dwm 23 | ----------- 24 | Add the following line to your .xinitrc to start dwm using startx: 25 | 26 | exec dwm 27 | 28 | In order to connect dwm to a specific display, make sure that 29 | the DISPLAY environment variable is set correctly, e.g.: 30 | 31 | DISPLAY=foo.bar:1 exec dwm 32 | 33 | (This will start dwm on display :1 of the host foo.bar.) 34 | 35 | In order to display status info in the bar, you can do something 36 | like this in your .xinitrc: 37 | 38 | while xsetroot -name "`date` `uptime | sed 's/.*,//'`" 39 | do 40 | sleep 1 41 | done & 42 | exec dwm 43 | 44 | 45 | Configuration 46 | ------------- 47 | The configuration of dwm is done by creating a custom config.h 48 | and (re)compiling the source code. 49 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Do not use. Do not patch. 2 | 3 | This fork of dwm adds extra comments and is intended for educational uses only. If you try to patch 4 | this version of dwm then that will most likely fail, more so the relative comments will no longer 5 | apply or be misleading as the underlying code will have changed. 6 | 7 | If you are interested in dwm then get a fresh clone from the https://dwm.suckless.org/ site and use 8 | this as a reference rather than the basis for your build. 9 | 10 | This fork has 0 patches and also does not cover patches. 11 | 12 | 13 | 14 | 15 | 16 | 17 | ```` 18 | ================================================================================================ 19 | Language Files Lines Code Comments Blanks 20 | ================================================================================================ 21 | C 4 8438 2342 5669 427 22 | ------------------------------------------------------------------------------------------------ 23 | dwm.c 7210 1916 4962 332 24 | drw.c 1013 364 571 78 25 | util.c 114 28 77 9 26 | transient.c 101 34 59 8 27 | ------------------------------------------------------------------------------------------------ 28 | C Header 3 414 132 252 30 29 | ------------------------------------------------------------------------------------------------ 30 | config.def.h 313 87 209 17 31 | drw.h 84 39 34 11 32 | util.h 17 6 9 2 33 | ================================================================================================ 34 | Total 7 8852 2474 5921 457 35 | ================================================================================================ 36 | Last tallied: 2024-10-29 23:18:24 37 | ```` 38 | 39 | 40 | -------------------------------------------------------------------------------- /config.def.h: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | 3 | /* This is the default configuration file for dwm. At compile time if a file config.h does not 4 | * exist then the default configuration file config.def.h will be copied to config.h. 5 | * 6 | * The config.h file is the user's personal configuration file that can be tailored to their 7 | * preferences. 8 | * 9 | * The relatively simple relationship between the two files is often misunderstood, however, due 10 | * to using patching tools like patch or git apply and being confused about why changes that were 11 | * applied to the default configuration were not applied to their personal configuration file. 12 | * 13 | * This in turn often lead to misleading recommendations to edit the default configuration file 14 | * instead and to delete config.h prior to compiling. 15 | * 16 | * While this may work in practice the downside of this approach is that: 17 | * - personal configuration is exposed in the default configuration file and 18 | * - personal configuration needs to be committed in version control and 19 | * - changes to the default configuration file opens for more conflicts when applying patches 20 | * as the underlying code has changed 21 | * 22 | * In general it is not complicated to work out what changes a patch makes to the default 23 | * configuration file and to copy those changes into the personal configuration file. Consider 24 | * doing a diff between the two files or simply look at the patch file to work out what has 25 | * changed. 26 | */ 27 | 28 | /* Settings related to appearance. */ 29 | 30 | /* The border pixel determines the size of the window border. */ 31 | static const unsigned int borderpx = 1; /* border pixel of windows */ 32 | /* The snap pixel controls two things: 33 | * - how close to the window area border a window must be before it "snaps" (or docks) against 34 | * that border when moving a floating window using the mouse 35 | * - how far the mouse needs to move before a tiled window "snaps" out to become floating when 36 | * moving or resizing a window using the mouse 37 | */ 38 | static const unsigned int snap = 32; /* snap pixel */ 39 | /* Whether the bar is shown by default on startup or not. */ 40 | static const int showbar = 1; /* 0 means no bar */ 41 | /* Whether the bar is shown at the top or at the bottom of the monitor. */ 42 | static const int topbar = 1; /* 0 means bottom bar */ 43 | /* This defines the primary font and optionally fallback fonts. If a glyph does not exist for a 44 | * character (code point) in the primary font then fallback fonts will be checked. 45 | * If the fallback fonts also do not have that character then system fonts will be checked for the 46 | * missing character. If a system font was found then that font will be added to the list of 47 | * fallback fonts for future reference. 48 | * 49 | * Note that "monospace" is not an actual font, it is an alias for another font which the system 50 | * denotes as the main monospace font. E.g. 51 | * 52 | * $ fc-match monospace 53 | * NotoSansMono-Regular.ttf: "Noto Sans Mono" "Regular" 54 | * 55 | * Use fc-list to find specific fonts to use, e.g. 56 | * 57 | * $ fc-list | grep DejaVu 58 | * /usr/share/fonts/TTF/DejaVuSansMono.ttf: DejaVu Sans Mono:style=Book 59 | * 60 | * Then add the family to the fonts array, e.g. 61 | * 62 | * static const char *fonts[] = { "DejaVu Sans Mono:style=Book:pixelsize=16" }; 63 | * 64 | * A note about pixelsize vs size; 1 pixel (px) is usually assumed to be 1/96th of an inch while 65 | * 1 point (pt) is assumed to be 1/72nd of an inch. Therefore a (point) size of 12 is the same as 66 | * a pixelsize of 16. 67 | * 68 | * For general information on font setup refer to: 69 | * https://wiki.archlinux.org/title/font_configuration 70 | * 71 | * The fonts array here will only be read once when the fonts are initially loaded. 72 | */ 73 | static const char *fonts[] = { "monospace:size=10" }; 74 | /* This specifies the font used for dmenu when called via dwm. */ 75 | static const char dmenufont[] = "monospace:size=10"; 76 | 77 | /* The variables here are merely intended to give a names to the colour codes. 78 | * 79 | * A very common misunderstanding of this is new starters making changes to the colour codes 80 | * directly to change the appearance of the bar. E.g. 81 | * 82 | * static const char col_gray1[] = "#E35A00"; 83 | * 84 | * This will change the background colour for the bar, but that colour will no longer be gray as 85 | * the variable name suggests. 86 | * 87 | * The intention is that you name your own variables, e.g. 88 | * 89 | * static const char col_orange[] = "#E35A00"; 90 | * 91 | * And use that variable in the colors array, e.g. 92 | * 93 | * [SchemeNorm] = { col_gray3, col_orange, col_gray2 }, 94 | * 95 | * It is also possible to have these colour codes inline, e.g. 96 | * 97 | * [SchemeNorm] = { "#bbbbbb", "#E35A00", "#444444" }, 98 | * 99 | * Another approach is to use more generic names like normfgcolor, normbgcolor, etc. and 100 | * leave the colors array as-is when changing colours. This is particularly used in relation to 101 | * Xresources. 102 | */ 103 | static const char col_gray1[] = "#222222"; 104 | static const char col_gray2[] = "#444444"; 105 | static const char col_gray3[] = "#bbbbbb"; 106 | static const char col_gray4[] = "#eeeeee"; 107 | static const char col_cyan[] = "#005577"; 108 | static const char *colors[][3] = { 109 | /* fg bg border */ 110 | [SchemeNorm] = { col_gray3, col_gray1, col_gray2 }, 111 | [SchemeSel] = { col_gray4, col_cyan, col_cyan }, 112 | }; 113 | 114 | /* These define the tag icons (or text) used in the bar while the number of strings in the array 115 | * determine the number of tags being used by dwm. This has an upper limit of 32 tags and anything 116 | * above that will result in a compilation error. */ 117 | static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; 118 | 119 | /* This array controls the client rules which consists of three rule matching filters (the class, 120 | * instance and title) and three rule options (tags, whether the client is floating or not and the 121 | * monitor it is supposed to start on). 122 | * 123 | * Refer to the writeup of the applyrules function for more details on this. 124 | */ 125 | static const Rule rules[] = { 126 | /* xprop(1): 127 | * WM_CLASS(STRING) = instance, class 128 | * WM_NAME(STRING) = title 129 | */ 130 | /* class instance title tags mask isfloating monitor */ 131 | { "Gimp", NULL, NULL, 0, 1, -1 }, 132 | { "Firefox", NULL, NULL, 1 << 8, 0, -1 }, 133 | }; 134 | 135 | /* layout(s) */ 136 | 137 | /* The master / stack factor controls how much of the window area is designated for the master area 138 | * vs the stack area for the tile layout. Refer to the writeup for the setmfact function for more 139 | * details. */ 140 | static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */ 141 | /* The nmaster variable controls the number of clients that are placed in the master area when 142 | * tiled. Refer to the incnmaster function writeup for more details. */ 143 | static const int nmaster = 1; /* number of clients in master area */ 144 | /* This controls whether or not the window manager will respect the size hints of a client window 145 | * when the client is tiled. Refer to the applysizehints function writeup for more details. */ 146 | static const int resizehints = 1; /* 1 means respect size hints in tiled resizals */ 147 | /* The lockfullscreen variable controls whether or not focus is allowed to drift from a fullscreen 148 | * window. Refer to the writeup of the focusstack function for which this feature is isolated. */ 149 | static const int lockfullscreen = 1; /* 1 will force focus on the fullscreen window */ 150 | 151 | /* This array contains the list of available layout options. 152 | * 153 | * When dwm starts the first layout in the list is the default layout and the last layout in the 154 | * array will be set as the previous layout. 155 | * 156 | * The layout symbol will be copied into the monitor's layout symbol when the layout is set. The 157 | * layout function may make changes to the layout symbol, for example the monocle layout that shows 158 | * the number of clients visible. 159 | * 160 | * Refer to the setlayout function writeup for more details. 161 | */ 162 | static const Layout layouts[] = { 163 | /* symbol arrange function */ 164 | { "[]=", tile }, /* first entry is default */ 165 | { "><>", NULL }, /* no layout function means floating behavior */ 166 | { "[M]", monocle }, 167 | }; 168 | 169 | /* key definitions */ 170 | 171 | /* This defines the primary modifier used by dwm. It is a macro which means that at compile time 172 | * all the references to MODKEY below will be replaced with the content of this macro. 173 | * 174 | * To see the available modifiers run the xmodmap command in a terminal, but typically there will 175 | * be a setup along the lines of: 176 | * 177 | * Mod1Mask - the Alt key (and/or Meta key) 178 | * Mod2Mask - Num_Lock 179 | * Mod3Mask - often not used 180 | * Mod4Mask - the Super / Windows key (and/or Hyper key) 181 | * Mod5Mask - ISO_Level3_Shift (AltGr) and/or Mode_switch 182 | * 183 | * Note that you can use xmodmap to change e.g. the right control key to become another 184 | * modifier should you need it. 185 | */ 186 | #define MODKEY Mod1Mask 187 | 188 | /* TAGKEYS is another macro that just avoids having to repeat the same thing nine times 189 | * for each tag. 190 | * 191 | * Consider this being used in the keys array further down. 192 | * 193 | * TAGKEYS( XK_3, 2) 194 | * 195 | * In this case the KEY variable will be XK_3 and the TAG value will be 2. This would then 196 | * expand in the keys array to: 197 | * 198 | * { MODKEY, XK_3, view, {.ui = 1 << 2} }, \ 199 | * { MODKEY|ControlMask, XK_3, toggleview, {.ui = 1 << 2} }, \ 200 | * { MODKEY|ShiftMask, XK_3, tag, {.ui = 1 << 2} }, \ 201 | * { MODKEY|ControlMask|ShiftMask, XK_3, toggletag, {.ui = 1 << 2} }, 202 | * 203 | * Using a macro also makes it easier to change the modifiers used for the functions 204 | * if need be. 205 | */ 206 | #define TAGKEYS(KEY,TAG) \ 207 | { MODKEY, KEY, view, {.ui = 1 << TAG} }, \ 208 | { MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \ 209 | { MODKEY|ShiftMask, KEY, tag, {.ui = 1 << TAG} }, \ 210 | { MODKEY|ControlMask|ShiftMask, KEY, toggletag, {.ui = 1 << TAG} }, 211 | 212 | /* Helper for spawning shell commands in the pre dwm-5.0 fashion */ 213 | #define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } } 214 | 215 | /* commands */ 216 | /* The dmenumon variable holds a reference to the current monitor number, to be passed to dmenu. 217 | * This is quite strictly not necessary as dmenu can work out on its own what monitor has focus. 218 | * Refer to the writeup in the spawn function for more details on this. */ 219 | static char dmenumon[2] = "0"; /* component of dmenucmd, manipulated in spawn() */ 220 | /* The command to launch dmenu. dmenu is a simple program that takes a series of options as input 221 | * and presents these to the user via a menu, when the user selects an option then that option is 222 | * printed to standard out. dmenu is often confused with dmenu_run, which is a shell script that 223 | * looks for executable commands, presents these options to the user, and runs whatever the user 224 | * selected. 225 | * 226 | * In the dmenu command we specify via command line arguments the font and colours that dmenu 227 | * should use. This is to make it appear stylistically similar to the bar in dwm. 228 | */ 229 | static const char *dmenucmd[] = { "dmenu_run", "-m", dmenumon, "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL }; 230 | /* dwm launches st as the terminal of choice by default. */ 231 | static const char *termcmd[] = { "st", NULL }; 232 | 233 | /* The keys array contains user defined keybindings and the functions that said keybindings should 234 | * run. Refer to the grabkeys function for details on how the window manager tells the X server 235 | * it is interested in receiving key press events corresponding to the given key combinations. 236 | * Refer to the keypress function for details on how the window manager interprets the events 237 | * received for the key combinations and calls the designated functions. */ 238 | static const Key keys[] = { 239 | /* modifier key function argument */ 240 | { MODKEY, XK_p, spawn, {.v = dmenucmd } }, 241 | { MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } }, 242 | { MODKEY, XK_b, togglebar, {0} }, 243 | { MODKEY, XK_j, focusstack, {.i = +1 } }, 244 | { MODKEY, XK_k, focusstack, {.i = -1 } }, 245 | { MODKEY, XK_i, incnmaster, {.i = +1 } }, 246 | { MODKEY, XK_d, incnmaster, {.i = -1 } }, 247 | { MODKEY, XK_h, setmfact, {.f = -0.05} }, 248 | { MODKEY, XK_l, setmfact, {.f = +0.05} }, 249 | { MODKEY, XK_Return, zoom, {0} }, 250 | { MODKEY, XK_Tab, view, {0} }, 251 | { MODKEY|ShiftMask, XK_c, killclient, {0} }, 252 | { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, 253 | { MODKEY, XK_f, setlayout, {.v = &layouts[1]} }, 254 | { MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, 255 | { MODKEY, XK_space, setlayout, {0} }, 256 | { MODKEY|ShiftMask, XK_space, togglefloating, {0} }, 257 | { MODKEY, XK_0, view, {.ui = ~0 } }, 258 | { MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } }, 259 | { MODKEY, XK_comma, focusmon, {.i = -1 } }, 260 | { MODKEY, XK_period, focusmon, {.i = +1 } }, 261 | { MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } }, 262 | { MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } }, 263 | TAGKEYS( XK_1, 0) 264 | TAGKEYS( XK_2, 1) 265 | TAGKEYS( XK_3, 2) 266 | TAGKEYS( XK_4, 3) 267 | TAGKEYS( XK_5, 4) 268 | TAGKEYS( XK_6, 5) 269 | TAGKEYS( XK_7, 6) 270 | TAGKEYS( XK_8, 7) 271 | TAGKEYS( XK_9, 8) 272 | { MODKEY|ShiftMask, XK_q, quit, {0} }, 273 | }; 274 | 275 | /* Mouse button definitions. 276 | * The buttons array contains user defined mouse button bindings and the functions that said 277 | * bindings should trigger. Refer to the grabbuttons function for details on how the window manager 278 | * tells the X server it is interested in receiving button press events corresponding to the given 279 | * modifier + button combinations. An event mask of 0 means no modifier. 280 | * Refer to the buttonpress function for details on how the window manager interprets the events 281 | * received for the button presses and calls the designated functions. 282 | * 283 | * What the user clicks on can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, 284 | * or ClkRootWin. 285 | * 286 | * Button1 through Button5 are macros that are defined within the X11 libraries. They simply have 287 | * values of 1 through 5. To bind additional buttons you can either define them yourself or just 288 | * use the button value directly. E.g. 289 | * 290 | * #define Button6 6 291 | * #define Button7 7 292 | * #define Button8 8 293 | * #define Button9 9 294 | * 295 | * { ClkClientWin, MODKEY, Button8, myfunc, {0} }, 296 | * or 297 | * { ClkClientWin, MODKEY, 8, myfunc, {0} }, 298 | **/ 299 | static const Button buttons[] = { 300 | /* click event mask button function argument */ 301 | { ClkLtSymbol, 0, Button1, setlayout, {0} }, 302 | { ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} }, 303 | { ClkWinTitle, 0, Button2, zoom, {0} }, 304 | { ClkStatusText, 0, Button2, spawn, {.v = termcmd } }, 305 | { ClkClientWin, MODKEY, Button1, movemouse, {0} }, 306 | { ClkClientWin, MODKEY, Button2, togglefloating, {0} }, 307 | { ClkClientWin, MODKEY, Button3, resizemouse, {0} }, 308 | { ClkTagBar, 0, Button1, view, {0} }, 309 | { ClkTagBar, 0, Button3, toggleview, {0} }, 310 | { ClkTagBar, MODKEY, Button1, tag, {0} }, 311 | { ClkTagBar, MODKEY, Button3, toggletag, {0} }, 312 | }; 313 | 314 | -------------------------------------------------------------------------------- /config.mk: -------------------------------------------------------------------------------- 1 | # dwm version 2 | VERSION = 6.5 3 | 4 | # Customize below to fit your system 5 | 6 | # paths 7 | PREFIX = /usr/local 8 | MANPREFIX = ${PREFIX}/share/man 9 | 10 | X11INC = /usr/X11R6/include 11 | X11LIB = /usr/X11R6/lib 12 | 13 | # Xinerama, comment if you don't want it 14 | XINERAMALIBS = -lXinerama 15 | XINERAMAFLAGS = -DXINERAMA 16 | 17 | # freetype 18 | FREETYPELIBS = -lfontconfig -lXft 19 | FREETYPEINC = /usr/include/freetype2 20 | # OpenBSD (uncomment) 21 | #FREETYPEINC = ${X11INC}/freetype2 22 | #MANPREFIX = ${PREFIX}/man 23 | 24 | # includes and libs 25 | INCS = -I${X11INC} -I${FREETYPEINC} 26 | LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} 27 | 28 | # flags 29 | CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} 30 | #CFLAGS = -g -std=c99 -pedantic -Wall -O0 ${INCS} ${CPPFLAGS} 31 | CFLAGS = -std=c99 -pedantic -Wall -Wno-deprecated-declarations -Os ${INCS} ${CPPFLAGS} 32 | LDFLAGS = ${LIBS} 33 | 34 | # Solaris 35 | #CFLAGS = -fast ${INCS} -DVERSION=\"${VERSION}\" 36 | #LDFLAGS = ${LIBS} 37 | 38 | # compiler and linker 39 | CC = cc 40 | -------------------------------------------------------------------------------- /drw.c: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | /* The drw library comes from libsl which contains some functionality that is common for several 9 | * suckless project, e.g. dmenu and dwm. 10 | * 11 | * @see https://git.suckless.org/libsl/files.html 12 | */ 13 | 14 | #include "drw.h" 15 | #include "util.h" 16 | 17 | /* UTF-8 is a popular encoding of multi-byte Unicode code-points into eight-bit octets. 18 | * 19 | * The functions below are in relation to working out how many bytes a single UTF-8 character spans 20 | * and is used in the drw_text function when progressing through text one UTF-8 character at a time 21 | * to work out what font has a glyph representing said character. 22 | * 23 | * We are not going to go into detail when it comes to how UTF-8 is decoded - there should be 24 | * better references regarding that elsewhere. 25 | * 26 | * @see https://rosettacode.org/wiki/UTF-8 27 | * @see https://rosettacode.org/wiki/UTF-8_encode_and_decode 28 | */ 29 | #define UTF_INVALID 0xFFFD 30 | 31 | /* This is a function that works out how many bytes a given UTF-8 character spans. 32 | * 33 | * @called_from drw_text to get the number of bytes a multi-byte UTF-8 character uses 34 | * 35 | * Internal call stack: 36 | * ~ -> drawbar -> drw_text -> utf8decode 37 | */ 38 | static int 39 | utf8decode(const char *s_in, long *u, int *err) 40 | { 41 | static const unsigned char lens[] = { 42 | /* 0XXXX */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 43 | /* 10XXX */ 0, 0, 0, 0, 0, 0, 0, 0, /* invalid */ 44 | /* 110XX */ 2, 2, 2, 2, 45 | /* 1110X */ 3, 3, 46 | /* 11110 */ 4, 47 | /* 11111 */ 0, /* invalid */ 48 | }; 49 | static const unsigned char leading_mask[] = { 0x7F, 0x1F, 0x0F, 0x07 }; 50 | static const unsigned int overlong[] = { 0x0, 0x80, 0x0800, 0x10000 }; 51 | 52 | const unsigned char *s = (const unsigned char *)s_in; 53 | int len = lens[*s >> 3]; 54 | *u = UTF_INVALID; 55 | 56 | /* If the byte code has an invalid length as per the lens array, then treat it as an error. */ 57 | *err = 1; 58 | if (len == 0) 59 | return 1; 60 | 61 | /* Otherwise keep looping through bytes until we find the last one. */ 62 | long cp = s[0] & leading_mask[len - 1]; 63 | for (int i = 1; i < len; ++i) { 64 | if (s[i] == '\0' || (s[i] & 0xC0) != 0x80) 65 | return i; 66 | cp = (cp << 6) | (s[i] & 0x3F); 67 | } 68 | 69 | /* If the encoding is invalid then we return the length here. The err variable is still set to 70 | * 1 so it will be treated as an error. */ 71 | 72 | /* out of range, surrogate, overlong encoding */ 73 | if (cp > 0x10FFFF || (cp >> 11) == 0x1B || cp < overlong[len - 1]) 74 | return len; 75 | 76 | /* Clear the error flag and return the length. */ 77 | *err = 0; 78 | *u = cp; 79 | return len; 80 | } 81 | 82 | /* Function to create the drawable, which is an internal structure used to hold the drawable pixel 83 | * map, the graphics context and other references. 84 | * 85 | * @called_from setup to create the drawable 86 | * @calls ecalloc to allocate memory for the drawable 87 | * @calls XCreatePixmap https://tronche.com/gui/x/xlib/pixmap-and-cursor/XCreatePixmap.html 88 | * @calls DefaultDepth https://linux.die.net/man/3/defaultdepth 89 | * @calls XCreateGC https://tronche.com/gui/x/xlib/GC/XCreateGC.html 90 | * @calls XSetLineAttributes https://tronche.com/gui/x/xlib/GC/convenience-functions/XSetLineAttributes.html 91 | * @see http://tinf2.vub.ac.be/~dvermeir/manuals/xlib/GC/manipulating.html 92 | * 93 | * Internal call stack: 94 | * main -> setup -> drw_create 95 | */ 96 | Drw * 97 | drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h) 98 | { 99 | Drw *drw = ecalloc(1, sizeof(Drw)); 100 | 101 | drw->dpy = dpy; 102 | drw->screen = screen; 103 | drw->root = root; 104 | drw->w = w; 105 | drw->h = h; 106 | /* The drawable is a pixel map that is used to draw things that are later copied to the bar 107 | * window. */ 108 | drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen)); 109 | 110 | /* The GC is the graphics context that is used in relation to colours. The graphics context 111 | * is passed to other function such as XSetLineAttributes, XSetForeground, XFillRectangle, 112 | * XDrawRectangle, XCopyArea and XFreeGC. */ 113 | drw->gc = XCreateGC(dpy, root, 0, NULL); 114 | 115 | /* This sets the line attributes for the graphics context. This affects how lines and 116 | * rectangles are drawn. 117 | * 118 | * Noting some details from http://tinf2.vub.ac.be/~dvermeir/manuals/xlib/GC/manipulating.html 119 | * regarding the options. 120 | * 121 | * The line-style defines which sections of a line are drawn: 122 | * LineSolid The full path of the line is drawn. 123 | * LineDoubleDash The full path of the line is drawn, but the even dashes are filled 124 | * differently than the odd dashes with CapButt style used where even 125 | * and odd dashes meet. 126 | * LineOnOffDash Only the even dashes are drawn, and cap-style applies to all internal 127 | * ends of the individual dashes, except CapNotLast is treated as 128 | * CapButt. 129 | * 130 | * The cap-style defines how the endpoints of a path are drawn: 131 | * CapNotLast This is equivalent to CapButt except that for a line-width of zero 132 | * the final endpoint is not drawn. 133 | * CapButt The line is square at the endpoint (perpendicular to the slope of the 134 | * line) with no projection beyond. 135 | * CapRound The line has a circular arc with the diameter equal to the line-width, 136 | * centered on the endpoint. (This is equivalent to CapButt for 137 | * line-width of zero). 138 | * CapProjecting The line is square at the end, but the path continues beyond the 139 | * endpoint for a distance equal to half the line-width. (This is 140 | * equivalent to CapButt for line-width of zero). 141 | * 142 | * The join-style defines how corners are drawn for wide lines: 143 | * JoinMiter The outer edges of two lines extend to meet at an angle. However, 144 | * if the angle is less than 11 degrees, then a JoinBevel join-style is 145 | * used instead. 146 | * JoinRound The corner is a circular arc with the diameter equal to the 147 | * line-width, centered on the joinpoint. 148 | * JoinBevel The corner has CapButt endpoint styles with the triangular notch 149 | * filled. 150 | * 151 | * For a line with coincident endpoints (x1=x2, y1=y2), when the cap-style is applied to 152 | * both endpoints, the semantics depends on the line-width and the cap-style: 153 | * CapNotLast thin The results are device-dependent, but the desired effect is 154 | * that nothing is drawn. 155 | * CapButt thin The results are device-dependent, but the desired effect is 156 | * that a single pixel is drawn. 157 | * CapRound thin The results are the same as for CapButt/thin. 158 | * CapProjecting thin The results are the same as for CapButt/thin. 159 | * CapButt wide Nothing is drawn. 160 | * CapRound wide The closed path is a circle, centered at the endpoint, and 161 | * with the diameter equal to the line-width. 162 | * CapProjecting wide The closed path is a square, aligned with the coordinate axes, 163 | * centered at the endpoint, and with the sides equal to the 164 | * line-width. 165 | */ 166 | XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter); 167 | 168 | return drw; 169 | } 170 | 171 | /* Function that resizes the drawable pixel map. 172 | * 173 | * @called_from drw_resize to change the size of the drawable pixmap when the scren size changes 174 | * @calls XFreePixmap https://tronche.com/gui/x/xlib/pixmap-and-cursor/XFreePixmap.html 175 | * @calls XCreatePixmap https://tronche.com/gui/x/xlib/pixmap-and-cursor/XCreatePixmap.html 176 | * @calls DefaultDepth https://linux.die.net/man/3/defaultdepth 177 | * 178 | * Internal call stack: 179 | * run -> configurenotify -> updategeom -> drw_resize 180 | */ 181 | void 182 | drw_resize(Drw *drw, unsigned int w, unsigned int h) 183 | { 184 | if (!drw) 185 | return; 186 | 187 | /* Set the new height and width for internal reference. */ 188 | drw->w = w; 189 | drw->h = h; 190 | 191 | /* The drawable pixel map cannot simply be resized, so we remove it and create a new one with 192 | * the desired dimensions. */ 193 | if (drw->drawable) 194 | XFreePixmap(drw->dpy, drw->drawable); 195 | drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen)); 196 | } 197 | 198 | /* This frees the drawable and its fonts. 199 | * 200 | * @called_from cleanup to handle the freeing of the drawable 201 | * @calls XFreePixmap https://tronche.com/gui/x/xlib/pixmap-and-cursor/XFreePixmap.html 202 | * @calls XFreeGC https://tronche.com/gui/x/xlib/GC/XFreeGC.html 203 | * @calls drw_fontset_free to free all fonts 204 | * @calls free to free the drawable 205 | * 206 | * Internal call stack: 207 | * main -> cleanup -> drw_free 208 | */ 209 | void 210 | drw_free(Drw *drw) 211 | { 212 | /* Free our Drawable instance. */ 213 | XFreePixmap(drw->dpy, drw->drawable); 214 | /* Free our GC (graphics context). */ 215 | XFreeGC(drw->dpy, drw->gc); 216 | /* Call drw_fontset_free to loop through and free all the fonts that have been loaded. */ 217 | drw_fontset_free(drw->fonts); 218 | /* Finally free our internal Drw structure. */ 219 | free(drw); 220 | } 221 | 222 | /* Function to load a single font. 223 | * 224 | * This function is an implementation detail. Library users should use drw_fontset_create instead. 225 | * 226 | * @called_from drw_fontset_create to load named fonts 227 | * @calls XftFontOpenName https://www.x.org/archive/X11R7.5/doc/man/man3/Xft.3.html 228 | * @calls XftFontClose https://www.x.org/archive/X11R7.5/doc/man/man3/Xft.3.html 229 | * @calls XftFontOpenPattern https://www.x.org/archive/X11R7.5/doc/man/man3/Xft.3.html 230 | * @calls FcPatternGetBool https://www.freedesktop.org/software/fontconfig/fontconfig-devel/fcpatternget-type.html 231 | * @calls die in the event that no font was specified 232 | * @calls ecalloc to allocate memory for the new font 233 | * 234 | * Internal call stack: 235 | * main -> setup -> drw_fontset_create -> xfont_create 236 | */ 237 | static Fnt * 238 | xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern) 239 | { 240 | Fnt *font; 241 | XftFont *xfont = NULL; 242 | FcPattern *pattern = NULL; 243 | 244 | /* Loading font using the font name. */ 245 | if (fontname) { 246 | /* Using the pattern found at font->xfont->pattern does not yield the 247 | * same substitution results as using the pattern returned by 248 | * FcNameParse; using the latter results in the desired fallback 249 | * behaviour whereas the former just results in missing-character 250 | * rectangles being drawn, at least with some fonts. */ 251 | if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fontname))) { 252 | fprintf(stderr, "error, cannot load font from name: '%s'\n", fontname); 253 | return NULL; 254 | } 255 | if (!(pattern = FcNameParse((FcChar8 *) fontname))) { 256 | fprintf(stderr, "error, cannot parse font name to pattern: '%s'\n", fontname); 257 | XftFontClose(drw->dpy, xfont); 258 | return NULL; 259 | } 260 | /* Fall back to loading fonts via a font pattern in the event that a name is not provided. */ 261 | } else if (fontpattern) { 262 | if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) { 263 | fprintf(stderr, "error, cannot load font from pattern.\n"); 264 | return NULL; 265 | } 266 | } else { 267 | die("no font specified."); 268 | } 269 | 270 | /* Allocate memory for the new font. Note that Fnt is an internal structure defined in drw.h. */ 271 | font = ecalloc(1, sizeof(Fnt)); 272 | font->xfont = xfont; 273 | font->pattern = pattern; 274 | 275 | /* The font height is the distance between the fonts' ascent and descent values. 276 | * 277 | * The ascent value is the recommended distance above the baseline for single spaced text while 278 | * the descent value is the recommended distance below the baseline. 279 | * 280 | * Refer to the following stackoverflow topic that demonstrates this with an image. 281 | * https://stackoverflow.com/questions/27631736/meaning-of-top-ascent-baseline-descent-bottom-and-leading-in-androids-font 282 | */ 283 | font->h = xfont->ascent + xfont->descent; 284 | /* Set the display for the font. Used when making calls to XftTextExtentsUtf8 and 285 | * XftFontClose. */ 286 | font->dpy = drw->dpy; 287 | 288 | return font; 289 | } 290 | 291 | /* Function to free a font. 292 | * 293 | * @called_from drw_text to free a loaded font that was not desireable 294 | * @called_from drw_fontset_free to free all fonts 295 | * @calls FcPatternDestroy https://www.freedesktop.org/software/fontconfig/fontconfig-devel/fcpatterndestroy.html 296 | * @calls XftFontClose https://www.x.org/archive/X11R7.5/doc/man/man3/Xft.3.html 297 | * @calls free to release the memory used by the font 298 | * 299 | * Internal call stack: 300 | * ~ -> drawbar -> drw_text -> xfont_free 301 | * ~ -> drawbar -> drw_fontset_getwidth -> drw_text -> xfont_free 302 | * run -> buttonpress -> drw_fontset_getwidth -> drw_text -> xfont_free 303 | * main -> cleanup -> drw_free -> drw_fontset_free -> xfont_free 304 | */ 305 | static void 306 | xfont_free(Fnt *font) 307 | { 308 | if (!font) 309 | return; 310 | /* Clear the font pattern if it exists. */ 311 | if (font->pattern) 312 | FcPatternDestroy(font->pattern); 313 | /* Close the font - marks the font as unused for the display. */ 314 | XftFontClose(font->dpy, font->xfont); 315 | /* Release the memory used for the font. */ 316 | free(font); 317 | } 318 | 319 | /* Function to loop through and load fonts based on names in an array. 320 | * 321 | * @called_from setup to load the fonts defined in the fonts array in the configuration file 322 | * @calls xfont_create to load each font 323 | * 324 | * Internal call stack: 325 | * main -> setup -> drw_fontset_create 326 | */ 327 | Fnt* 328 | drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount) 329 | { 330 | Fnt *cur, *ret = NULL; 331 | size_t i; 332 | 333 | /* General guard, should never happen in practice. */ 334 | if (!drw || !fonts) 335 | return NULL; 336 | 337 | /* Loop through all fonts. Note how the loop starts at 1 rather than 0, this has to do with 338 | * that we are loading the last font first, then working our way upwards to the first font. 339 | * This is also reflected in how we add the new font to the head of the linked list of fonts 340 | * used. */ 341 | for (i = 1; i <= fontcount; i++) { 342 | /* Try to load the given font */ 343 | if ((cur = xfont_create(drw, fonts[fontcount - i], NULL))) { 344 | /* If the font could be loaded insert this new font at the head of the linked list of 345 | * fonts. */ 346 | cur->next = ret; 347 | ret = cur; 348 | } 349 | } 350 | 351 | /* Return the linked list of fonts created. */ 352 | return (drw->fonts = ret); 353 | } 354 | 355 | /* Function to recursively free fonts in an array. 356 | * 357 | * @called_from drw_free to free the all fonts 358 | * @called_from drw_fontset_free (itself) to free the remaining fonts 359 | * @calls drw_fontset_free (itself) to free the remaining fonts 360 | * @calls xfont_free to release the memory used for this font 361 | * 362 | * Internal call stack: 363 | * main -> cleanup -> drw_free -> drw_fontset_free -> drw_fontset_free 364 | * ^__________________/ 365 | */ 366 | void 367 | drw_fontset_free(Fnt *font) 368 | { 369 | /* Only enter if we have any more fonts to free. */ 370 | if (font) { 371 | /* Calls drw_fontset_free recursively. */ 372 | drw_fontset_free(font->next); 373 | /* Release the memory used by the font. */ 374 | xfont_free(font); 375 | } 376 | } 377 | 378 | /* Wrapper function to create an XftColor based on a given name. 379 | * 380 | * @called_from setup to create the various colour schemes 381 | * @calls XftColorAllocName https://www.x.org/archive/X11R7.5/doc/man/man3/Xft.3.html 382 | * @calls DefaultVisual https://linux.die.net/man/3/defaultvisual 383 | * @calls DefaultColormap https://linux.die.net/man/3/defaultcolormap 384 | * @calls die in the event that a colour could not be created 385 | * 386 | * Internal call stack: 387 | * main -> setup -> drw_scm_create -> drw_clr_create 388 | */ 389 | void 390 | drw_clr_create(Drw *drw, Clr *dest, const char *clrname) 391 | { 392 | if (!drw || !dest || !clrname) 393 | return; 394 | 395 | /* This creates the colour. The colour name can be on the form of "#3399ab", "#ccc" or "red". */ 396 | if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen), 397 | DefaultColormap(drw->dpy, drw->screen), 398 | clrname, dest)) 399 | die("error, cannot allocate color '%s'", clrname); 400 | 401 | /* It is worth noting that the created colours do not have anything set for the alpha channel 402 | * which is used in the context of compositors and transparency. As such using a compositor 403 | * to apply transparency will also affect the window borders. A workaround for this is to 404 | * remove transparency from the created colours by setting the alpha value to be 100% opaque. 405 | * 406 | * dest->pixel |= 0xff << 24; 407 | */ 408 | } 409 | 410 | /* Wrapper to create color schemes. The caller has to call free(3) on the 411 | * returned color scheme when done using it. 412 | * 413 | * A colour scheme contains multiple (XftColor) colours. In dwm a colour scheme contains by default 414 | * three colours; the forground colour, the background colour and the border colour. 415 | * 416 | * @called_from setup to create the various colour schemes 417 | * @calls drw_clr_create to create the individual colours 418 | * @calls ecalloc to allocate memory for the colour scheme 419 | * 420 | * Internal call stack: 421 | * main -> setup -> drw_scm_create 422 | */ 423 | Clr * 424 | drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount) 425 | { 426 | size_t i; 427 | Clr *ret; 428 | 429 | /* Need at least two colors for a scheme. Why that restriction exists is not clear, but 430 | * presumably if you only needed a single colour then you would just call drw_clr_create 431 | * and use the returned colour directly. The rest are merely guards in case something is 432 | * not as it should be. The last ecalloc allocates memory to hold the number of colours in 433 | * the colour scheme. */ 434 | if (!drw || !clrnames || clrcount < 2 || !(ret = ecalloc(clrcount, sizeof(XftColor)))) 435 | return NULL; 436 | 437 | /* Loop through all the colours and create them while storing them in the XftColor array 438 | * (which represents our colour scheme). */ 439 | for (i = 0; i < clrcount; i++) 440 | drw_clr_create(drw, &ret[i], clrnames[i]); 441 | /* Return the created colour scheme. */ 442 | return ret; 443 | } 444 | 445 | /* Function to set or change the given font set. 446 | * 447 | * This function is not used in dwm. 448 | */ 449 | void 450 | drw_setfontset(Drw *drw, Fnt *set) 451 | { 452 | if (drw) 453 | drw->fonts = set; 454 | } 455 | 456 | /* Function to set the next colour scheme to use when drawing text or othwerwise. 457 | * 458 | * @called_from drawbar to change colours for different segments of the bar 459 | * 460 | * Internal call stack: 461 | * ~ -> drawbar -> drw_setscheme 462 | */ 463 | void 464 | drw_setscheme(Drw *drw, Clr *scm) 465 | { 466 | /* General guard in the unlikely event that drw would be NULL. */ 467 | if (drw) 468 | /* Set the scheme in our drawable struct. */ 469 | drw->scheme = scm; 470 | } 471 | 472 | /* Function to draw a filled or hollow rectangle. 473 | * 474 | * @called_from drawbar to draw rectangles on the bar 475 | * @calls XSetForeground https://tronche.com/gui/x/xlib/GC/convenience-functions/XSetForeground.html 476 | * @calls XFillRectangle https://tronche.com/gui/x/xlib/graphics/filling-areas/XFillRectangle.html 477 | * @calls XDrawRectangle https://tronche.com/gui/x/xlib/graphics/drawing/XDrawRectangle.html 478 | * 479 | * Internal call stack: 480 | * ~ -> drawbar -> drw_rect 481 | */ 482 | void 483 | drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert) 484 | { 485 | /* General guard, should never happen in practice. */ 486 | if (!drw || !drw->scheme) 487 | return; 488 | 489 | /* This sets the foreground colour to the current colour scheme's foreground pixel. If the 490 | * colours are inverted then the current colour scheme's background pixel is used instead. */ 491 | XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme[ColBg].pixel : drw->scheme[ColFg].pixel); 492 | /* If the rectangle should be solid then we call the XFillRectangle function to draw it. */ 493 | if (filled) 494 | XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); 495 | /* Otherwise we call the XDrawRectangle function to draw a hollow rectangle. */ 496 | else 497 | XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w - 1, h - 1); 498 | } 499 | 500 | /* This function handles the drawing of text as well as calculating the width of text when called 501 | * via drw_fontset_getwidth. 502 | * 503 | * The general flow is to: 504 | * - work out how many bytes the next multi-byte UTF-8 character spans 505 | * - find a font that has a glyph for that character, this may involve searching for and loading 506 | * additional fonts 507 | * - work out the width of the character when drawn with that font 508 | * - draw the as many characters as possible using the specific font, but fall back to the 509 | * primary font if that has a glyph for the next character 510 | * - if the text is too long to be shown, then end the text by drawing an ellipsis (...) 511 | * 512 | * @called_from drawbar to draw text on the bar (e.g. status, tags, layout symbol) 513 | * @called_from drw_fontset_getwidth to calculate the width of text 514 | * @called_from drw_fontset_getwidth_clamp to calculate the width of text up to a certain value 515 | * @called_from drw_text itself to draw the ellipsis if the text is too long to fit 516 | * @calls FcCharSetCreate https://www.freedesktop.org/software/fontconfig/fontconfig-devel/fccharsetcreate.html 517 | * @calls FcCharSetAddChar https://www.freedesktop.org/software/fontconfig/fontconfig-devel/fccharsetaddchar.html 518 | * @calls FcPatternDuplicate https://www.freedesktop.org/software/fontconfig/fontconfig-devel/fcpatternduplicate.html 519 | * @calls FcPatternAddCharSet https://www.freedesktop.org/software/fontconfig/fontconfig-devel/fcpatternadd-type.html 520 | * @calls FcPatternAddBool https://www.freedesktop.org/software/fontconfig/fontconfig-devel/fcpatternadd-type.html 521 | * @calls FcConfigSubstitute https://www.freedesktop.org/software/fontconfig/fontconfig-devel/fcconfigsubstitute.html 522 | * @calls FcDefaultSubstitute https://www.freedesktop.org/software/fontconfig/fontconfig-devel/fcdefaultsubstitute.html 523 | * @calls FcCharSetDestroy https://www.freedesktop.org/software/fontconfig/fontconfig-devel/fccharsetdestroy.html 524 | * @calls FcPatternDestroy https://www.freedesktop.org/software/fontconfig/fontconfig-devel/fcpatterndestroy.html 525 | * @calls XSetForeground https://tronche.com/gui/x/xlib/GC/convenience-functions/XSetForeground.html 526 | * @calls XFillRectangle https://tronche.com/gui/x/xlib/graphics/filling-areas/XFillRectangle.html 527 | * @calls XftDrawCreate https://www.x.org/archive/X11R7.5/doc/man/man3/Xft.3.html 528 | * @calls XftCharExists https://www.x.org/archive/X11R7.5/doc/man/man3/Xft.3.html 529 | * @calls XftFontMatch https://www.x.org/archive/X11R7.5/doc/man/man3/Xft.3.html 530 | * @calls XftDrawDestroy https://www.x.org/archive/X11R7.5/doc/man/man3/Xft.3.html 531 | * @calls XftDrawStringUtf8 https://www.x.org/archive/X11R7.5/doc/man/man3/Xft.3.html 532 | * @calls DefaultVisual https://linux.die.net/man/3/defaultvisual 533 | * @calls DefaultColormap https://linux.die.net/man/3/defaultcolormap 534 | * @calls xfont_create in the event additional fallback fonts need to be loaded 535 | * @calls xfont_free if a loaded font did not contain the desired glyph 536 | * @calls utf8decode to work out the number of bytes in a multi-byte UTF-8 character 537 | * @calls drw_font_getexts to work out the width of a character when drawn using a specific font 538 | * @calls drw_fontset_getwidth to work out the width of the ellipsis 539 | * @calls drw_text to draw the ellipsis 540 | * @calls die in the event that something goes wrong loading a font 541 | * 542 | * Internal call stack: 543 | * ~ -> drawbar -> drw_text 544 | * ~ -> drawbar -> drw_fontset_getwidth -> drw_text 545 | * run -> buttonpress -> drw_fontset_getwidth -> drw_text 546 | */ 547 | int 548 | drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert) 549 | { 550 | /* Initialising a series of variables: 551 | * ty - text y position, used in the context of fallback fonts that may have a different 552 | * height to the primary font 553 | * ellipsis_x - the position that the ellipsis will be drawn at, if needed 554 | * ellipsis_w - the remaining space available to draw the ellipsis 555 | * ellipsis_len - keeps track of the number of bytes in the multi-byte UTF-8 character that 556 | * is drawn at the ellipsis_x position 557 | * tmpw - temporary width holding the width of the last UTF-8 character checked 558 | * ew - represents the extent width, as in width of a UTF-8 character 559 | * d - holds the XftDraw structure used in the event that text is to be drawn 560 | * hash, h0, h1 - these hash variables are used to make a decoded UTF-8 code point fit in 561 | * the nomatches array for lookup purposes 562 | * usedfont - the currently used font 563 | * curfont - the font that was found to hold a glyph for the given UTF-8 character 564 | * nextfont - the next font to use 565 | * utf8strlen - the number of continuous bytes that can be drawn together with the used font 566 | * utf8charlen - the number of bytes in the current multi-byte UTF-8 character 567 | * utf8err - indicates whether a UTF-8 code point could be decoded or not 568 | * render - whether to render text or just calculate the width of the text 569 | * utf8codepoint - represents the code point for the current UTF-8 character 570 | * utf8str - used to hold the position in the text string 571 | * fccharset - used in the context of loading additional fallback fonts 572 | * fcpattern - used in the context of loading additional fallback fonts 573 | * match - used in the context of loading additional fallback fonts 574 | * result - used in the context of loading additional fallback fonts 575 | * charexists - internal flag indicating whether a character exists within the current fonts 576 | * overflow - internal flag indicating whether the text is too long to fit in the given width 577 | */ 578 | int ty, ellipsis_x = 0; 579 | unsigned int tmpw, ew, ellipsis_w = 0, ellipsis_len, hash, h0, h1; 580 | XftDraw *d = NULL; 581 | Fnt *usedfont, *curfont, *nextfont; 582 | int utf8strlen, utf8charlen, utf8err, render = x || y || w || h; 583 | long utf8codepoint = 0; 584 | const char *utf8str; 585 | FcCharSet *fccharset; 586 | FcPattern *fcpattern; 587 | FcPattern *match; 588 | XftResult result; 589 | int charexists = 0, overflow = 0; 590 | /* Keep track of a couple codepoints for which we have no match. This is a performance 591 | * optimisation to avoid spending wasteful time searching for a font that does not exit over 592 | * and over again. Here we reserve space to hold up to 128 known code points (characters) for 593 | * which we know there is no font that has a glyph for that character. 594 | * The ellipsis_width variable holds the ellipsis' rendered width (in pixels). 595 | * The invalid_width variable holds the rendered width (in pixels) of an invalid character 596 | * representation. 597 | * These variables are static so that we will only ever have to calculate them once. 598 | */ 599 | static unsigned int nomatches[128], ellipsis_width, invalid_width; 600 | static const char invalid[] = "�"; 601 | 602 | /* General guard to prevent anything bad from happening in the event that this function is 603 | * called before we have everything we need set up (like colour schemes, fonts, etc.). */ 604 | if (!drw || (render && (!drw->scheme || !w)) || !text || !drw->fonts) 605 | return 0; 606 | 607 | /* If we are only calculating the width of the text then do not impose any limit on the width, 608 | * unless this is called from drw_fontset_getwidth_clamp which passes in a maximum width via 609 | * the invert variable. In this context the meaning of the invert parameter is overloaded in 610 | * the sense that it is used for something other than what it is intended for, but the invert 611 | * variable is otherwise not used when calculating the width of a given text and the 612 | * overloading is limited to the one line below. */ 613 | if (!render) { 614 | w = invert ? invert : ~invert; 615 | } else { 616 | /* If we are rendering the text then the first thing we do is to set the foreground color 617 | * and drawing a rectangle covering the width of the text. This is to clear anything that 618 | * may have been drawn before. */ 619 | XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel); 620 | XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); 621 | 622 | /* Cover for an edge case where the remaining width is less than the left padding in which 623 | * we just skip to the end. Without this it is possible to end up with an unsigned integer 624 | * underflow (i.e. w ending up very very large) and text potentially being overwritten. */ 625 | if (w < lpad) 626 | return x + w; 627 | 628 | /* We prepare the XftDraw structure that will be used to draw the text later. */ 629 | d = XftDrawCreate(drw->dpy, drw->drawable, 630 | DefaultVisual(drw->dpy, drw->screen), 631 | DefaultColormap(drw->dpy, drw->screen)); 632 | /* Apply the left padding to the starting position of the text. Reduce the width 633 | * accordingly. */ 634 | x += lpad; 635 | w -= lpad; 636 | } 637 | 638 | /* Start with the primary font. */ 639 | usedfont = drw->fonts; 640 | /* If this is the first time we are actually drawing text, then ellipsis_width will be 0 and 641 | * we call drw_fontset_getwidth to get the actual width of the ellipsis ("...") text. The 642 | * ellipsis_width variable is static which means that it will keep this value for all future 643 | * calls to this function, which in turn means that the width of the ellipsis will only be 644 | * calculated once for the as long as the program runs. */ 645 | if (!ellipsis_width && render) 646 | ellipsis_width = drw_fontset_getwidth(drw, "..."); 647 | /* As above here we calculate the rendered width of an invalid character (in pixels). */ 648 | if (!invalid_width && render) 649 | invalid_width = drw_fontset_getwidth(drw, invalid); 650 | 651 | /* Keep doing the below until we run out of text or we run out of space to draw the text. */ 652 | while (1) { 653 | /* This is the first loop or we have previously drawn a character or a set of characters. 654 | * Reset the various width variables back to 0. */ 655 | ew = ellipsis_len = utf8err = utf8charlen = utf8strlen = 0; 656 | /* We set utf8str to be the text (or what is left of it). This holds a reference to where 657 | * we are in the text string before we enter the while loop below. This is later used when 658 | * drawing the text after we have exited the while loop which traverses through the text 659 | * array. */ 660 | utf8str = text; 661 | nextfont = NULL; 662 | 663 | /* Keep doing this as long as we have text or we break out of the loop due to overflowing, 664 | * the character does not exist or we need to switch to another font. */ 665 | while (*text) { 666 | /* We call utf8decode to work out how many bytes the multi-byte UTF-8 character at the 667 | * current position holds. The variable could have been named utf8bytelen, but it is 668 | * named as it is due to that text is stored in an array of char and a char in c holds 669 | * the same amount of data as one byte. The call to utf8decode also gives us the 670 | * utf8codepoint which represents the actual Unicode character. */ 671 | utf8charlen = utf8decode(text, &utf8codepoint, &utf8err); 672 | /* Now we loop through all fonts, starting at the primary font. Notably this means that 673 | * the primary font is always going to take precedence over fallback fonts provided that 674 | * the primary font has a glyph for the current character. */ 675 | for (curfont = drw->fonts; curfont; curfont = curfont->next) { 676 | /* Then we check if the current font has a glyph for that code point. If the glyph 677 | * does not exist then we check the next font. It is possible that none of the 678 | * loaded fonts has the specific glyph, in which case nextfont will remain with 679 | * the value of NULL. */ 680 | charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint); 681 | 682 | /* If the current font has the glyph then */ 683 | if (charexists) { 684 | /* Call drw_font_getexts to get the width of the character (in pixels) when 685 | * drawn using the current font (at the size the font was loaded with). Store 686 | * the result in the temporary width variable (tmpw). */ 687 | drw_font_getexts(curfont, text, utf8charlen, &tmpw, NULL); 688 | 689 | /* This ellipsis check stores the position, width and byte length of the 690 | * previous character that was processed. This is in order to know where the 691 | * ellipsis should be drawn in the event that the text is too long to fit in 692 | * the available width. The extent width (ew) variable is set further down for 693 | * the current character. This should make more sense after having read through 694 | * the whole for loop at least once. */ 695 | if (ew + ellipsis_width <= w) { 696 | /* Keep track where the ellipsis still fits. */ 697 | ellipsis_x = x + ew; 698 | ellipsis_w = w - ew; 699 | ellipsis_len = utf8strlen; 700 | } 701 | 702 | /* If the addition of the current UTF-8 character would result in overflow, as 703 | * in taking more space than we have available to us, then ... */ 704 | if (ew + tmpw > w) { 705 | /* ... we set the overflow flag to 1 so that we break out of this and the 706 | * outer while loop. */ 707 | overflow = 1; 708 | /* If we are not rendering anything then this drw_text call must have been 709 | * made from drw_fontset_getwidth_clamp which is interested in the width 710 | * after the overflow. As in how much more space does this take. As such 711 | * we increment the x position with the width of the current character as 712 | * well. */ 713 | if (!render) 714 | x += tmpw; 715 | /* Otherwise revert utf8strlen back to the last character where the 716 | * ellipsis would still fit. */ 717 | else 718 | utf8strlen = ellipsis_len; 719 | } else if (curfont == usedfont) { 720 | /* If the found font is the same as what we used for the previous character 721 | * then we can draw these consecutively.. This moves the text cursor to the 722 | * next UTF-8 character by moving forward the number of bytes the current 723 | * character has. */ 724 | text += utf8charlen; 725 | 726 | /* Increment running total of number of characters that can be drawn and 727 | * the combined extent width, but only in the event that there were no 728 | * errors when decoding the UTF-8 character. */ 729 | utf8strlen += utf8err ? 0 : utf8charlen; 730 | ew += utf8err ? 0 : tmpw; 731 | } else { 732 | /* Oh, we are in a situation where the next UTF-8 character to be drawn is 733 | * for another font. Before we can do that though we need to draw the text 734 | * we have already processed that we know use the same font. We set the 735 | * nextfont to the current font so that we can switch to that after having 736 | * drawn the text up until this point. */ 737 | nextfont = curfont; 738 | } 739 | /* In any event we have found the font that has the desired glyph, so we do not 740 | * need to loop through any more fonts. Thus we break here. */ 741 | break; 742 | } 743 | } 744 | 745 | /* If we have exceeded the available width (overflow), or the character does not exist 746 | * in any of the fonts loaded, or in the case that we need to change to another font 747 | * then we need to break out of the while loop. */ 748 | if (overflow || !charexists || nextfont || utf8err) 749 | break; 750 | /* Otherwise we set charexists to 0. */ 751 | else 752 | charexists = 0; 753 | } 754 | 755 | /* If we have text to draw then */ 756 | if (utf8strlen) { 757 | /* and if we are actually drawing the text then */ 758 | if (render) { 759 | /* Calculate the text y position relative to the used font. When using fallback 760 | * fonts then these may have a different size than the primary font. The 761 | * calculation aims to center the font text in the middle of the defined height 762 | * for the text. */ 763 | ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent; 764 | /* This is the bit that actually draws text up until this point using the currently 765 | * used font. It will use the current scheme's foreground colour for the text unless 766 | * invert is true, in which case the background colour is used for the text. */ 767 | XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg], 768 | usedfont->xfont, x, ty, (XftChar8 *)utf8str, utf8strlen); 769 | } 770 | /* Move the x position (or cursor) up the width of the text drawn. Reduce the remaining 771 | * width accordingly. */ 772 | x += ew; 773 | w -= ew; 774 | } 775 | 776 | /* If we have encountered any bad UTF-8 encoding then print the designated "invalid" 777 | * character in place of the decoded character. */ 778 | if (utf8err && (!render || invalid_width < w)) { 779 | if (render) 780 | drw_text(drw, x, y, w, h, 0, invalid, invert); 781 | x += invalid_width; 782 | w -= invalid_width; 783 | } 784 | 785 | /* If we are drawing text and we have run out of space, then draw the ellipsis at the last 786 | * known position where we know that the ellipsis will still fit. The ellipsis_w variable 787 | * may be larger than the ellipsis_width variable, this is because we will want to make 788 | * sure that the entire remaining area of text is cleared as well. */ 789 | if (render && overflow) 790 | drw_text(drw, ellipsis_x, y, ellipsis_w, h, 0, "...", invert); 791 | 792 | /* If we have run out of text or if we have run out of space to draw the text, then break 793 | * out of the main while loop, finishing the drw_text call. */ 794 | if (!*text || overflow) { 795 | break; 796 | /* Otherwise if we have a next font to use then we set usedfont to be the next font. We 797 | * also make sure to set charexists to 0 so that we don't skip the call to XftCharExists 798 | * when entering the for loop that goes through all the fonts again. */ 799 | } else if (nextfont) { 800 | charexists = 0; 801 | usedfont = nextfont; 802 | } else { 803 | /* Regardless of whether or not a fallback font is found, the 804 | * character must be drawn. */ 805 | charexists = 1; 806 | 807 | /* Here we go through known cases of code points that have no matching fonts. This is 808 | * a performance optimisation that voids the need to waste time searching for a font 809 | * that offers a certain glyph and there are no such font in the system. */ 810 | 811 | /* This hashes the UTF-8 code point in order for it to fit and be looked up in the 812 | * nomatches array. */ 813 | hash = (unsigned int)utf8codepoint; 814 | hash = ((hash >> 16) ^ hash) * 0x21F0AAAD; 815 | hash = ((hash >> 15) ^ hash) * 0xD35A2D97; 816 | h0 = ((hash >> 15) ^ hash) % LENGTH(nomatches); 817 | h1 = (hash >> 17) % LENGTH(nomatches); 818 | /* avoid expensive XftFontMatch call when we know we won't find a match */ 819 | if (nomatches[h0] == utf8codepoint || nomatches[h1] == utf8codepoint) 820 | /* Here we have a goto statement that skips most of the code below. */ 821 | goto no_match; 822 | 823 | /* Here we create a character set and add our code point to that character set. 824 | * We then use this to make the Xft library search for a font that matches the 825 | * criteria that we set up. Documentation on fontconfig is relatively scarce, but 826 | * most if not all Fc* functions should have their respective man pages. */ 827 | fccharset = FcCharSetCreate(); 828 | FcCharSetAddChar(fccharset, utf8codepoint); 829 | 830 | if (!drw->fonts->pattern) { 831 | /* Refer to the comment in xfont_create for more information. */ 832 | die("the first font in the cache must be loaded from a font string."); 833 | } 834 | 835 | fcpattern = FcPatternDuplicate(drw->fonts->pattern); 836 | FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset); 837 | FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue); 838 | 839 | FcConfigSubstitute(NULL, fcpattern, FcMatchPattern); 840 | FcDefaultSubstitute(fcpattern); 841 | 842 | /* The matching font, if any. */ 843 | match = XftFontMatch(drw->dpy, drw->screen, fcpattern, &result); 844 | 845 | /* Cleanup, free the character set and fontconfig pattern. */ 846 | FcCharSetDestroy(fccharset); 847 | FcPatternDestroy(fcpattern); 848 | 849 | /* If we did find a matching font then */ 850 | if (match) { 851 | /* Set the used font to be this new font. */ 852 | usedfont = xfont_create(drw, NULL, match); 853 | /* Sanity check that the character does exist for this newly loaded font. */ 854 | if (usedfont && XftCharExists(drw->dpy, usedfont->xfont, utf8codepoint)) { 855 | /* If the character does exist in the newly loaded font then all good. 856 | * Here we loop through and append the newly loaded font at the end of the 857 | * linked list of loaded fonts. */ 858 | for (curfont = drw->fonts; curfont->next; curfont = curfont->next) 859 | ; /* NOP */ 860 | curfont->next = usedfont; 861 | } else { 862 | /* In the unfortunate event that the matching font does not have a glyph for 863 | * the given code point then it means that there are no fonts in the system 864 | * that has that glyph. Free and unload the font found and record add the bad 865 | * code point to the nomatches array so that we don't waste more time searching 866 | * for this character. */ 867 | xfont_free(usedfont); 868 | nomatches[nomatches[h0] ? h1 : h0] = utf8codepoint; 869 | no_match: 870 | /* Set the used font back to the primary font. */ 871 | usedfont = drw->fonts; 872 | } 873 | } 874 | } 875 | } 876 | /* If we are drawing the text then we have finished by now so we clean up after ourselves by 877 | * freeing our XftDraw structure. */ 878 | if (d) 879 | XftDrawDestroy(d); 880 | 881 | /* Finally we return the x position following the drawn text, or just x in the event that we 882 | * are only after the text width. The w here represents the remaining space. */ 883 | return x + (render ? w : 0); 884 | } 885 | 886 | /* This copies graphics from the drawable and places that on the designated window. 887 | * 888 | * @called_from drawbar to copy the bar graphics to the bar window 889 | * @calls XCopyArea https://tronche.com/gui/x/xlib/graphics/XCopyArea.html 890 | * @calls XSync https://tronche.com/gui/x/xlib/event-handling/XSync.html 891 | * 892 | * Internal call stack: 893 | * ~ -> drawbar -> drw_map 894 | */ 895 | void 896 | drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h) 897 | { 898 | if (!drw) 899 | return; 900 | 901 | /* This copies the graphics drawn on the drawable too the designated window (in this case the 902 | * bar window). */ 903 | XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y); 904 | /* This flushes the output buffer and then waits until all requests have been 905 | * received and processed by the X server. */ 906 | XSync(drw->dpy, False); 907 | } 908 | 909 | /* This copies graphics from the drawable and places that on the designated window. 910 | * 911 | * @called_from TEXTW macro to get the width of a given text string 912 | * @called_from drawbar via TEXTW macro 913 | * @called_from buttonpress via TEXTW macro 914 | * @calls XCopyArea https://tronche.com/gui/x/xlib/graphics/XCopyArea.html 915 | * @calls XSync https://tronche.com/gui/x/xlib/event-handling/XSync.html 916 | * 917 | * Internal call stack: 918 | * ~ -> drawbar -> drw_fontset_getwidth 919 | * run -> buttonpress -> drw_fontset_getwidth 920 | */ 921 | unsigned int 922 | drw_fontset_getwidth(Drw *drw, const char *text) 923 | { 924 | /* If we have no drawable, have no fonts or the text is NULL then bail. This is 925 | * just a general guard that should never happen in practice. */ 926 | if (!drw || !drw->fonts || !text) 927 | return 0; 928 | /* Calls drw_text with parameters indicating that we are only interested in the 929 | * width of the text and that no effort should be done drawing the text. */ 930 | return drw_text(drw, 0, 0, 0, 0, 0, text, 0); 931 | } 932 | 933 | /* Helper function in the context of finding the longest of a set of strings. This finds the next 934 | * size when the text is too large to fit in the given width. This is primarily designed for dmenu 935 | * which shares the same drw code. 936 | * 937 | * @calls drw_text to get the width of a given text string after overflow 938 | * 939 | * This function is currently not used within dwm. 940 | */ 941 | unsigned int 942 | drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n) 943 | { 944 | unsigned int tmp = 0; 945 | if (drw && drw->fonts && text && n) 946 | tmp = drw_text(drw, 0, 0, 0, 0, 0, text, n); 947 | return MIN(n, tmp); 948 | } 949 | 950 | /* Wrapper function for XftTextExtentsUtf8 to get the text width for a string of characters using 951 | * a particular font. 952 | * 953 | * @called_from drw_text to get the width of a single character for a given font 954 | * @calls XftTextExtentsUtf8 https://linux.die.net/man/3/xft 955 | * 956 | * Internal call stack: 957 | * ~ -> drawbar -> drw_text -> drw_font_getexts 958 | * ~ -> drawbar -> drw_fontset_getwidth -> drw_text -> drw_font_getexts 959 | * run -> buttonpress -> drw_fontset_getwidth -> drw_text -> drw_font_getexts 960 | */ 961 | void 962 | drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h) 963 | { 964 | XGlyphInfo ext; 965 | 966 | /* General guard to protect against NULL values. Should not happen in practice. */ 967 | if (!font || !text) 968 | return; 969 | 970 | /* This call gets the width of the given text reading len bytes. */ 971 | XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext); 972 | /* The x offset of the extent tells the width of the text usign the given font. */ 973 | if (w) 974 | *w = ext.xOff; 975 | /* The height of the text is always the font height. */ 976 | if (h) 977 | *h = font->h; 978 | } 979 | 980 | /* Function to create and return a given font cursor. 981 | * 982 | * @called_from setup to create various mouse cursors 983 | * @calls XCreateFontCursor https://tronche.com/gui/x/xlib/pixmap-and-cursor/XCreateFontCursor.html 984 | * @calls ecalloc to allocate memory for the given cursor 985 | * @see https://tronche.com/gui/x/xlib/appendix/b/ 986 | * 987 | * Internal call stack: 988 | * main -> setup -> drw_cur_create 989 | */ 990 | Cur * 991 | drw_cur_create(Drw *drw, int shape) 992 | { 993 | Cur *cur; 994 | 995 | if (!drw || !(cur = ecalloc(1, sizeof(Cur)))) 996 | return NULL; 997 | 998 | cur->cursor = XCreateFontCursor(drw->dpy, shape); 999 | 1000 | return cur; 1001 | } 1002 | 1003 | /* Function to free a given font cursor. 1004 | * 1005 | * @called_from cleanup to free memory before exiting the program 1006 | * @calls XFreeCursor https://tronche.com/gui/x/xlib/pixmap-and-cursor/XFreeCursor.html 1007 | * @calls free to release the memory used by the given cursor 1008 | * 1009 | * Internal call stack: 1010 | * main -> cleanup -> drw_cur_free 1011 | */ 1012 | void 1013 | drw_cur_free(Drw *drw, Cur *cursor) 1014 | { 1015 | if (!cursor) 1016 | return; 1017 | 1018 | XFreeCursor(drw->dpy, cursor->cursor); 1019 | free(cursor); 1020 | } 1021 | -------------------------------------------------------------------------------- /drw.h: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | 3 | /* This is an internal structure that holds a single cursor. */ 4 | typedef struct { 5 | Cursor cursor; 6 | } Cur; 7 | 8 | /* This is an internal structure that represents a font. */ 9 | typedef struct Fnt { 10 | /* The display. */ 11 | Display *dpy; 12 | /* The font height. */ 13 | unsigned int h; 14 | /* The actual font. */ 15 | XftFont *xfont; 16 | /* The fontconfig pattern, used when searching for fonts. */ 17 | FcPattern *pattern; 18 | /* The next font in the linked list. */ 19 | struct Fnt *next; 20 | } Fnt; 21 | 22 | /* This represents the columns for colour schemes. 23 | * 24 | * E.g. as defined in the dwm configuration file: 25 | * 26 | * static const char *colors[][3] = { 27 | * // fg bg border 28 | * [SchemeNorm] = { col_gray3, col_gray1, col_gray2 }, 29 | * [SchemeSel] = { col_gray4, col_cyan, col_cyan }, 30 | * }; 31 | */ 32 | enum { ColFg, ColBg, ColBorder }; /* Clr scheme index */ 33 | typedef XftColor Clr; 34 | 35 | /* This is an internal structure representing the drawable, used when drawing the bar. */ 36 | typedef struct { 37 | /* The width and height of the drawable. */ 38 | unsigned int w, h; 39 | /* The display. */ 40 | Display *dpy; 41 | /* The screen as returned by DefaultScreen for the given display. */ 42 | int screen; 43 | /* The root window. */ 44 | Window root; 45 | /* The drawable pixel map. */ 46 | Drawable drawable; 47 | /* The graphics context that handles colours. */ 48 | GC gc; 49 | /* The currently used colour scheme. */ 50 | Clr *scheme; 51 | /* A linked list of loaded fonts. */ 52 | Fnt *fonts; 53 | } Drw; 54 | 55 | /* Drawable abstraction */ 56 | Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h); 57 | void drw_resize(Drw *drw, unsigned int w, unsigned int h); 58 | void drw_free(Drw *drw); 59 | 60 | /* Fnt abstraction */ 61 | Fnt *drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount); 62 | void drw_fontset_free(Fnt* set); 63 | unsigned int drw_fontset_getwidth(Drw *drw, const char *text); 64 | unsigned int drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n); 65 | void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h); 66 | 67 | /* Colorscheme abstraction */ 68 | void drw_clr_create(Drw *drw, Clr *dest, const char *clrname); 69 | Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount); 70 | 71 | /* Cursor abstraction */ 72 | Cur *drw_cur_create(Drw *drw, int shape); 73 | void drw_cur_free(Drw *drw, Cur *cursor); 74 | 75 | /* Drawing context manipulation */ 76 | void drw_setfontset(Drw *drw, Fnt *set); 77 | void drw_setscheme(Drw *drw, Clr *scm); 78 | 79 | /* Drawing functions */ 80 | void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert); 81 | int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert); 82 | 83 | /* Map functions */ 84 | void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h); 85 | -------------------------------------------------------------------------------- /dwm.1: -------------------------------------------------------------------------------- 1 | .TH DWM 1 dwm\-VERSION 2 | .SH NAME 3 | dwm \- dynamic window manager 4 | .SH SYNOPSIS 5 | .B dwm 6 | .RB [ \-v ] 7 | .SH DESCRIPTION 8 | dwm is a dynamic window manager for X. It manages windows in tiled, monocle 9 | and floating layouts. Either layout can be applied dynamically, optimising the 10 | environment for the application in use and the task performed. 11 | .P 12 | In tiled layouts windows are managed in a master and stacking area. The master 13 | area on the left contains one window by default, and the stacking area on the 14 | right contains all other windows. The number of master area windows can be 15 | adjusted from zero to an arbitrary number. In monocle layout all windows are 16 | maximised to the screen size. In floating layout windows can be resized and 17 | moved freely. Dialog windows are always managed floating, regardless of the 18 | layout applied. 19 | .P 20 | Windows are grouped by tags. Each window can be tagged with one or multiple 21 | tags. Selecting certain tags displays all windows with these tags. 22 | .P 23 | Each screen contains a small status bar which displays all available tags, the 24 | layout, the title of the focused window, and the text read from the root window 25 | name property, if the screen is focused. A floating window is indicated with an 26 | empty square and a maximised floating window is indicated with a filled square 27 | before the windows title. The selected tags are indicated with a different 28 | color. The tags of the focused window are indicated with a filled square in the 29 | top left corner. The tags which are applied to one or more windows are 30 | indicated with an empty square in the top left corner. 31 | .P 32 | dwm draws a small border around windows to indicate the focus state. 33 | .SH OPTIONS 34 | .TP 35 | .B \-v 36 | prints version information to stderr, then exits. 37 | .SH USAGE 38 | .SS Status bar 39 | .TP 40 | .B X root window name 41 | is read and displayed in the status text area. It can be set with the 42 | .BR xsetroot (1) 43 | command. 44 | .TP 45 | .B Button1 46 | click on a tag label to display all windows with that tag, click on the layout 47 | label toggles between tiled and floating layout. 48 | .TP 49 | .B Button3 50 | click on a tag label adds/removes all windows with that tag to/from the view. 51 | .TP 52 | .B Mod1\-Button1 53 | click on a tag label applies that tag to the focused window. 54 | .TP 55 | .B Mod1\-Button3 56 | click on a tag label adds/removes that tag to/from the focused window. 57 | .SS Keyboard commands 58 | .TP 59 | .B Mod1\-Shift\-Return 60 | Start 61 | .BR st(1). 62 | .TP 63 | .B Mod1\-p 64 | Spawn 65 | .BR dmenu(1) 66 | for launching other programs. 67 | .TP 68 | .B Mod1\-, 69 | Focus previous screen, if any. 70 | .TP 71 | .B Mod1\-. 72 | Focus next screen, if any. 73 | .TP 74 | .B Mod1\-Shift\-, 75 | Send focused window to previous screen, if any. 76 | .TP 77 | .B Mod1\-Shift\-. 78 | Send focused window to next screen, if any. 79 | .TP 80 | .B Mod1\-b 81 | Toggles bar on and off. 82 | .TP 83 | .B Mod1\-t 84 | Sets tiled layout. 85 | .TP 86 | .B Mod1\-f 87 | Sets floating layout. 88 | .TP 89 | .B Mod1\-m 90 | Sets monocle layout. 91 | .TP 92 | .B Mod1\-space 93 | Toggles between current and previous layout. 94 | .TP 95 | .B Mod1\-j 96 | Focus next window. 97 | .TP 98 | .B Mod1\-k 99 | Focus previous window. 100 | .TP 101 | .B Mod1\-i 102 | Increase number of windows in master area. 103 | .TP 104 | .B Mod1\-d 105 | Decrease number of windows in master area. 106 | .TP 107 | .B Mod1\-l 108 | Increase master area size. 109 | .TP 110 | .B Mod1\-h 111 | Decrease master area size. 112 | .TP 113 | .B Mod1\-Return 114 | Zooms/cycles focused window to/from master area (tiled layouts only). 115 | .TP 116 | .B Mod1\-Shift\-c 117 | Close focused window. 118 | .TP 119 | .B Mod1\-Shift\-space 120 | Toggle focused window between tiled and floating state. 121 | .TP 122 | .B Mod1\-Tab 123 | Toggles to the previously selected tags. 124 | .TP 125 | .B Mod1\-Shift\-[1..n] 126 | Apply nth tag to focused window. 127 | .TP 128 | .B Mod1\-Shift\-0 129 | Apply all tags to focused window. 130 | .TP 131 | .B Mod1\-Control\-Shift\-[1..n] 132 | Add/remove nth tag to/from focused window. 133 | .TP 134 | .B Mod1\-[1..n] 135 | View all windows with nth tag. 136 | .TP 137 | .B Mod1\-0 138 | View all windows with any tag. 139 | .TP 140 | .B Mod1\-Control\-[1..n] 141 | Add/remove all windows with nth tag to/from the view. 142 | .TP 143 | .B Mod1\-Shift\-q 144 | Quit dwm. 145 | .SS Mouse commands 146 | .TP 147 | .B Mod1\-Button1 148 | Move focused window while dragging. Tiled windows will be toggled to the floating state. 149 | .TP 150 | .B Mod1\-Button2 151 | Toggles focused window between floating and tiled state. 152 | .TP 153 | .B Mod1\-Button3 154 | Resize focused window while dragging. Tiled windows will be toggled to the floating state. 155 | .SH CUSTOMIZATION 156 | dwm is customized by creating a custom config.h and (re)compiling the source 157 | code. This keeps it fast, secure and simple. 158 | .SH SEE ALSO 159 | .BR dmenu (1), 160 | .BR st (1) 161 | .SH ISSUES 162 | Java applications which use the XToolkit/XAWT backend may draw grey windows 163 | only. The XToolkit/XAWT backend breaks ICCCM-compliance in recent JDK 1.5 and early 164 | JDK 1.6 versions, because it assumes a reparenting window manager. Possible workarounds 165 | are using JDK 1.4 (which doesn't contain the XToolkit/XAWT backend) or setting the 166 | environment variable 167 | .BR AWT_TOOLKIT=MToolkit 168 | (to use the older Motif backend instead) or running 169 | .B xprop -root -f _NET_WM_NAME 32a -set _NET_WM_NAME LG3D 170 | or 171 | .B wmname LG3D 172 | (to pretend that a non-reparenting window manager is running that the 173 | XToolkit/XAWT backend can recognize) or when using OpenJDK setting the environment variable 174 | .BR _JAVA_AWT_WM_NONREPARENTING=1 . 175 | .SH BUGS 176 | Send all bug reports with a patch to hackers@suckless.org. 177 | -------------------------------------------------------------------------------- /dwm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bakkeby/dwm-commented/2fbd38e094e75abaee03206f64a4af80dc4c3df0/dwm.png -------------------------------------------------------------------------------- /transient.c: -------------------------------------------------------------------------------- 1 | /* cc transient.c -o transient -lX11 */ 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | /* Why do we have a transient.c file in the dwm source code? It is not sourced by any of the other 9 | * files and it is not referred to in the Makefile either. 10 | * 11 | * This is a not so uncommon question and the answer is that transient is just a small test tool 12 | * to help test dwm features that involves transient windows. 13 | * 14 | * The first line at the top of the file tells you how you can compile this file. 15 | * 16 | * Running the compiled transient executable will open a simple 400x400 blank window. The window 17 | * title is "floating" and it is intended to start as floating due to setting the minimum and 18 | * maximum size hints to the same value of 400, which should cause the window to be "fixed" in 19 | * size. The window starts at position 100,100. 20 | * 21 | * After five seconds it will open another smaller window that has the WM_TRANSIENT_FOR property 22 | * set referring to the parent window. The name of the transient window is "transient" and should 23 | * be floating because it is transient. The window has a size of 100x100 and starts at position 24 | * 50,50 and will overlap the parent window. Client rules will not apply for this window as it is 25 | * transient. 26 | * 27 | * @calls XOpenDisplay https://tronche.com/gui/x/xlib/display/opening.html 28 | * @calls XCloseDisplay https://tronche.com/gui/x/xlib/display/XCloseDisplay.html 29 | * @calls XMapWindow https://tronche.com/gui/x/xlib/window/XMapWindow.html 30 | * @calls DefaultRootWindow a macro that returns the root window for the default screen 31 | * @calls XCreateSimpleWindow https://tronche.com/gui/x/xlib/window/XCreateWindow.html 32 | * @calls XSetWMNormalHints https://tronche.com/gui/x/xlib/ICC/client-to-window-manager/XSetWMNormalHints.html 33 | * @calls XStoreName https://tronche.com/gui/x/xlib/ICC/client-to-window-manager/XStoreName.html 34 | * @calls XMapWindow https://tronche.com/gui/x/xlib/window/XMapWindow.html 35 | * @calls XSelectInput https://tronche.com/gui/x/xlib/event-handling/XSelectInput.html 36 | * @calls XNextEvent https://tronche.com/gui/x/xlib/event-handling/manipulating-event-queue/XNextEvent.html 37 | * @calls XSetTransientForHint https://tronche.com/gui/x/xlib/ICC/client-to-window-manager/XSetTransientForHint.html 38 | * @calls exit https://linux.die.net/man/3/exit 39 | * @calls sleep https://linux.die.net/man/3/sleep 40 | */ 41 | 42 | int main(void) { 43 | Display *d; 44 | Window r, f, t = None; 45 | XSizeHints h; 46 | XEvent e; 47 | 48 | /* This opens the display and bails if it can't. */ 49 | d = XOpenDisplay(NULL); 50 | if (!d) 51 | exit(1); 52 | /* Get the root window for the display. */ 53 | r = DefaultRootWindow(d); 54 | 55 | /* This creates the main (parent) window at position 100,100 with a size of 400x400. The three 56 | * values at the end are for border width, border colour and background colour. */ 57 | f = XCreateSimpleWindow(d, r, 100, 100, 400, 400, 0, 0, 0); 58 | /* This sets the minimum and maximum size hints to 400x400, which should make the client fixed 59 | * in size and thus become a floating window that can not become tiled. */ 60 | h.min_width = h.max_width = h.min_height = h.max_height = 400; 61 | /* Hint flags to say that we provided hints for minimum and maximum size. */ 62 | h.flags = PMinSize | PMaxSize; 63 | /* This sets the hints specified above for the window. */ 64 | XSetWMNormalHints(d, f, &h); 65 | /* This sets the name (title) for the main window to "floating". */ 66 | XStoreName(d, f, "floating"); 67 | /* Map the window so that it can be seen. */ 68 | XMapWindow(d, f); 69 | 70 | /* This tells the window manager we are interested in receiving Expose events in relation to 71 | * this window. */ 72 | XSelectInput(d, f, ExposureMask); 73 | while (1) { 74 | /* This will fetch the next event in the queue. Any event is simply ignored. */ 75 | XNextEvent(d, &e); 76 | 77 | /* If we have not yet created a transient window then */ 78 | if (t == None) { 79 | /* we first wait five seconds. */ 80 | sleep(5); 81 | /* Then we create the new window at position 50,50 with a size of 100x100. */ 82 | t = XCreateSimpleWindow(d, r, 50, 50, 100, 100, 0, 0, 0); 83 | /* This sets the WM_TRANSIENT_FOR property for the new window referring to the floating 84 | * window as its parent window. */ 85 | XSetTransientForHint(d, t, f); 86 | /* This sets the name (title) for the transient window to "transient". */ 87 | XStoreName(d, t, "transient"); 88 | /* Map the window so that it can be seen. */ 89 | XMapWindow(d, t); 90 | /* This tells the window manager we are interested in receiving Expose events in 91 | * relation to this window. */ 92 | XSelectInput(d, t, ExposureMask); 93 | } 94 | } 95 | 96 | /* Call to close the display and exit. This code is for all practical reasons unreachable as 97 | * the while loop above is infinite and we will end up forcibly killing the window to make it 98 | * go away. */ 99 | XCloseDisplay(d); 100 | exit(0); 101 | } 102 | -------------------------------------------------------------------------------- /util.c: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | /* The util library comes from libsl which contains some functionality that is common for several 9 | * suckless project, e.g. dmenu and dwm. 10 | * 11 | * @see https://git.suckless.org/libsl/files.html 12 | */ 13 | 14 | #include "util.h" 15 | 16 | /* Helper function that prints an error before exiting the process. 17 | * 18 | * @called_from ecalloc in case of error 19 | * @called_from sigchld in case SIGCHLD handler could not be installed 20 | * @called_from main in the event of unexpected arguments 21 | * @called_from xerrorstart in case another window manager is running 22 | * @calls va_start 23 | * @calls vfprintf 24 | * @calls va_end 25 | * @calls strlen to get the length of the error string 26 | * @calls strerror to look up the description of the error that occurred 27 | * @calls fputc to write a character to standard err 28 | * @calls exit to stop the process 29 | * @see https://www.tutorialspoint.com/c_standard_library/c_macro_va_start.htm 30 | * 31 | * Internal call stack: 32 | * main -> setup -> drw_fontset_create -> xfont_create 33 | * main -> setup -> drw_scm_create -> drw_clr_create -> die 34 | * main -> setup -> ecalloc -> die 35 | * main -> setup -> updategeom -> ecalloc -> die 36 | * main -> setup -> updategeom -> createmon -> ecalloc -> die 37 | * main -> setup -> sigchld -> die 38 | * main -> die 39 | * run -> buttonpress -> drw_fontset_getwidth -> drw_text -> die 40 | * run -> buttonpress -> spawn -> die 41 | * run -> configurenotify -> updategeom -> ecalloc -> die 42 | * run -> configurenotify -> updategeom -> createmon -> ecalloc -> die 43 | * run -> keypress -> spawn -> die 44 | * run -> maprequest -> manage -> ecalloc -> die 45 | * run -> scan -> manage -> ecalloc -> die 46 | * xerrorstart -> die 47 | * ~ -> drawbar -> drw_text -> die 48 | * ~ -> drawbar -> drw_fontset_getwidth -> drw_text -> die 49 | */ 50 | void 51 | die(const char *fmt, ...) 52 | { 53 | va_list ap; 54 | int saved_errno; 55 | 56 | saved_errno = errno; 57 | 58 | /* Note how the function ends with , ... - this means that the function takes variable 59 | * arguments. Have a look at the tutorial on these macros for more information, but the gist 60 | * of it is that it allows for calls to die on this form: 61 | * 62 | * die("error, cannot allocate color '%s'", clrname); 63 | * 64 | * where the %s is substituted for the value of the clrname variable. 65 | */ 66 | va_start(ap, fmt); 67 | vfprintf(stderr, fmt, ap); 68 | va_end(ap); 69 | 70 | /* If the error string ends with a colon then print the error that happened as well. */ 71 | if (fmt[0] && fmt[strlen(fmt)-1] == ':') 72 | fprintf(stderr, " %s", strerror(saved_errno)); 73 | fputc('\n', stderr); 74 | 75 | /* Stop the process passing 1 to exit to signify failure. */ 76 | exit(1); 77 | } 78 | 79 | /* Memory allocation wrapper around calloc that calls die in the event that memory could not be 80 | * allocated. 81 | * 82 | * @called_from drw_cur_create to allocate memory for a cursor 83 | * @called_from drw_scm_create to allocate memory for the colour scheme 84 | * @called_from drw_create to allocate memory for the drawable 85 | * @called_from setup to allocate memory for colour schemes 86 | * @called_from updategeom to allocate memory to hold unique screen info 87 | * @called_from createmon to allocate memory for new Monitor structures 88 | * @called_from manage to allocate memory for new Client structures 89 | * @calls calloc to allocate memory 90 | * @calls die in the event that memory could not be allocated 91 | * 92 | * Internal call stack: 93 | * main -> setup -> drw_cur_create -> ecalloc 94 | * main -> setup -> drw_fontset_create -> xfont_create -> ecalloc 95 | * main -> setup -> drw_scm_create -> ecalloc 96 | * main -> setup -> drw_create -> ecalloc 97 | * main -> setup -> ecalloc 98 | * main -> setup -> updategeom -> ecalloc 99 | * main -> setup -> updategeom -> createmon -> ecalloc 100 | * run -> configurenotify -> updategeom -> ecalloc 101 | * run -> configurenotify -> updategeom -> createmon -> ecalloc 102 | * run -> maprequest -> manage -> ecalloc 103 | * run -> scan -> manage -> ecalloc 104 | */ 105 | void * 106 | ecalloc(size_t nmemb, size_t size) 107 | { 108 | void *p; 109 | 110 | /* If memory could not be allocated then call die to exit the window manager. */ 111 | if (!(p = calloc(nmemb, size))) 112 | die("calloc:"); 113 | return p; 114 | } 115 | -------------------------------------------------------------------------------- /util.h: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | 3 | /* Macro that chooses the maximum of two values. If used with a function call then that call may 4 | * be called twice due to how the macro unfolds in the code. */ 5 | #define MAX(A, B) ((A) > (B) ? (A) : (B)) 6 | /* Macro that chooses the minimum of two values. If used with a function call then that call may 7 | * be called twice due to how the macro unfolds in the code. */ 8 | #define MIN(A, B) ((A) < (B) ? (A) : (B)) 9 | /* Macro that is true if a value is between two values. If used with a function call then that 10 | * call will happen twice due to how the macro unfolds in the code. */ 11 | #define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B)) 12 | /* This calculates the number of items of an array. */ 13 | #define LENGTH(X) (sizeof X / sizeof X[0]) 14 | 15 | /* Function declarations. */ 16 | void die(const char *fmt, ...); 17 | void *ecalloc(size_t nmemb, size_t size); 18 | --------------------------------------------------------------------------------