├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── config.h ├── config.mk ├── drw.c ├── drw.h ├── dwm.1 ├── dwm.c ├── dwm.png ├── transient.c ├── util.c └── util.h /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vscode/ 3 | node_modules/ 4 | dist/ 5 | tmp/ 6 | temp/ 7 | logs/ 8 | *.log 9 | **/dump.rdb 10 | **/.DS_Store 11 | dwm 12 | *.o 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT/X Consortium License 2 | 3 | © 2006-2019 Anselm R Garbe 4 | © 2006-2009 Jukka Salmi 5 | © 2006-2007 Sander van Dijk 6 | © 2007-2011 Peter Hartlich 7 | © 2007-2009 Szabolcs Nagy 8 | © 2007-2009 Christof Musik 9 | © 2007-2009 Premysl Hruby 10 | © 2007-2008 Enno Gottox Boland 11 | © 2008 Martin Hurton 12 | © 2008 Neale Pickett 13 | © 2009 Mate Nagy 14 | © 2010-2016 Hiltjo Posthuma 15 | © 2010-2012 Connor Lane Smith 16 | © 2011 Christoph Lohmann <20h@r-36.net> 17 | © 2015-2016 Quentin Rameau 18 | © 2015-2016 Eric Pruitt 19 | © 2016-2017 Markus Teich 20 | © 2020-2022 Chris Down 21 | 22 | Permission is hereby granted, free of charge, to any person obtaining a 23 | copy of this software and associated documentation files (the "Software"), 24 | to deal in the Software without restriction, including without limitation 25 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 26 | and/or sell copies of the Software, and to permit persons to whom the 27 | Software is furnished to do so, subject to the following conditions: 28 | 29 | The above copyright notice and this permission notice shall be included in 30 | all copies or substantial portions of the Software. 31 | 32 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 33 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 34 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 35 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 36 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 37 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 38 | DEALINGS IN THE SOFTWARE. 39 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # dwm - dynamic window manager 2 | # See LICENSE file for copyright and license details. 3 | 4 | include config.mk 5 | 6 | SRC = drw.c dwm.c util.c 7 | OBJ = ${SRC:.c=.o} 8 | 9 | all: options dwm 10 | 11 | options: 12 | @echo dwm build options: 13 | @echo "CFLAGS = ${CFLAGS}" 14 | @echo "LDFLAGS = ${LDFLAGS}" 15 | @echo "CC = ${CC}" 16 | 17 | .c.o: 18 | ${CC} -c ${CFLAGS} $< 19 | 20 | ${OBJ}: config.h config.mk 21 | 22 | config.h: 23 | cp config.def.h $@ 24 | 25 | dwm: ${OBJ} 26 | ${CC} -o $@ ${OBJ} ${LDFLAGS} 27 | 28 | clean: 29 | rm -f dwm ${OBJ} dwm-${VERSION}.tar.gz 30 | 31 | dist: clean 32 | mkdir -p dwm-${VERSION} 33 | cp -R LICENSE Makefile README config.def.h config.mk\ 34 | dwm.1 drw.h util.h ${SRC} dwm.png transient.c dwm-${VERSION} 35 | tar -cf dwm-${VERSION}.tar dwm-${VERSION} 36 | gzip dwm-${VERSION}.tar 37 | rm -rf dwm-${VERSION} 38 | 39 | install: all 40 | mkdir -p ${DESTDIR}${PREFIX}/bin 41 | cp -f dwm ${DESTDIR}${PREFIX}/bin 42 | chmod 755 ${DESTDIR}${PREFIX}/bin/dwm 43 | mkdir -p ${DESTDIR}${MANPREFIX}/man1 44 | sed "s/VERSION/${VERSION}/g" < dwm.1 > ${DESTDIR}${MANPREFIX}/man1/dwm.1 45 | chmod 644 ${DESTDIR}${MANPREFIX}/man1/dwm.1 46 | 47 | uninstall: 48 | rm -f ${DESTDIR}${PREFIX}/bin/dwm\ 49 | ${DESTDIR}${MANPREFIX}/man1/dwm.1 50 | 51 | .PHONY: all options clean dist install uninstall 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DWM教学仓库 2 | 3 | 本文档关联bilibili up [称呼我C先生](https://space.bilibili.com/303522232) 的dwm相关教学视频 4 | 5 | 尽量做到每一期视频对应一个commit 6 | 7 | ## COMMIT 以及 关联视频 8 | 9 | 1. [1.1](https://github.com/yaocccc/dwms/tree/1.1) - [添加新布局grid 和 magicgrid](https://www.bilibili.com/video/BV1UU4y1Y7j1) 10 | 2. [1.2](https://github.com/yaocccc/dwms/tree/1.2) - [配置新窗口在栈的头部还是底部](https://www.bilibili.com/video/BV1gF411P7kA) 11 | 3. [1.3](https://github.com/yaocccc/dwms/tree/1.3) - [实现overview](https://www.bilibili.com/video/BV11U4y1r7YU) 12 | 13 | ## DOC 14 | 15 | ### 项目结构 16 | 17 | ```plaintext 18 | . 19 | ├── config.h -- 配置文件入口 20 | ├── config.mk -- make相关的配置 一般不需要更改 21 | ├── dwm.c -- 具体业务逻辑 22 | ├── drw.c -- 和draw绘制相关的一些实现 23 | ├── drw.h -- 和draw绘制相关的一些头 24 | ├── dwm.png -- 图标 25 | ├── LICENSE -- 版本说明等 26 | ├── Makefile -- makefile 27 | ├── README.md -- 项目说明文件 28 | ├── transient.c -- 工具类 29 | ├── util.c -- 工具类 30 | └── util.h -- 工具类 31 | ``` 32 | -------------------------------------------------------------------------------- /config.h: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | 3 | /* appearance */ 4 | static const int newclientathead = 0; /* 定义新窗口在栈顶还是栈底 */ 5 | static const unsigned int borderpx = 1; /* border pixel of windows */ 6 | static const unsigned int snap = 32; /* snap pixel */ 7 | static const int showbar = 1; /* 0 means no bar */ 8 | static const int topbar = 1; /* 0 means bottom bar */ 9 | static const char *fonts[] = { "monospace:size=10" }; 10 | static const char dmenufont[] = "monospace:size=10"; 11 | static const char col_gray1[] = "#222222"; 12 | static const char col_gray2[] = "#444444"; 13 | static const char col_gray3[] = "#bbbbbb"; 14 | static const char col_gray4[] = "#eeeeee"; 15 | static const char col_cyan[] = "#005577"; 16 | static const char *colors[][3] = { 17 | /* fg bg border */ 18 | [SchemeNorm] = { col_gray3, col_gray1, col_gray2 }, 19 | [SchemeSel] = { col_gray4, col_cyan, col_cyan }, 20 | }; 21 | 22 | /* tagging */ 23 | static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; 24 | 25 | static const int overviewgappi = 24; /* overview时 窗口与边缘 缝隙大小 */ 26 | static const int overviewgappo = 60; /* overview时 窗口与窗口 缝隙大小 */ 27 | static const char *overviewtag = "OVERVIEW"; 28 | static const Layout overviewlayout = { "", overview }; 29 | 30 | static const Rule rules[] = { 31 | /* xprop(1): 32 | * WM_CLASS(STRING) = instance, class 33 | * WM_NAME(STRING) = title 34 | */ 35 | /* class instance title tags mask isfloating monitor */ 36 | { "Gimp", NULL, NULL, 0, 1, -1 }, 37 | { "Firefox", NULL, NULL, 1 << 8, 0, -1 }, 38 | { "float", NULL, NULL, 0, 1, -1 }, 39 | }; 40 | 41 | /* layout(s) */ 42 | static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */ 43 | static const int nmaster = 1; /* number of clients in master area */ 44 | static const int resizehints = 1; /* 1 means respect size hints in tiled resizals */ 45 | static const int lockfullscreen = 1; /* 1 will force focus on the fullscreen window */ 46 | 47 | static const Layout layouts[] = { 48 | /* symbol arrange function */ 49 | { "[]=", tile }, /* first entry is default */ 50 | { "><>", NULL }, /* no layout function means floating behavior */ 51 | { "[M]", monocle }, 52 | { "[G]", magicgrid }, 53 | }; 54 | 55 | /* key definitions */ 56 | #define MODKEY Mod1Mask // Mod4Mask is the super key [win] 57 | #define TAGKEYS(KEY,TAG) \ 58 | { MODKEY, KEY, view, {.ui = 1 << TAG} }, \ 59 | { MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \ 60 | { MODKEY|ShiftMask, KEY, tag, {.ui = 1 << TAG} }, \ 61 | { MODKEY|ControlMask|ShiftMask, KEY, toggletag, {.ui = 1 << TAG} }, 62 | 63 | /* helper for spawning shell commands in the pre dwm-5.0 fashion */ 64 | #define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } } 65 | 66 | /* commands */ 67 | static char dmenumon[2] = "0"; /* component of dmenucmd, manipulated in spawn() */ 68 | static const char *dmenucmd[] = { "dmenu_run", "-m", dmenumon, "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL }; 69 | static const char *termcmd[] = { "st", NULL }; 70 | 71 | static Key keys[] = { 72 | /* modifier key function argument */ 73 | { MODKEY, XK_a, toggleoverview, {0} }, 74 | { MODKEY, XK_p, spawn, {.v = dmenucmd } }, 75 | { MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } }, 76 | { MODKEY, XK_b, togglebar, {0} }, 77 | { MODKEY, XK_j, focusstack, {.i = +1 } }, 78 | { MODKEY, XK_k, focusstack, {.i = -1 } }, 79 | { MODKEY, XK_i, incnmaster, {.i = +1 } }, 80 | { MODKEY, XK_d, incnmaster, {.i = -1 } }, 81 | { MODKEY, XK_h, setmfact, {.f = -0.05} }, 82 | { MODKEY, XK_l, setmfact, {.f = +0.05} }, 83 | { MODKEY, XK_Return, zoom, {0} }, 84 | { MODKEY, XK_Tab, view, {0} }, 85 | { MODKEY|ShiftMask, XK_c, killclient, {0} }, 86 | { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, 87 | { MODKEY, XK_f, setlayout, {.v = &layouts[1]} }, 88 | { MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, 89 | { MODKEY, XK_g, setlayout, {.v = &layouts[3]} }, 90 | { MODKEY, XK_space, setlayout, {0} }, 91 | { MODKEY|ShiftMask, XK_space, togglefloating, {0} }, 92 | { MODKEY, XK_0, view, {.ui = ~0 } }, 93 | { MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } }, 94 | { MODKEY, XK_comma, focusmon, {.i = -1 } }, 95 | { MODKEY, XK_period, focusmon, {.i = +1 } }, 96 | { MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } }, 97 | { MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } }, 98 | TAGKEYS( XK_1, 0) 99 | TAGKEYS( XK_2, 1) 100 | TAGKEYS( XK_3, 2) 101 | TAGKEYS( XK_4, 3) 102 | TAGKEYS( XK_5, 4) 103 | TAGKEYS( XK_6, 5) 104 | TAGKEYS( XK_7, 6) 105 | TAGKEYS( XK_8, 7) 106 | TAGKEYS( XK_9, 8) 107 | { MODKEY|ShiftMask, XK_q, quit, {0} }, 108 | }; 109 | 110 | /* button definitions */ 111 | /* click can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */ 112 | static Button buttons[] = { 113 | /* click event mask button function argument */ 114 | { ClkLtSymbol, 0, Button1, setlayout, {0} }, 115 | { ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} }, 116 | { ClkWinTitle, 0, Button2, zoom, {0} }, 117 | { ClkStatusText, 0, Button2, spawn, {.v = termcmd } }, 118 | { ClkClientWin, MODKEY, Button1, movemouse, {0} }, 119 | { ClkClientWin, MODKEY, Button2, togglefloating, {0} }, 120 | { ClkClientWin, MODKEY, Button3, resizemouse, {0} }, 121 | { ClkTagBar, 0, Button1, view, {0} }, 122 | { ClkTagBar, 0, Button3, toggleview, {0} }, 123 | { ClkTagBar, MODKEY, Button1, tag, {0} }, 124 | { ClkTagBar, MODKEY, Button3, toggletag, {0} }, 125 | }; 126 | 127 | -------------------------------------------------------------------------------- /config.mk: -------------------------------------------------------------------------------- 1 | # dwm version 2 | VERSION = 6.3 3 | 4 | # Customize below to fit your system 5 | 6 | # paths 7 | PREFIX = /usr/local 8 | MANPREFIX = ${PREFIX}/share/man 9 | 10 | X11INC = /usr/X11R6/include 11 | X11LIB = /usr/X11R6/lib 12 | 13 | # Xinerama, comment if you don't want it 14 | XINERAMALIBS = -lXinerama 15 | XINERAMAFLAGS = -DXINERAMA 16 | 17 | # freetype 18 | FREETYPELIBS = -lfontconfig -lXft 19 | FREETYPEINC = /usr/include/freetype2 20 | # OpenBSD (uncomment) 21 | #FREETYPEINC = ${X11INC}/freetype2 22 | #MANPREFIX = ${PREFIX}/man 23 | 24 | # includes and libs 25 | INCS = -I${X11INC} -I${FREETYPEINC} 26 | LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} 27 | 28 | # flags 29 | CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} 30 | #CFLAGS = -g -std=c99 -pedantic -Wall -O0 ${INCS} ${CPPFLAGS} 31 | CFLAGS = -std=c99 -pedantic -Wall -Wno-deprecated-declarations -Os ${INCS} ${CPPFLAGS} 32 | LDFLAGS = ${LIBS} 33 | 34 | # Solaris 35 | #CFLAGS = -fast ${INCS} -DVERSION=\"${VERSION}\" 36 | #LDFLAGS = ${LIBS} 37 | 38 | # compiler and linker 39 | CC = cc 40 | -------------------------------------------------------------------------------- /drw.c: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "drw.h" 9 | #include "util.h" 10 | 11 | #define UTF_INVALID 0xFFFD 12 | #define UTF_SIZ 4 13 | 14 | static const unsigned char utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; 15 | static const unsigned char utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; 16 | static const long utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000}; 17 | static const long utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; 18 | 19 | static long 20 | utf8decodebyte(const char c, size_t *i) 21 | { 22 | for (*i = 0; *i < (UTF_SIZ + 1); ++(*i)) 23 | if (((unsigned char)c & utfmask[*i]) == utfbyte[*i]) 24 | return (unsigned char)c & ~utfmask[*i]; 25 | return 0; 26 | } 27 | 28 | static size_t 29 | utf8validate(long *u, size_t i) 30 | { 31 | if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF)) 32 | *u = UTF_INVALID; 33 | for (i = 1; *u > utfmax[i]; ++i) 34 | ; 35 | return i; 36 | } 37 | 38 | static size_t 39 | utf8decode(const char *c, long *u, size_t clen) 40 | { 41 | size_t i, j, len, type; 42 | long udecoded; 43 | 44 | *u = UTF_INVALID; 45 | if (!clen) 46 | return 0; 47 | udecoded = utf8decodebyte(c[0], &len); 48 | if (!BETWEEN(len, 1, UTF_SIZ)) 49 | return 1; 50 | for (i = 1, j = 1; i < clen && j < len; ++i, ++j) { 51 | udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type); 52 | if (type) 53 | return j; 54 | } 55 | if (j < len) 56 | return 0; 57 | *u = udecoded; 58 | utf8validate(u, len); 59 | 60 | return len; 61 | } 62 | 63 | Drw * 64 | drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h) 65 | { 66 | Drw *drw = ecalloc(1, sizeof(Drw)); 67 | 68 | drw->dpy = dpy; 69 | drw->screen = screen; 70 | drw->root = root; 71 | drw->w = w; 72 | drw->h = h; 73 | drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen)); 74 | drw->gc = XCreateGC(dpy, root, 0, NULL); 75 | XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter); 76 | 77 | return drw; 78 | } 79 | 80 | void 81 | drw_resize(Drw *drw, unsigned int w, unsigned int h) 82 | { 83 | if (!drw) 84 | return; 85 | 86 | drw->w = w; 87 | drw->h = h; 88 | if (drw->drawable) 89 | XFreePixmap(drw->dpy, drw->drawable); 90 | drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen)); 91 | } 92 | 93 | void 94 | drw_free(Drw *drw) 95 | { 96 | XFreePixmap(drw->dpy, drw->drawable); 97 | XFreeGC(drw->dpy, drw->gc); 98 | drw_fontset_free(drw->fonts); 99 | free(drw); 100 | } 101 | 102 | /* This function is an implementation detail. Library users should use 103 | * drw_fontset_create instead. 104 | */ 105 | static Fnt * 106 | xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern) 107 | { 108 | Fnt *font; 109 | XftFont *xfont = NULL; 110 | FcPattern *pattern = NULL; 111 | 112 | if (fontname) { 113 | /* Using the pattern found at font->xfont->pattern does not yield the 114 | * same substitution results as using the pattern returned by 115 | * FcNameParse; using the latter results in the desired fallback 116 | * behaviour whereas the former just results in missing-character 117 | * rectangles being drawn, at least with some fonts. */ 118 | if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fontname))) { 119 | fprintf(stderr, "error, cannot load font from name: '%s'\n", fontname); 120 | return NULL; 121 | } 122 | if (!(pattern = FcNameParse((FcChar8 *) fontname))) { 123 | fprintf(stderr, "error, cannot parse font name to pattern: '%s'\n", fontname); 124 | XftFontClose(drw->dpy, xfont); 125 | return NULL; 126 | } 127 | } else if (fontpattern) { 128 | if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) { 129 | fprintf(stderr, "error, cannot load font from pattern.\n"); 130 | return NULL; 131 | } 132 | } else { 133 | die("no font specified."); 134 | } 135 | 136 | /* Do not allow using color fonts. This is a workaround for a BadLength 137 | * error from Xft with color glyphs. Modelled on the Xterm workaround. See 138 | * https://bugzilla.redhat.com/show_bug.cgi?id=1498269 139 | * https://lists.suckless.org/dev/1701/30932.html 140 | * https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=916349 141 | * and lots more all over the internet. 142 | */ 143 | FcBool iscol; 144 | if(FcPatternGetBool(xfont->pattern, FC_COLOR, 0, &iscol) == FcResultMatch && iscol) { 145 | XftFontClose(drw->dpy, xfont); 146 | return NULL; 147 | } 148 | 149 | font = ecalloc(1, sizeof(Fnt)); 150 | font->xfont = xfont; 151 | font->pattern = pattern; 152 | font->h = xfont->ascent + xfont->descent; 153 | font->dpy = drw->dpy; 154 | 155 | return font; 156 | } 157 | 158 | static void 159 | xfont_free(Fnt *font) 160 | { 161 | if (!font) 162 | return; 163 | if (font->pattern) 164 | FcPatternDestroy(font->pattern); 165 | XftFontClose(font->dpy, font->xfont); 166 | free(font); 167 | } 168 | 169 | Fnt* 170 | drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount) 171 | { 172 | Fnt *cur, *ret = NULL; 173 | size_t i; 174 | 175 | if (!drw || !fonts) 176 | return NULL; 177 | 178 | for (i = 1; i <= fontcount; i++) { 179 | if ((cur = xfont_create(drw, fonts[fontcount - i], NULL))) { 180 | cur->next = ret; 181 | ret = cur; 182 | } 183 | } 184 | return (drw->fonts = ret); 185 | } 186 | 187 | void 188 | drw_fontset_free(Fnt *font) 189 | { 190 | if (font) { 191 | drw_fontset_free(font->next); 192 | xfont_free(font); 193 | } 194 | } 195 | 196 | void 197 | drw_clr_create(Drw *drw, Clr *dest, const char *clrname) 198 | { 199 | if (!drw || !dest || !clrname) 200 | return; 201 | 202 | if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen), 203 | DefaultColormap(drw->dpy, drw->screen), 204 | clrname, dest)) 205 | die("error, cannot allocate color '%s'", clrname); 206 | } 207 | 208 | /* Wrapper to create color schemes. The caller has to call free(3) on the 209 | * returned color scheme when done using it. */ 210 | Clr * 211 | drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount) 212 | { 213 | size_t i; 214 | Clr *ret; 215 | 216 | /* need at least two colors for a scheme */ 217 | if (!drw || !clrnames || clrcount < 2 || !(ret = ecalloc(clrcount, sizeof(XftColor)))) 218 | return NULL; 219 | 220 | for (i = 0; i < clrcount; i++) 221 | drw_clr_create(drw, &ret[i], clrnames[i]); 222 | return ret; 223 | } 224 | 225 | void 226 | drw_setfontset(Drw *drw, Fnt *set) 227 | { 228 | if (drw) 229 | drw->fonts = set; 230 | } 231 | 232 | void 233 | drw_setscheme(Drw *drw, Clr *scm) 234 | { 235 | if (drw) 236 | drw->scheme = scm; 237 | } 238 | 239 | void 240 | drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert) 241 | { 242 | if (!drw || !drw->scheme) 243 | return; 244 | XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme[ColBg].pixel : drw->scheme[ColFg].pixel); 245 | if (filled) 246 | XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); 247 | else 248 | XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w - 1, h - 1); 249 | } 250 | 251 | int 252 | drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert) 253 | { 254 | int i, ty, ellipsis_x = 0; 255 | unsigned int tmpw, ew, ellipsis_w = 0, ellipsis_len; 256 | XftDraw *d = NULL; 257 | Fnt *usedfont, *curfont, *nextfont; 258 | int utf8strlen, utf8charlen, render = x || y || w || h; 259 | long utf8codepoint = 0; 260 | const char *utf8str; 261 | FcCharSet *fccharset; 262 | FcPattern *fcpattern; 263 | FcPattern *match; 264 | XftResult result; 265 | int charexists = 0, overflow = 0; 266 | /* keep track of a couple codepoints for which we have no match. */ 267 | enum { nomatches_len = 64 }; 268 | static struct { long codepoint[nomatches_len]; unsigned int idx; } nomatches; 269 | static unsigned int ellipsis_width = 0; 270 | 271 | if (!drw || (render && (!drw->scheme || !w)) || !text || !drw->fonts) 272 | return 0; 273 | 274 | if (!render) { 275 | w = invert ? invert : ~invert; 276 | } else { 277 | XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel); 278 | XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); 279 | d = XftDrawCreate(drw->dpy, drw->drawable, 280 | DefaultVisual(drw->dpy, drw->screen), 281 | DefaultColormap(drw->dpy, drw->screen)); 282 | x += lpad; 283 | w -= lpad; 284 | } 285 | 286 | usedfont = drw->fonts; 287 | if (!ellipsis_width && render) 288 | ellipsis_width = drw_fontset_getwidth(drw, "..."); 289 | while (1) { 290 | ew = ellipsis_len = utf8strlen = 0; 291 | utf8str = text; 292 | nextfont = NULL; 293 | while (*text) { 294 | utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ); 295 | for (curfont = drw->fonts; curfont; curfont = curfont->next) { 296 | charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint); 297 | if (charexists) { 298 | drw_font_getexts(curfont, text, utf8charlen, &tmpw, NULL); 299 | if (ew + ellipsis_width <= w) { 300 | /* keep track where the ellipsis still fits */ 301 | ellipsis_x = x + ew; 302 | ellipsis_w = w - ew; 303 | ellipsis_len = utf8strlen; 304 | } 305 | 306 | if (ew + tmpw > w) { 307 | overflow = 1; 308 | /* called from drw_fontset_getwidth_clamp(): 309 | * it wants the width AFTER the overflow 310 | */ 311 | if (!render) 312 | x += tmpw; 313 | else 314 | utf8strlen = ellipsis_len; 315 | } else if (curfont == usedfont) { 316 | utf8strlen += utf8charlen; 317 | text += utf8charlen; 318 | ew += tmpw; 319 | } else { 320 | nextfont = curfont; 321 | } 322 | break; 323 | } 324 | } 325 | 326 | if (overflow || !charexists || nextfont) 327 | break; 328 | else 329 | charexists = 0; 330 | } 331 | 332 | if (utf8strlen) { 333 | if (render) { 334 | ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent; 335 | XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg], 336 | usedfont->xfont, x, ty, (XftChar8 *)utf8str, utf8strlen); 337 | } 338 | x += ew; 339 | w -= ew; 340 | } 341 | if (render && overflow) 342 | drw_text(drw, ellipsis_x, y, ellipsis_w, h, 0, "...", invert); 343 | 344 | if (!*text || overflow) { 345 | break; 346 | } else if (nextfont) { 347 | charexists = 0; 348 | usedfont = nextfont; 349 | } else { 350 | /* Regardless of whether or not a fallback font is found, the 351 | * character must be drawn. */ 352 | charexists = 1; 353 | 354 | for (i = 0; i < nomatches_len; ++i) { 355 | /* avoid calling XftFontMatch if we know we won't find a match */ 356 | if (utf8codepoint == nomatches.codepoint[i]) 357 | goto no_match; 358 | } 359 | 360 | fccharset = FcCharSetCreate(); 361 | FcCharSetAddChar(fccharset, utf8codepoint); 362 | 363 | if (!drw->fonts->pattern) { 364 | /* Refer to the comment in xfont_create for more information. */ 365 | die("the first font in the cache must be loaded from a font string."); 366 | } 367 | 368 | fcpattern = FcPatternDuplicate(drw->fonts->pattern); 369 | FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset); 370 | FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue); 371 | FcPatternAddBool(fcpattern, FC_COLOR, FcFalse); 372 | 373 | FcConfigSubstitute(NULL, fcpattern, FcMatchPattern); 374 | FcDefaultSubstitute(fcpattern); 375 | match = XftFontMatch(drw->dpy, drw->screen, fcpattern, &result); 376 | 377 | FcCharSetDestroy(fccharset); 378 | FcPatternDestroy(fcpattern); 379 | 380 | if (match) { 381 | usedfont = xfont_create(drw, NULL, match); 382 | if (usedfont && XftCharExists(drw->dpy, usedfont->xfont, utf8codepoint)) { 383 | for (curfont = drw->fonts; curfont->next; curfont = curfont->next) 384 | ; /* NOP */ 385 | curfont->next = usedfont; 386 | } else { 387 | xfont_free(usedfont); 388 | nomatches.codepoint[++nomatches.idx % nomatches_len] = utf8codepoint; 389 | no_match: 390 | usedfont = drw->fonts; 391 | } 392 | } 393 | } 394 | } 395 | if (d) 396 | XftDrawDestroy(d); 397 | 398 | return x + (render ? w : 0); 399 | } 400 | 401 | void 402 | drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h) 403 | { 404 | if (!drw) 405 | return; 406 | 407 | XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y); 408 | XSync(drw->dpy, False); 409 | } 410 | 411 | unsigned int 412 | drw_fontset_getwidth(Drw *drw, const char *text) 413 | { 414 | if (!drw || !drw->fonts || !text) 415 | return 0; 416 | return drw_text(drw, 0, 0, 0, 0, 0, text, 0); 417 | } 418 | 419 | unsigned int 420 | drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n) 421 | { 422 | unsigned int tmp = 0; 423 | if (drw && drw->fonts && text && n) 424 | tmp = drw_text(drw, 0, 0, 0, 0, 0, text, n); 425 | return MIN(n, tmp); 426 | } 427 | 428 | void 429 | drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h) 430 | { 431 | XGlyphInfo ext; 432 | 433 | if (!font || !text) 434 | return; 435 | 436 | XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext); 437 | if (w) 438 | *w = ext.xOff; 439 | if (h) 440 | *h = font->h; 441 | } 442 | 443 | Cur * 444 | drw_cur_create(Drw *drw, int shape) 445 | { 446 | Cur *cur; 447 | 448 | if (!drw || !(cur = ecalloc(1, sizeof(Cur)))) 449 | return NULL; 450 | 451 | cur->cursor = XCreateFontCursor(drw->dpy, shape); 452 | 453 | return cur; 454 | } 455 | 456 | void 457 | drw_cur_free(Drw *drw, Cur *cursor) 458 | { 459 | if (!cursor) 460 | return; 461 | 462 | XFreeCursor(drw->dpy, cursor->cursor); 463 | free(cursor); 464 | } 465 | -------------------------------------------------------------------------------- /drw.h: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | 3 | typedef struct { 4 | Cursor cursor; 5 | } Cur; 6 | 7 | typedef struct Fnt { 8 | Display *dpy; 9 | unsigned int h; 10 | XftFont *xfont; 11 | FcPattern *pattern; 12 | struct Fnt *next; 13 | } Fnt; 14 | 15 | enum { ColFg, ColBg, ColBorder }; /* Clr scheme index */ 16 | typedef XftColor Clr; 17 | 18 | typedef struct { 19 | unsigned int w, h; 20 | Display *dpy; 21 | int screen; 22 | Window root; 23 | Drawable drawable; 24 | GC gc; 25 | Clr *scheme; 26 | Fnt *fonts; 27 | } Drw; 28 | 29 | /* Drawable abstraction */ 30 | Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h); 31 | void drw_resize(Drw *drw, unsigned int w, unsigned int h); 32 | void drw_free(Drw *drw); 33 | 34 | /* Fnt abstraction */ 35 | Fnt *drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount); 36 | void drw_fontset_free(Fnt* set); 37 | unsigned int drw_fontset_getwidth(Drw *drw, const char *text); 38 | unsigned int drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n); 39 | void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h); 40 | 41 | /* Colorscheme abstraction */ 42 | void drw_clr_create(Drw *drw, Clr *dest, const char *clrname); 43 | Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount); 44 | 45 | /* Cursor abstraction */ 46 | Cur *drw_cur_create(Drw *drw, int shape); 47 | void drw_cur_free(Drw *drw, Cur *cursor); 48 | 49 | /* Drawing context manipulation */ 50 | void drw_setfontset(Drw *drw, Fnt *set); 51 | void drw_setscheme(Drw *drw, Clr *scm); 52 | 53 | /* Drawing functions */ 54 | void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert); 55 | int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert); 56 | 57 | /* Map functions */ 58 | void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h); 59 | -------------------------------------------------------------------------------- /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.c: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. 2 | * 3 | * dynamic window manager is designed like any other X client as well. It is 4 | * driven through handling X events. In contrast to other X clients, a window 5 | * manager selects for SubstructureRedirectMask on the root window, to receive 6 | * events about window (dis-)appearance. Only one X connection at a time is 7 | * allowed to select for this event mask. 8 | * 9 | * The event handlers of dwm are organized in an array which is accessed 10 | * whenever a new event has been fetched. This allows event dispatching 11 | * in O(1) time. 12 | * 13 | * Each child of the root window is called a client, except windows which have 14 | * set the override_redirect flag. Clients are organized in a linked client 15 | * list on each monitor, the focus history is remembered through a stack list 16 | * on each monitor. Each client contains a bit array to indicate the tags of a 17 | * client. 18 | * 19 | * Keys and tagging rules are organized as arrays and defined in config.h. 20 | * 21 | * To understand everything else, start reading main(). 22 | */ 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #ifdef XINERAMA 40 | #include 41 | #endif /* XINERAMA */ 42 | #include 43 | 44 | #include "drw.h" 45 | #include "util.h" 46 | 47 | /* macros */ 48 | #define BUTTONMASK (ButtonPressMask|ButtonReleaseMask) 49 | #define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)) 50 | #define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \ 51 | * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy))) 52 | #define ISVISIBLE(C) ((C->mon->isoverview || C->tags & C->mon->tagset[C->mon->seltags])) 53 | #define LENGTH(X) (sizeof X / sizeof X[0]) 54 | #define MOUSEMASK (BUTTONMASK|PointerMotionMask) 55 | #define WIDTH(X) ((X)->w + 2 * (X)->bw) 56 | #define HEIGHT(X) ((X)->h + 2 * (X)->bw) 57 | #define TAGMASK ((1 << LENGTH(tags)) - 1) 58 | #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) 59 | 60 | /* enums */ 61 | enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ 62 | enum { SchemeNorm, SchemeSel }; /* color schemes */ 63 | enum { NetSupported, NetWMName, NetWMState, NetWMCheck, 64 | NetWMFullscreen, NetActiveWindow, NetWMWindowType, 65 | NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ 66 | enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ 67 | enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, 68 | ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ 69 | 70 | typedef union { 71 | int i; 72 | unsigned int ui; 73 | float f; 74 | const void *v; 75 | } Arg; 76 | 77 | typedef struct { 78 | unsigned int click; 79 | unsigned int mask; 80 | unsigned int button; 81 | void (*func)(const Arg *arg); 82 | const Arg arg; 83 | } Button; 84 | 85 | typedef struct Monitor Monitor; 86 | typedef struct Client Client; 87 | struct Client { 88 | char name[256]; 89 | float mina, maxa; 90 | int x, y, w, h; 91 | int oldx, oldy, oldw, oldh; 92 | int basew, baseh, incw, inch, maxw, maxh, minw, minh, hintsvalid; 93 | int bw, oldbw; 94 | unsigned int tags; 95 | int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen; 96 | Client *next; 97 | Client *snext; 98 | Monitor *mon; 99 | Window win; 100 | }; 101 | 102 | typedef struct { 103 | unsigned int mod; 104 | KeySym keysym; 105 | void (*func)(const Arg *); 106 | const Arg arg; 107 | } Key; 108 | 109 | typedef struct { 110 | const char *symbol; 111 | void (*arrange)(Monitor *); 112 | } Layout; 113 | 114 | struct Monitor { 115 | char ltsymbol[16]; 116 | float mfact; 117 | int nmaster; 118 | int num; 119 | int by; /* bar geometry */ 120 | int mx, my, mw, mh; /* screen size */ 121 | int wx, wy, ww, wh; /* window area */ 122 | unsigned int seltags; 123 | unsigned int sellt; 124 | unsigned int tagset[2]; 125 | int showbar; 126 | int topbar; 127 | Client *clients; 128 | Client *sel; 129 | Client *stack; 130 | Monitor *next; 131 | Window barwin; 132 | const Layout *lt[2]; 133 | unsigned int isoverview; 134 | }; 135 | 136 | typedef struct { 137 | const char *class; 138 | const char *instance; 139 | const char *title; 140 | unsigned int tags; 141 | int isfloating; 142 | int monitor; 143 | } Rule; 144 | 145 | /* function declarations */ 146 | static void applyrules(Client *c); 147 | static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact); 148 | static void arrange(Monitor *m); 149 | static void arrangemon(Monitor *m); 150 | static void attach(Client *c); 151 | static void attachstack(Client *c); 152 | static void buttonpress(XEvent *e); 153 | static void checkotherwm(void); 154 | static void cleanup(void); 155 | static void cleanupmon(Monitor *mon); 156 | static void clientmessage(XEvent *e); 157 | static void configure(Client *c); 158 | static void configurenotify(XEvent *e); 159 | static void configurerequest(XEvent *e); 160 | static Monitor *createmon(void); 161 | static void destroynotify(XEvent *e); 162 | static void detach(Client *c); 163 | static void detachstack(Client *c); 164 | static Monitor *dirtomon(int dir); 165 | static void drawbar(Monitor *m); 166 | static void drawbars(void); 167 | static void enternotify(XEvent *e); 168 | static void expose(XEvent *e); 169 | static void focus(Client *c); 170 | static void focusin(XEvent *e); 171 | static void focusmon(const Arg *arg); 172 | static void focusstack(const Arg *arg); 173 | static Atom getatomprop(Client *c, Atom prop); 174 | static int getrootptr(int *x, int *y); 175 | static long getstate(Window w); 176 | static int gettextprop(Window w, Atom atom, char *text, unsigned int size); 177 | static void grabbuttons(Client *c, int focused); 178 | static void grabkeys(void); 179 | static void incnmaster(const Arg *arg); 180 | static void keypress(XEvent *e); 181 | static void killclient(const Arg *arg); 182 | static void manage(Window w, XWindowAttributes *wa); 183 | static void managefloating(Client *c); 184 | static void mappingnotify(XEvent *e); 185 | static void maprequest(XEvent *e); 186 | static void monocle(Monitor *m); 187 | static void motionnotify(XEvent *e); 188 | static void movemouse(const Arg *arg); 189 | static Client *nexttiled(Client *c); 190 | static void pop(Client *); 191 | static void propertynotify(XEvent *e); 192 | static void quit(const Arg *arg); 193 | static Monitor *recttomon(int x, int y, int w, int h); 194 | static void resize(Client *c, int x, int y, int w, int h, int interact); 195 | static void resizeclient(Client *c, int x, int y, int w, int h); 196 | static void resizemouse(const Arg *arg); 197 | static void restack(Monitor *m); 198 | static void run(void); 199 | static void scan(void); 200 | static int sendevent(Client *c, Atom proto); 201 | static void sendmon(Client *c, Monitor *m); 202 | static void setclientstate(Client *c, long state); 203 | static void setfocus(Client *c); 204 | static void setfullscreen(Client *c, int fullscreen); 205 | static void setlayout(const Arg *arg); 206 | static void setmfact(const Arg *arg); 207 | static void setup(void); 208 | static void seturgent(Client *c, int urg); 209 | static void showhide(Client *c); 210 | static void sigchld(int unused); 211 | static void spawn(const Arg *arg); 212 | static void tag(const Arg *arg); 213 | static void tagmon(const Arg *arg); 214 | static void tile(Monitor *); 215 | static void magicgrid(Monitor *m); 216 | static void overview(Monitor *m); 217 | static void grid(Monitor *m, uint gappo, uint uappi); 218 | static void togglebar(const Arg *arg); 219 | static void togglefloating(const Arg *arg); 220 | static void toggletag(const Arg *arg); 221 | static void toggleview(const Arg *arg); 222 | static void toggleoverview(const Arg *arg); 223 | static void unfocus(Client *c, int setfocus); 224 | static void unmanage(Client *c, int destroyed); 225 | static void unmapnotify(XEvent *e); 226 | static void updatebarpos(Monitor *m); 227 | static void updatebars(void); 228 | static void updateclientlist(void); 229 | static int updategeom(void); 230 | static void updatenumlockmask(void); 231 | static void updatesizehints(Client *c); 232 | static void updatestatus(void); 233 | static void updatetitle(Client *c); 234 | static void updatewindowtype(Client *c); 235 | static void updatewmhints(Client *c); 236 | static void view(const Arg *arg); 237 | static Client *wintoclient(Window w); 238 | static Monitor *wintomon(Window w); 239 | static int xerror(Display *dpy, XErrorEvent *ee); 240 | static int xerrordummy(Display *dpy, XErrorEvent *ee); 241 | static int xerrorstart(Display *dpy, XErrorEvent *ee); 242 | static void zoom(const Arg *arg); 243 | 244 | /* variables */ 245 | static const char broken[] = "broken"; 246 | static char stext[256]; 247 | static int screen; 248 | static int sw, sh; /* X display screen geometry width, height */ 249 | static int bh, blw = 0; /* bar geometry */ 250 | static int lrpad; /* sum of left and right padding for text */ 251 | static int (*xerrorxlib)(Display *, XErrorEvent *); 252 | static unsigned int numlockmask = 0; 253 | static void (*handler[LASTEvent]) (XEvent *) = { 254 | [ButtonPress] = buttonpress, 255 | [ClientMessage] = clientmessage, 256 | [ConfigureRequest] = configurerequest, 257 | [ConfigureNotify] = configurenotify, 258 | [DestroyNotify] = destroynotify, 259 | [EnterNotify] = enternotify, 260 | [Expose] = expose, 261 | [FocusIn] = focusin, 262 | [KeyPress] = keypress, 263 | [MappingNotify] = mappingnotify, 264 | [MapRequest] = maprequest, 265 | [MotionNotify] = motionnotify, 266 | [PropertyNotify] = propertynotify, 267 | [UnmapNotify] = unmapnotify 268 | }; 269 | static Atom wmatom[WMLast], netatom[NetLast]; 270 | static int running = 1; 271 | static Cur *cursor[CurLast]; 272 | static Clr **scheme; 273 | static Display *dpy; 274 | static Drw *drw; 275 | static Monitor *mons, *selmon; 276 | static Window root, wmcheckwin; 277 | 278 | /* configuration, allows nested code to access above variables */ 279 | #include "config.h" 280 | 281 | /* compile-time check if all tags fit into an unsigned int bit array. */ 282 | struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; 283 | 284 | /* function implementations */ 285 | void 286 | applyrules(Client *c) 287 | { 288 | const char *class, *instance; 289 | unsigned int i; 290 | const Rule *r; 291 | Monitor *m; 292 | XClassHint ch = { NULL, NULL }; 293 | 294 | /* rule matching */ 295 | c->isfloating = 0; 296 | c->tags = 0; 297 | XGetClassHint(dpy, c->win, &ch); 298 | class = ch.res_class ? ch.res_class : broken; 299 | instance = ch.res_name ? ch.res_name : broken; 300 | 301 | for (i = 0; i < LENGTH(rules); i++) { 302 | r = &rules[i]; 303 | if ((!r->title || strstr(c->name, r->title)) 304 | && (!r->class || strstr(class, r->class)) 305 | && (!r->instance || strstr(instance, r->instance))) 306 | { 307 | c->isfloating = r->isfloating; 308 | c->tags |= r->tags; 309 | for (m = mons; m && m->num != r->monitor; m = m->next); 310 | if (m) 311 | c->mon = m; 312 | } 313 | } 314 | if (ch.res_class) 315 | XFree(ch.res_class); 316 | if (ch.res_name) 317 | XFree(ch.res_name); 318 | c->tags = c->tags & TAGMASK ? c->tags & TAGMASK : c->mon->tagset[c->mon->seltags]; 319 | } 320 | 321 | int 322 | applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact) 323 | { 324 | int baseismin; 325 | Monitor *m = c->mon; 326 | 327 | /* set minimum possible */ 328 | *w = MAX(1, *w); 329 | *h = MAX(1, *h); 330 | if (interact) { 331 | if (*x > sw) 332 | *x = sw - WIDTH(c); 333 | if (*y > sh) 334 | *y = sh - HEIGHT(c); 335 | if (*x + *w + 2 * c->bw < 0) 336 | *x = 0; 337 | if (*y + *h + 2 * c->bw < 0) 338 | *y = 0; 339 | } else { 340 | if (*x >= m->wx + m->ww) 341 | *x = m->wx + m->ww - WIDTH(c); 342 | if (*y >= m->wy + m->wh) 343 | *y = m->wy + m->wh - HEIGHT(c); 344 | if (*x + *w + 2 * c->bw <= m->wx) 345 | *x = m->wx; 346 | if (*y + *h + 2 * c->bw <= m->wy) 347 | *y = m->wy; 348 | } 349 | if (*h < bh) 350 | *h = bh; 351 | if (*w < bh) 352 | *w = bh; 353 | if (resizehints || c->isfloating || !c->mon->lt[c->mon->sellt]->arrange) { 354 | if (!c->hintsvalid) 355 | updatesizehints(c); 356 | /* see last two sentences in ICCCM 4.1.2.3 */ 357 | baseismin = c->basew == c->minw && c->baseh == c->minh; 358 | if (!baseismin) { /* temporarily remove base dimensions */ 359 | *w -= c->basew; 360 | *h -= c->baseh; 361 | } 362 | /* adjust for aspect limits */ 363 | if (c->mina > 0 && c->maxa > 0) { 364 | if (c->maxa < (float)*w / *h) 365 | *w = *h * c->maxa + 0.5; 366 | else if (c->mina < (float)*h / *w) 367 | *h = *w * c->mina + 0.5; 368 | } 369 | if (baseismin) { /* increment calculation requires this */ 370 | *w -= c->basew; 371 | *h -= c->baseh; 372 | } 373 | /* adjust for increment value */ 374 | if (c->incw) 375 | *w -= *w % c->incw; 376 | if (c->inch) 377 | *h -= *h % c->inch; 378 | /* restore base dimensions */ 379 | *w = MAX(*w + c->basew, c->minw); 380 | *h = MAX(*h + c->baseh, c->minh); 381 | if (c->maxw) 382 | *w = MIN(*w, c->maxw); 383 | if (c->maxh) 384 | *h = MIN(*h, c->maxh); 385 | } 386 | return *x != c->x || *y != c->y || *w != c->w || *h != c->h; 387 | } 388 | 389 | void 390 | arrange(Monitor *m) 391 | { 392 | if (m) 393 | showhide(m->stack); 394 | else for (m = mons; m; m = m->next) 395 | showhide(m->stack); 396 | if (m) { 397 | arrangemon(m); 398 | restack(m); 399 | } else for (m = mons; m; m = m->next) 400 | arrangemon(m); 401 | } 402 | 403 | void 404 | arrangemon(Monitor *m) 405 | { 406 | if (m->isoverview) { 407 | strncpy(m->ltsymbol, overviewlayout.symbol, sizeof m->ltsymbol); 408 | overviewlayout.arrange(m); 409 | } else { 410 | strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol); 411 | if (m->lt[m->sellt]->arrange) 412 | m->lt[m->sellt]->arrange(m); 413 | } 414 | } 415 | 416 | void 417 | attach(Client *c) 418 | { 419 | if (!newclientathead) { 420 | Client **tc; 421 | for (tc = &c->mon->clients; *tc; tc = &(*tc)->next); 422 | *tc = c; 423 | c->next = NULL; 424 | } else { 425 | c->next = c->mon->clients; 426 | c->mon->clients = c; 427 | } 428 | } 429 | 430 | void 431 | attachstack(Client *c) 432 | { 433 | c->snext = c->mon->stack; 434 | c->mon->stack = c; 435 | } 436 | 437 | void 438 | buttonpress(XEvent *e) 439 | { 440 | unsigned int i, x, click; 441 | Arg arg = {0}; 442 | Client *c; 443 | Monitor *m; 444 | XButtonPressedEvent *ev = &e->xbutton; 445 | 446 | click = ClkRootWin; 447 | /* focus monitor if necessary */ 448 | if ((m = wintomon(ev->window)) && m != selmon) { 449 | unfocus(selmon->sel, 1); 450 | selmon = m; 451 | focus(NULL); 452 | } 453 | if (ev->window == selmon->barwin) { 454 | i = x = 0; 455 | if (selmon->isoverview) { 456 | x += TEXTW(overviewtag); 457 | i = ~0; 458 | if (ev->x > x) 459 | i = LENGTH(tags); 460 | } else { 461 | do 462 | x += TEXTW(tags[i]); 463 | while (ev->x >= x && ++i < LENGTH(tags)); 464 | } 465 | if (i < LENGTH(tags)) { 466 | click = ClkTagBar; 467 | arg.ui = 1 << i; 468 | } else if (ev->x < x + blw) 469 | click = ClkLtSymbol; 470 | else if (ev->x > selmon->ww - (int)TEXTW(stext)) 471 | click = ClkStatusText; 472 | else 473 | click = ClkWinTitle; 474 | } else if ((c = wintoclient(ev->window))) { 475 | focus(c); 476 | restack(selmon); 477 | XAllowEvents(dpy, ReplayPointer, CurrentTime); 478 | click = ClkClientWin; 479 | } 480 | for (i = 0; i < LENGTH(buttons); i++) 481 | if (click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button 482 | && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) 483 | buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg); 484 | } 485 | 486 | void 487 | checkotherwm(void) 488 | { 489 | xerrorxlib = XSetErrorHandler(xerrorstart); 490 | /* this causes an error if some other window manager is running */ 491 | XSelectInput(dpy, DefaultRootWindow(dpy), SubstructureRedirectMask); 492 | XSync(dpy, False); 493 | XSetErrorHandler(xerror); 494 | XSync(dpy, False); 495 | } 496 | 497 | void 498 | cleanup(void) 499 | { 500 | Arg a = {.ui = ~0}; 501 | Layout foo = { "", NULL }; 502 | Monitor *m; 503 | size_t i; 504 | 505 | view(&a); 506 | selmon->lt[selmon->sellt] = &foo; 507 | for (m = mons; m; m = m->next) 508 | while (m->stack) 509 | unmanage(m->stack, 0); 510 | XUngrabKey(dpy, AnyKey, AnyModifier, root); 511 | while (mons) 512 | cleanupmon(mons); 513 | for (i = 0; i < CurLast; i++) 514 | drw_cur_free(drw, cursor[i]); 515 | for (i = 0; i < LENGTH(colors); i++) 516 | free(scheme[i]); 517 | free(scheme); 518 | XDestroyWindow(dpy, wmcheckwin); 519 | drw_free(drw); 520 | XSync(dpy, False); 521 | XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); 522 | XDeleteProperty(dpy, root, netatom[NetActiveWindow]); 523 | } 524 | 525 | void 526 | cleanupmon(Monitor *mon) 527 | { 528 | Monitor *m; 529 | 530 | if (mon == mons) 531 | mons = mons->next; 532 | else { 533 | for (m = mons; m && m->next != mon; m = m->next); 534 | m->next = mon->next; 535 | } 536 | XUnmapWindow(dpy, mon->barwin); 537 | XDestroyWindow(dpy, mon->barwin); 538 | free(mon); 539 | } 540 | 541 | void 542 | clientmessage(XEvent *e) 543 | { 544 | XClientMessageEvent *cme = &e->xclient; 545 | Client *c = wintoclient(cme->window); 546 | 547 | if (!c) 548 | return; 549 | if (cme->message_type == netatom[NetWMState]) { 550 | if (cme->data.l[1] == netatom[NetWMFullscreen] 551 | || cme->data.l[2] == netatom[NetWMFullscreen]) 552 | setfullscreen(c, (cme->data.l[0] == 1 /* _NET_WM_STATE_ADD */ 553 | || (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */ && !c->isfullscreen))); 554 | } else if (cme->message_type == netatom[NetActiveWindow]) { 555 | if (c != selmon->sel && !c->isurgent) 556 | seturgent(c, 1); 557 | } 558 | } 559 | 560 | void 561 | configure(Client *c) 562 | { 563 | XConfigureEvent ce; 564 | 565 | ce.type = ConfigureNotify; 566 | ce.display = dpy; 567 | ce.event = c->win; 568 | ce.window = c->win; 569 | ce.x = c->x; 570 | ce.y = c->y; 571 | ce.width = c->w; 572 | ce.height = c->h; 573 | ce.border_width = c->bw; 574 | ce.above = None; 575 | ce.override_redirect = False; 576 | XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce); 577 | } 578 | 579 | void 580 | configurenotify(XEvent *e) 581 | { 582 | Monitor *m; 583 | Client *c; 584 | XConfigureEvent *ev = &e->xconfigure; 585 | int dirty; 586 | 587 | /* TODO: updategeom handling sucks, needs to be simplified */ 588 | if (ev->window == root) { 589 | dirty = (sw != ev->width || sh != ev->height); 590 | sw = ev->width; 591 | sh = ev->height; 592 | if (updategeom() || dirty) { 593 | drw_resize(drw, sw, bh); 594 | updatebars(); 595 | for (m = mons; m; m = m->next) { 596 | for (c = m->clients; c; c = c->next) 597 | if (c->isfullscreen) 598 | resizeclient(c, m->mx, m->my, m->mw, m->mh); 599 | XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); 600 | } 601 | focus(NULL); 602 | arrange(NULL); 603 | } 604 | } 605 | } 606 | 607 | void 608 | configurerequest(XEvent *e) 609 | { 610 | Client *c; 611 | Monitor *m; 612 | XConfigureRequestEvent *ev = &e->xconfigurerequest; 613 | XWindowChanges wc; 614 | 615 | if ((c = wintoclient(ev->window))) { 616 | if (ev->value_mask & CWBorderWidth) 617 | c->bw = ev->border_width; 618 | else if (c->isfloating || !selmon->lt[selmon->sellt]->arrange) { 619 | m = c->mon; 620 | if (ev->value_mask & CWX) { 621 | c->oldx = c->x; 622 | c->x = m->mx + ev->x; 623 | } 624 | if (ev->value_mask & CWY) { 625 | c->oldy = c->y; 626 | c->y = m->my + ev->y; 627 | } 628 | if (ev->value_mask & CWWidth) { 629 | c->oldw = c->w; 630 | c->w = ev->width; 631 | } 632 | if (ev->value_mask & CWHeight) { 633 | c->oldh = c->h; 634 | c->h = ev->height; 635 | } 636 | if ((c->x + c->w) > m->mx + m->mw && c->isfloating) 637 | c->x = m->mx + (m->mw / 2 - WIDTH(c) / 2); /* center in x direction */ 638 | if ((c->y + c->h) > m->my + m->mh && c->isfloating) 639 | c->y = m->my + (m->mh / 2 - HEIGHT(c) / 2); /* center in y direction */ 640 | if ((ev->value_mask & (CWX|CWY)) && !(ev->value_mask & (CWWidth|CWHeight))) 641 | configure(c); 642 | if (ISVISIBLE(c)) 643 | XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); 644 | } else 645 | configure(c); 646 | } else { 647 | wc.x = ev->x; 648 | wc.y = ev->y; 649 | wc.width = ev->width; 650 | wc.height = ev->height; 651 | wc.border_width = ev->border_width; 652 | wc.sibling = ev->above; 653 | wc.stack_mode = ev->detail; 654 | XConfigureWindow(dpy, ev->window, ev->value_mask, &wc); 655 | } 656 | XSync(dpy, False); 657 | } 658 | 659 | Monitor * 660 | createmon(void) 661 | { 662 | Monitor *m; 663 | 664 | m = ecalloc(1, sizeof(Monitor)); 665 | m->tagset[0] = m->tagset[1] = 1; 666 | m->mfact = mfact; 667 | m->nmaster = nmaster; 668 | m->showbar = showbar; 669 | m->topbar = topbar; 670 | m->lt[0] = &layouts[0]; 671 | m->lt[1] = &layouts[1 % LENGTH(layouts)]; 672 | m->isoverview = 0; 673 | strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); 674 | return m; 675 | } 676 | 677 | void 678 | destroynotify(XEvent *e) 679 | { 680 | Client *c; 681 | XDestroyWindowEvent *ev = &e->xdestroywindow; 682 | 683 | if ((c = wintoclient(ev->window))) 684 | unmanage(c, 1); 685 | } 686 | 687 | void 688 | detach(Client *c) 689 | { 690 | Client **tc; 691 | 692 | for (tc = &c->mon->clients; *tc && *tc != c; tc = &(*tc)->next); 693 | *tc = c->next; 694 | } 695 | 696 | void 697 | detachstack(Client *c) 698 | { 699 | Client **tc, *t; 700 | 701 | for (tc = &c->mon->stack; *tc && *tc != c; tc = &(*tc)->snext); 702 | *tc = c->snext; 703 | 704 | if (c == c->mon->sel) { 705 | for (t = c->mon->stack; t && !ISVISIBLE(t); t = t->snext); 706 | c->mon->sel = t; 707 | } 708 | } 709 | 710 | Monitor * 711 | dirtomon(int dir) 712 | { 713 | Monitor *m = NULL; 714 | 715 | if (dir > 0) { 716 | if (!(m = selmon->next)) 717 | m = mons; 718 | } else if (selmon == mons) 719 | for (m = mons; m->next; m = m->next); 720 | else 721 | for (m = mons; m->next != selmon; m = m->next); 722 | return m; 723 | } 724 | 725 | void 726 | drawbar(Monitor *m) 727 | { 728 | int x, w, tw = 0; 729 | int boxs = drw->fonts->h / 9; 730 | int boxw = drw->fonts->h / 6 + 2; 731 | unsigned int i, occ = 0, urg = 0; 732 | Client *c; 733 | 734 | if (!m->showbar) 735 | return; 736 | 737 | /* draw status first so it can be overdrawn by tags later */ 738 | if (m == selmon) { /* status is only drawn on selected monitor */ 739 | drw_setscheme(drw, scheme[SchemeNorm]); 740 | tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ 741 | drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0); 742 | } 743 | 744 | for (c = m->clients; c; c = c->next) { 745 | occ |= c->tags; 746 | if (c->isurgent) 747 | urg |= c->tags; 748 | } 749 | x = 0; 750 | if (m->isoverview) { 751 | w = TEXTW(overviewtag); 752 | drw_setscheme(drw, scheme[SchemeSel]); 753 | drw_text(drw, x, 0, w, bh, lrpad / 2, overviewtag, 0); 754 | x += w; 755 | } else { 756 | for (i = 0; i < LENGTH(tags); i++) { 757 | w = TEXTW(tags[i]); 758 | drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]); 759 | drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i); 760 | if (occ & 1 << i) 761 | drw_rect(drw, x + boxs, boxs, boxw, boxw, 762 | m == selmon && selmon->sel && selmon->sel->tags & 1 << i, 763 | urg & 1 << i); 764 | x += w; 765 | } 766 | } 767 | 768 | w = blw = TEXTW(m->ltsymbol); 769 | drw_setscheme(drw, scheme[SchemeNorm]); 770 | x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); 771 | 772 | if ((w = m->ww - tw - x) > bh) { 773 | if (m->sel) { 774 | drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); 775 | drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); 776 | if (m->sel->isfloating) 777 | drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0); 778 | } else { 779 | drw_setscheme(drw, scheme[SchemeNorm]); 780 | drw_rect(drw, x, 0, w, bh, 1, 1); 781 | } 782 | } 783 | drw_map(drw, m->barwin, 0, 0, m->ww, bh); 784 | } 785 | 786 | void 787 | drawbars(void) 788 | { 789 | Monitor *m; 790 | 791 | for (m = mons; m; m = m->next) 792 | drawbar(m); 793 | } 794 | 795 | void 796 | enternotify(XEvent *e) 797 | { 798 | Client *c; 799 | Monitor *m; 800 | XCrossingEvent *ev = &e->xcrossing; 801 | 802 | if ((ev->mode != NotifyNormal || ev->detail == NotifyInferior) && ev->window != root) 803 | return; 804 | c = wintoclient(ev->window); 805 | m = c ? c->mon : wintomon(ev->window); 806 | if (m != selmon) { 807 | unfocus(selmon->sel, 1); 808 | selmon = m; 809 | } else if (!c || c == selmon->sel) 810 | return; 811 | focus(c); 812 | } 813 | 814 | void 815 | expose(XEvent *e) 816 | { 817 | Monitor *m; 818 | XExposeEvent *ev = &e->xexpose; 819 | 820 | if (ev->count == 0 && (m = wintomon(ev->window))) 821 | drawbar(m); 822 | } 823 | 824 | void 825 | focus(Client *c) 826 | { 827 | if (!c || !ISVISIBLE(c)) 828 | for (c = selmon->stack; c && !ISVISIBLE(c); c = c->snext); 829 | if (selmon->sel && selmon->sel != c) 830 | unfocus(selmon->sel, 0); 831 | if (c) { 832 | if (c->mon != selmon) 833 | selmon = c->mon; 834 | if (c->isurgent) 835 | seturgent(c, 0); 836 | detachstack(c); 837 | attachstack(c); 838 | grabbuttons(c, 1); 839 | XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel); 840 | setfocus(c); 841 | } else { 842 | XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); 843 | XDeleteProperty(dpy, root, netatom[NetActiveWindow]); 844 | } 845 | selmon->sel = c; 846 | drawbars(); 847 | } 848 | 849 | /* there are some broken focus acquiring clients needing extra handling */ 850 | void 851 | focusin(XEvent *e) 852 | { 853 | XFocusChangeEvent *ev = &e->xfocus; 854 | 855 | if (selmon->sel && ev->window != selmon->sel->win) 856 | setfocus(selmon->sel); 857 | } 858 | 859 | void 860 | focusmon(const Arg *arg) 861 | { 862 | Monitor *m; 863 | 864 | if (!mons->next) 865 | return; 866 | if ((m = dirtomon(arg->i)) == selmon) 867 | return; 868 | unfocus(selmon->sel, 0); 869 | selmon = m; 870 | focus(NULL); 871 | } 872 | 873 | void 874 | focusstack(const Arg *arg) 875 | { 876 | Client *c = NULL, *i; 877 | 878 | if (!selmon->sel || (selmon->sel->isfullscreen && lockfullscreen)) 879 | return; 880 | if (arg->i > 0) { 881 | for (c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next); 882 | if (!c) 883 | for (c = selmon->clients; c && !ISVISIBLE(c); c = c->next); 884 | } else { 885 | for (i = selmon->clients; i != selmon->sel; i = i->next) 886 | if (ISVISIBLE(i)) 887 | c = i; 888 | if (!c) 889 | for (; i; i = i->next) 890 | if (ISVISIBLE(i)) 891 | c = i; 892 | } 893 | if (c) { 894 | focus(c); 895 | restack(selmon); 896 | } 897 | } 898 | 899 | Atom 900 | getatomprop(Client *c, Atom prop) 901 | { 902 | int di; 903 | unsigned long dl; 904 | unsigned char *p = NULL; 905 | Atom da, atom = None; 906 | 907 | if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM, 908 | &da, &di, &dl, &dl, &p) == Success && p) { 909 | atom = *(Atom *)p; 910 | XFree(p); 911 | } 912 | return atom; 913 | } 914 | 915 | int 916 | getrootptr(int *x, int *y) 917 | { 918 | int di; 919 | unsigned int dui; 920 | Window dummy; 921 | 922 | return XQueryPointer(dpy, root, &dummy, &dummy, x, y, &di, &di, &dui); 923 | } 924 | 925 | long 926 | getstate(Window w) 927 | { 928 | int format; 929 | long result = -1; 930 | unsigned char *p = NULL; 931 | unsigned long n, extra; 932 | Atom real; 933 | 934 | if (XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState], 935 | &real, &format, &n, &extra, (unsigned char **)&p) != Success) 936 | return -1; 937 | if (n != 0) 938 | result = *p; 939 | XFree(p); 940 | return result; 941 | } 942 | 943 | int 944 | gettextprop(Window w, Atom atom, char *text, unsigned int size) 945 | { 946 | char **list = NULL; 947 | int n; 948 | XTextProperty name; 949 | 950 | if (!text || size == 0) 951 | return 0; 952 | text[0] = '\0'; 953 | if (!XGetTextProperty(dpy, w, &name, atom) || !name.nitems) 954 | return 0; 955 | if (name.encoding == XA_STRING) 956 | strncpy(text, (char *)name.value, size - 1); 957 | else { 958 | if (XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success && n > 0 && *list) { 959 | strncpy(text, *list, size - 1); 960 | XFreeStringList(list); 961 | } 962 | } 963 | text[size - 1] = '\0'; 964 | XFree(name.value); 965 | return 1; 966 | } 967 | 968 | void 969 | grabbuttons(Client *c, int focused) 970 | { 971 | updatenumlockmask(); 972 | { 973 | unsigned int i, j; 974 | unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; 975 | XUngrabButton(dpy, AnyButton, AnyModifier, c->win); 976 | if (!focused) 977 | XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, 978 | BUTTONMASK, GrabModeSync, GrabModeSync, None, None); 979 | for (i = 0; i < LENGTH(buttons); i++) 980 | if (buttons[i].click == ClkClientWin) 981 | for (j = 0; j < LENGTH(modifiers); j++) 982 | XGrabButton(dpy, buttons[i].button, 983 | buttons[i].mask | modifiers[j], 984 | c->win, False, BUTTONMASK, 985 | GrabModeAsync, GrabModeSync, None, None); 986 | } 987 | } 988 | 989 | void 990 | grabkeys(void) 991 | { 992 | updatenumlockmask(); 993 | { 994 | unsigned int i, j; 995 | unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; 996 | KeyCode code; 997 | 998 | XUngrabKey(dpy, AnyKey, AnyModifier, root); 999 | for (i = 0; i < LENGTH(keys); i++) 1000 | if ((code = XKeysymToKeycode(dpy, keys[i].keysym))) 1001 | for (j = 0; j < LENGTH(modifiers); j++) 1002 | XGrabKey(dpy, code, keys[i].mod | modifiers[j], root, 1003 | True, GrabModeAsync, GrabModeAsync); 1004 | } 1005 | } 1006 | 1007 | void 1008 | incnmaster(const Arg *arg) 1009 | { 1010 | selmon->nmaster = MAX(selmon->nmaster + arg->i, 0); 1011 | arrange(selmon); 1012 | } 1013 | 1014 | #ifdef XINERAMA 1015 | static int 1016 | isuniquegeom(XineramaScreenInfo *unique, size_t n, XineramaScreenInfo *info) 1017 | { 1018 | while (n--) 1019 | if (unique[n].x_org == info->x_org && unique[n].y_org == info->y_org 1020 | && unique[n].width == info->width && unique[n].height == info->height) 1021 | return 0; 1022 | return 1; 1023 | } 1024 | #endif /* XINERAMA */ 1025 | 1026 | void 1027 | keypress(XEvent *e) 1028 | { 1029 | unsigned int i; 1030 | KeySym keysym; 1031 | XKeyEvent *ev; 1032 | 1033 | ev = &e->xkey; 1034 | keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0); 1035 | for (i = 0; i < LENGTH(keys); i++) 1036 | if (keysym == keys[i].keysym 1037 | && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state) 1038 | && keys[i].func) 1039 | keys[i].func(&(keys[i].arg)); 1040 | } 1041 | 1042 | void 1043 | killclient(const Arg *arg) 1044 | { 1045 | if (!selmon->sel) 1046 | return; 1047 | if (!sendevent(selmon->sel, wmatom[WMDelete])) { 1048 | XGrabServer(dpy); 1049 | XSetErrorHandler(xerrordummy); 1050 | XSetCloseDownMode(dpy, DestroyAll); 1051 | XKillClient(dpy, selmon->sel->win); 1052 | XSync(dpy, False); 1053 | XSetErrorHandler(xerror); 1054 | XUngrabServer(dpy); 1055 | } 1056 | } 1057 | 1058 | void 1059 | manage(Window w, XWindowAttributes *wa) 1060 | { 1061 | Client *c, *t = NULL; 1062 | Window trans = None; 1063 | XWindowChanges wc; 1064 | 1065 | c = ecalloc(1, sizeof(Client)); 1066 | c->win = w; 1067 | /* geometry */ 1068 | c->x = c->oldx = wa->x; 1069 | c->y = c->oldy = wa->y; 1070 | c->w = c->oldw = wa->width; 1071 | c->h = c->oldh = wa->height; 1072 | c->oldbw = wa->border_width; 1073 | 1074 | updatetitle(c); 1075 | if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) { 1076 | c->mon = t->mon; 1077 | c->tags = t->tags; 1078 | } else { 1079 | c->mon = selmon; 1080 | applyrules(c); 1081 | } 1082 | 1083 | if (c->x + WIDTH(c) > c->mon->mx + c->mon->mw) 1084 | c->x = c->mon->mx + c->mon->mw - WIDTH(c); 1085 | if (c->y + HEIGHT(c) > c->mon->my + c->mon->mh) 1086 | c->y = c->mon->my + c->mon->mh - HEIGHT(c); 1087 | c->x = MAX(c->x, c->mon->mx); 1088 | /* only fix client y-offset, if the client center might cover the bar */ 1089 | c->y = MAX(c->y, ((c->mon->by == c->mon->my) && (c->x + (c->w / 2) >= c->mon->wx) 1090 | && (c->x + (c->w / 2) < c->mon->wx + c->mon->ww)) ? bh : c->mon->my); 1091 | c->bw = borderpx; 1092 | 1093 | wc.border_width = c->bw; 1094 | 1095 | if (c->isfloating) { 1096 | if (wa->x==0 && wa->y==0) { 1097 | c->x = selmon->wx + (selmon->ww - c->w) / 2; 1098 | c->y = selmon->wy + (selmon->wh - c->h) / 2; 1099 | } 1100 | managefloating(c); 1101 | } 1102 | 1103 | XConfigureWindow(dpy, w, CWBorderWidth, &wc); 1104 | XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel); 1105 | configure(c); /* propagates border_width, if size doesn't change */ 1106 | updatewindowtype(c); 1107 | updatesizehints(c); 1108 | updatewmhints(c); 1109 | XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask); 1110 | grabbuttons(c, 0); 1111 | if (!c->isfloating) 1112 | c->isfloating = c->oldstate = trans != None || c->isfixed; 1113 | if (c->isfloating) 1114 | XRaiseWindow(dpy, c->win); 1115 | attach(c); 1116 | attachstack(c); 1117 | XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, 1118 | (unsigned char *) &(c->win), 1); 1119 | XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */ 1120 | setclientstate(c, NormalState); 1121 | if (c->mon == selmon) 1122 | unfocus(selmon->sel, 0); 1123 | c->mon->sel = c; 1124 | arrange(c->mon); 1125 | XMapWindow(dpy, c->win); 1126 | focus(NULL); 1127 | } 1128 | 1129 | void 1130 | managefloating(Client *c) 1131 | { 1132 | Client *tc; 1133 | int d1 = 0, d2 = 0, tx, ty; 1134 | int tryed = 0; 1135 | while (tryed++ < 10) { 1136 | int dw, dh, existed = 0; 1137 | dw = (selmon->ww / 20) * d1, dh = (selmon->wh / 20) * d2; 1138 | tx = c->x + dw, ty = c->y + dh; 1139 | for (tc = selmon->clients; tc; tc = tc->next) { 1140 | if (ISVISIBLE(tc) && tc != c && tc->x == tx && tc->y == ty) { 1141 | existed = 1; 1142 | break; 1143 | } 1144 | } 1145 | if (!existed) { 1146 | c->x = tx; 1147 | c->y = ty; 1148 | break; 1149 | } else { 1150 | while (d1 == 0) d1 = rand()%7 - 3; 1151 | while (d2 == 0) d2 = rand()%7 - 3; 1152 | } 1153 | } 1154 | } 1155 | 1156 | void 1157 | mappingnotify(XEvent *e) 1158 | { 1159 | XMappingEvent *ev = &e->xmapping; 1160 | 1161 | XRefreshKeyboardMapping(ev); 1162 | if (ev->request == MappingKeyboard) 1163 | grabkeys(); 1164 | } 1165 | 1166 | void 1167 | maprequest(XEvent *e) 1168 | { 1169 | static XWindowAttributes wa; 1170 | XMapRequestEvent *ev = &e->xmaprequest; 1171 | 1172 | if (!XGetWindowAttributes(dpy, ev->window, &wa)) 1173 | return; 1174 | if (wa.override_redirect) 1175 | return; 1176 | if (!wintoclient(ev->window)) 1177 | manage(ev->window, &wa); 1178 | } 1179 | 1180 | void 1181 | monocle(Monitor *m) 1182 | { 1183 | unsigned int n = 0; 1184 | Client *c; 1185 | 1186 | for (c = m->clients; c; c = c->next) 1187 | if (ISVISIBLE(c)) 1188 | n++; 1189 | if (n > 0) /* override layout symbol */ 1190 | snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n); 1191 | for (c = nexttiled(m->clients); c; c = nexttiled(c->next)) 1192 | resize(c, m->wx, m->wy, m->ww - 2 * c->bw, m->wh - 2 * c->bw, 0); 1193 | } 1194 | 1195 | void 1196 | motionnotify(XEvent *e) 1197 | { 1198 | static Monitor *mon = NULL; 1199 | Monitor *m; 1200 | XMotionEvent *ev = &e->xmotion; 1201 | 1202 | if (ev->window != root) 1203 | return; 1204 | if ((m = recttomon(ev->x_root, ev->y_root, 1, 1)) != mon && mon) { 1205 | unfocus(selmon->sel, 1); 1206 | selmon = m; 1207 | focus(NULL); 1208 | } 1209 | mon = m; 1210 | } 1211 | 1212 | void 1213 | movemouse(const Arg *arg) 1214 | { 1215 | int x, y, ocx, ocy, nx, ny; 1216 | Client *c; 1217 | Monitor *m; 1218 | XEvent ev; 1219 | Time lasttime = 0; 1220 | 1221 | if (!(c = selmon->sel)) 1222 | return; 1223 | if (c->isfullscreen) /* no support moving fullscreen windows by mouse */ 1224 | return; 1225 | restack(selmon); 1226 | ocx = c->x; 1227 | ocy = c->y; 1228 | if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, 1229 | None, cursor[CurMove]->cursor, CurrentTime) != GrabSuccess) 1230 | return; 1231 | if (!getrootptr(&x, &y)) 1232 | return; 1233 | do { 1234 | XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); 1235 | switch(ev.type) { 1236 | case ConfigureRequest: 1237 | case Expose: 1238 | case MapRequest: 1239 | handler[ev.type](&ev); 1240 | break; 1241 | case MotionNotify: 1242 | if ((ev.xmotion.time - lasttime) <= (1000 / 60)) 1243 | continue; 1244 | lasttime = ev.xmotion.time; 1245 | 1246 | nx = ocx + (ev.xmotion.x - x); 1247 | ny = ocy + (ev.xmotion.y - y); 1248 | if (abs(selmon->wx - nx) < snap) 1249 | nx = selmon->wx; 1250 | else if (abs((selmon->wx + selmon->ww) - (nx + WIDTH(c))) < snap) 1251 | nx = selmon->wx + selmon->ww - WIDTH(c); 1252 | if (abs(selmon->wy - ny) < snap) 1253 | ny = selmon->wy; 1254 | else if (abs((selmon->wy + selmon->wh) - (ny + HEIGHT(c))) < snap) 1255 | ny = selmon->wy + selmon->wh - HEIGHT(c); 1256 | if (!c->isfloating && selmon->lt[selmon->sellt]->arrange 1257 | && (abs(nx - c->x) > snap || abs(ny - c->y) > snap)) 1258 | togglefloating(NULL); 1259 | if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) 1260 | resize(c, nx, ny, c->w, c->h, 1); 1261 | break; 1262 | } 1263 | } while (ev.type != ButtonRelease); 1264 | XUngrabPointer(dpy, CurrentTime); 1265 | if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { 1266 | sendmon(c, m); 1267 | selmon = m; 1268 | focus(NULL); 1269 | } 1270 | } 1271 | 1272 | Client * 1273 | nexttiled(Client *c) 1274 | { 1275 | for (; c && (c->isfloating || !ISVISIBLE(c)); c = c->next); 1276 | return c; 1277 | } 1278 | 1279 | void 1280 | pop(Client *c) 1281 | { 1282 | detach(c); 1283 | attach(c); 1284 | focus(c); 1285 | arrange(c->mon); 1286 | } 1287 | 1288 | void 1289 | propertynotify(XEvent *e) 1290 | { 1291 | Client *c; 1292 | Window trans; 1293 | XPropertyEvent *ev = &e->xproperty; 1294 | 1295 | if ((ev->window == root) && (ev->atom == XA_WM_NAME)) 1296 | updatestatus(); 1297 | else if (ev->state == PropertyDelete) 1298 | return; /* ignore */ 1299 | else if ((c = wintoclient(ev->window))) { 1300 | switch(ev->atom) { 1301 | default: break; 1302 | case XA_WM_TRANSIENT_FOR: 1303 | if (!c->isfloating && (XGetTransientForHint(dpy, c->win, &trans)) && 1304 | (c->isfloating = (wintoclient(trans)) != NULL)) 1305 | arrange(c->mon); 1306 | break; 1307 | case XA_WM_NORMAL_HINTS: 1308 | c->hintsvalid = 0; 1309 | break; 1310 | case XA_WM_HINTS: 1311 | updatewmhints(c); 1312 | drawbars(); 1313 | break; 1314 | } 1315 | if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { 1316 | updatetitle(c); 1317 | if (c == c->mon->sel) 1318 | drawbar(c->mon); 1319 | } 1320 | if (ev->atom == netatom[NetWMWindowType]) 1321 | updatewindowtype(c); 1322 | } 1323 | } 1324 | 1325 | void 1326 | quit(const Arg *arg) 1327 | { 1328 | running = 0; 1329 | } 1330 | 1331 | Monitor * 1332 | recttomon(int x, int y, int w, int h) 1333 | { 1334 | Monitor *m, *r = selmon; 1335 | int a, area = 0; 1336 | 1337 | for (m = mons; m; m = m->next) 1338 | if ((a = INTERSECT(x, y, w, h, m)) > area) { 1339 | area = a; 1340 | r = m; 1341 | } 1342 | return r; 1343 | } 1344 | 1345 | void 1346 | resize(Client *c, int x, int y, int w, int h, int interact) 1347 | { 1348 | if (applysizehints(c, &x, &y, &w, &h, interact)) 1349 | resizeclient(c, x, y, w, h); 1350 | } 1351 | 1352 | void 1353 | resizeclient(Client *c, int x, int y, int w, int h) 1354 | { 1355 | XWindowChanges wc; 1356 | 1357 | c->oldx = c->x; c->x = wc.x = x; 1358 | c->oldy = c->y; c->y = wc.y = y; 1359 | c->oldw = c->w; c->w = wc.width = w; 1360 | c->oldh = c->h; c->h = wc.height = h; 1361 | wc.border_width = c->bw; 1362 | XConfigureWindow(dpy, c->win, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc); 1363 | configure(c); 1364 | XSync(dpy, False); 1365 | } 1366 | 1367 | void 1368 | resizemouse(const Arg *arg) 1369 | { 1370 | int ocx, ocy, nw, nh; 1371 | Client *c; 1372 | Monitor *m; 1373 | XEvent ev; 1374 | Time lasttime = 0; 1375 | 1376 | if (!(c = selmon->sel)) 1377 | return; 1378 | if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */ 1379 | return; 1380 | restack(selmon); 1381 | ocx = c->x; 1382 | ocy = c->y; 1383 | if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, 1384 | None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess) 1385 | return; 1386 | XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); 1387 | do { 1388 | XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); 1389 | switch(ev.type) { 1390 | case ConfigureRequest: 1391 | case Expose: 1392 | case MapRequest: 1393 | handler[ev.type](&ev); 1394 | break; 1395 | case MotionNotify: 1396 | if ((ev.xmotion.time - lasttime) <= (1000 / 60)) 1397 | continue; 1398 | lasttime = ev.xmotion.time; 1399 | 1400 | nw = MAX(ev.xmotion.x - ocx - 2 * c->bw + 1, 1); 1401 | nh = MAX(ev.xmotion.y - ocy - 2 * c->bw + 1, 1); 1402 | if (c->mon->wx + nw >= selmon->wx && c->mon->wx + nw <= selmon->wx + selmon->ww 1403 | && c->mon->wy + nh >= selmon->wy && c->mon->wy + nh <= selmon->wy + selmon->wh) 1404 | { 1405 | if (!c->isfloating && selmon->lt[selmon->sellt]->arrange 1406 | && (abs(nw - c->w) > snap || abs(nh - c->h) > snap)) 1407 | togglefloating(NULL); 1408 | } 1409 | if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) 1410 | resize(c, c->x, c->y, nw, nh, 1); 1411 | break; 1412 | } 1413 | } while (ev.type != ButtonRelease); 1414 | XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); 1415 | XUngrabPointer(dpy, CurrentTime); 1416 | while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); 1417 | if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { 1418 | sendmon(c, m); 1419 | selmon = m; 1420 | focus(NULL); 1421 | } 1422 | } 1423 | 1424 | void 1425 | restack(Monitor *m) 1426 | { 1427 | Client *c; 1428 | XEvent ev; 1429 | XWindowChanges wc; 1430 | 1431 | drawbar(m); 1432 | if (!m->sel) 1433 | return; 1434 | if (m->sel->isfloating || !m->lt[m->sellt]->arrange) 1435 | XRaiseWindow(dpy, m->sel->win); 1436 | if (m->lt[m->sellt]->arrange) { 1437 | wc.stack_mode = Below; 1438 | wc.sibling = m->barwin; 1439 | for (c = m->stack; c; c = c->snext) 1440 | if (!c->isfloating && ISVISIBLE(c)) { 1441 | XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc); 1442 | wc.sibling = c->win; 1443 | } 1444 | } 1445 | XSync(dpy, False); 1446 | while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); 1447 | } 1448 | 1449 | void 1450 | run(void) 1451 | { 1452 | XEvent ev; 1453 | /* main event loop */ 1454 | XSync(dpy, False); 1455 | while (running && !XNextEvent(dpy, &ev)) 1456 | if (handler[ev.type]) 1457 | handler[ev.type](&ev); /* call handler */ 1458 | } 1459 | 1460 | void 1461 | scan(void) 1462 | { 1463 | unsigned int i, num; 1464 | Window d1, d2, *wins = NULL; 1465 | XWindowAttributes wa; 1466 | 1467 | if (XQueryTree(dpy, root, &d1, &d2, &wins, &num)) { 1468 | for (i = 0; i < num; i++) { 1469 | if (!XGetWindowAttributes(dpy, wins[i], &wa) 1470 | || wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1)) 1471 | continue; 1472 | if (wa.map_state == IsViewable || getstate(wins[i]) == IconicState) 1473 | manage(wins[i], &wa); 1474 | } 1475 | for (i = 0; i < num; i++) { /* now the transients */ 1476 | if (!XGetWindowAttributes(dpy, wins[i], &wa)) 1477 | continue; 1478 | if (XGetTransientForHint(dpy, wins[i], &d1) 1479 | && (wa.map_state == IsViewable || getstate(wins[i]) == IconicState)) 1480 | manage(wins[i], &wa); 1481 | } 1482 | if (wins) 1483 | XFree(wins); 1484 | } 1485 | } 1486 | 1487 | void 1488 | sendmon(Client *c, Monitor *m) 1489 | { 1490 | if (c->mon == m) 1491 | return; 1492 | unfocus(c, 1); 1493 | detach(c); 1494 | detachstack(c); 1495 | c->mon = m; 1496 | c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ 1497 | attach(c); 1498 | attachstack(c); 1499 | focus(NULL); 1500 | arrange(NULL); 1501 | } 1502 | 1503 | void 1504 | setclientstate(Client *c, long state) 1505 | { 1506 | long data[] = { state, None }; 1507 | 1508 | XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32, 1509 | PropModeReplace, (unsigned char *)data, 2); 1510 | } 1511 | 1512 | int 1513 | sendevent(Client *c, Atom proto) 1514 | { 1515 | int n; 1516 | Atom *protocols; 1517 | int exists = 0; 1518 | XEvent ev; 1519 | 1520 | if (XGetWMProtocols(dpy, c->win, &protocols, &n)) { 1521 | while (!exists && n--) 1522 | exists = protocols[n] == proto; 1523 | XFree(protocols); 1524 | } 1525 | if (exists) { 1526 | ev.type = ClientMessage; 1527 | ev.xclient.window = c->win; 1528 | ev.xclient.message_type = wmatom[WMProtocols]; 1529 | ev.xclient.format = 32; 1530 | ev.xclient.data.l[0] = proto; 1531 | ev.xclient.data.l[1] = CurrentTime; 1532 | XSendEvent(dpy, c->win, False, NoEventMask, &ev); 1533 | } 1534 | return exists; 1535 | } 1536 | 1537 | void 1538 | setfocus(Client *c) 1539 | { 1540 | if (!c->neverfocus) { 1541 | XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); 1542 | XChangeProperty(dpy, root, netatom[NetActiveWindow], 1543 | XA_WINDOW, 32, PropModeReplace, 1544 | (unsigned char *) &(c->win), 1); 1545 | } 1546 | sendevent(c, wmatom[WMTakeFocus]); 1547 | } 1548 | 1549 | void 1550 | setfullscreen(Client *c, int fullscreen) 1551 | { 1552 | if (fullscreen && !c->isfullscreen) { 1553 | XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, 1554 | PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1); 1555 | c->isfullscreen = 1; 1556 | c->oldstate = c->isfloating; 1557 | c->oldbw = c->bw; 1558 | c->bw = 0; 1559 | c->isfloating = 1; 1560 | resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); 1561 | XRaiseWindow(dpy, c->win); 1562 | } else if (!fullscreen && c->isfullscreen){ 1563 | XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, 1564 | PropModeReplace, (unsigned char*)0, 0); 1565 | c->isfullscreen = 0; 1566 | c->isfloating = c->oldstate; 1567 | c->bw = c->oldbw; 1568 | c->x = c->oldx; 1569 | c->y = c->oldy; 1570 | c->w = c->oldw; 1571 | c->h = c->oldh; 1572 | resizeclient(c, c->x, c->y, c->w, c->h); 1573 | arrange(c->mon); 1574 | } 1575 | } 1576 | 1577 | void 1578 | setlayout(const Arg *arg) 1579 | { 1580 | if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) 1581 | selmon->sellt ^= 1; 1582 | if (arg && arg->v) 1583 | selmon->lt[selmon->sellt] = (Layout *)arg->v; 1584 | strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol); 1585 | if (selmon->sel) 1586 | arrange(selmon); 1587 | else 1588 | drawbar(selmon); 1589 | } 1590 | 1591 | /* arg > 1.0 will set mfact absolutely */ 1592 | void 1593 | setmfact(const Arg *arg) 1594 | { 1595 | float f; 1596 | 1597 | if (!arg || !selmon->lt[selmon->sellt]->arrange) 1598 | return; 1599 | f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0; 1600 | if (f < 0.05 || f > 0.95) 1601 | return; 1602 | selmon->mfact = f; 1603 | arrange(selmon); 1604 | } 1605 | 1606 | void 1607 | setup(void) 1608 | { 1609 | int i; 1610 | XSetWindowAttributes wa; 1611 | Atom utf8string; 1612 | 1613 | /* clean up any zombies immediately */ 1614 | sigchld(0); 1615 | 1616 | /* init screen */ 1617 | screen = DefaultScreen(dpy); 1618 | sw = DisplayWidth(dpy, screen); 1619 | sh = DisplayHeight(dpy, screen); 1620 | root = RootWindow(dpy, screen); 1621 | drw = drw_create(dpy, screen, root, sw, sh); 1622 | if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) 1623 | die("no fonts could be loaded."); 1624 | lrpad = drw->fonts->h; 1625 | bh = drw->fonts->h + 2; 1626 | updategeom(); 1627 | /* init atoms */ 1628 | utf8string = XInternAtom(dpy, "UTF8_STRING", False); 1629 | wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); 1630 | wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); 1631 | wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False); 1632 | wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False); 1633 | netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); 1634 | netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); 1635 | netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); 1636 | netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False); 1637 | netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False); 1638 | netatom[NetWMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); 1639 | netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); 1640 | netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); 1641 | netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False); 1642 | /* init cursors */ 1643 | cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr); 1644 | cursor[CurResize] = drw_cur_create(drw, XC_sizing); 1645 | cursor[CurMove] = drw_cur_create(drw, XC_fleur); 1646 | /* init appearance */ 1647 | scheme = ecalloc(LENGTH(colors), sizeof(Clr *)); 1648 | for (i = 0; i < LENGTH(colors); i++) 1649 | scheme[i] = drw_scm_create(drw, colors[i], 3); 1650 | /* init bars */ 1651 | updatebars(); 1652 | updatestatus(); 1653 | /* supporting window for NetWMCheck */ 1654 | wmcheckwin = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 0, 0, 0); 1655 | XChangeProperty(dpy, wmcheckwin, netatom[NetWMCheck], XA_WINDOW, 32, 1656 | PropModeReplace, (unsigned char *) &wmcheckwin, 1); 1657 | XChangeProperty(dpy, wmcheckwin, netatom[NetWMName], utf8string, 8, 1658 | PropModeReplace, (unsigned char *) "dwm", 3); 1659 | XChangeProperty(dpy, root, netatom[NetWMCheck], XA_WINDOW, 32, 1660 | PropModeReplace, (unsigned char *) &wmcheckwin, 1); 1661 | /* EWMH support per view */ 1662 | XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32, 1663 | PropModeReplace, (unsigned char *) netatom, NetLast); 1664 | XDeleteProperty(dpy, root, netatom[NetClientList]); 1665 | /* select events */ 1666 | wa.cursor = cursor[CurNormal]->cursor; 1667 | wa.event_mask = SubstructureRedirectMask|SubstructureNotifyMask 1668 | |ButtonPressMask|PointerMotionMask|EnterWindowMask 1669 | |LeaveWindowMask|StructureNotifyMask|PropertyChangeMask; 1670 | XChangeWindowAttributes(dpy, root, CWEventMask|CWCursor, &wa); 1671 | XSelectInput(dpy, root, wa.event_mask); 1672 | grabkeys(); 1673 | focus(NULL); 1674 | } 1675 | 1676 | 1677 | void 1678 | seturgent(Client *c, int urg) 1679 | { 1680 | XWMHints *wmh; 1681 | 1682 | c->isurgent = urg; 1683 | if (!(wmh = XGetWMHints(dpy, c->win))) 1684 | return; 1685 | wmh->flags = urg ? (wmh->flags | XUrgencyHint) : (wmh->flags & ~XUrgencyHint); 1686 | XSetWMHints(dpy, c->win, wmh); 1687 | XFree(wmh); 1688 | } 1689 | 1690 | void 1691 | showhide(Client *c) 1692 | { 1693 | if (!c) 1694 | return; 1695 | if (ISVISIBLE(c)) { 1696 | /* show clients top down */ 1697 | XMoveWindow(dpy, c->win, c->x, c->y); 1698 | if ((!c->mon->lt[c->mon->sellt]->arrange || c->isfloating) && !c->isfullscreen) 1699 | resize(c, c->x, c->y, c->w, c->h, 0); 1700 | showhide(c->snext); 1701 | } else { 1702 | /* hide clients bottom up */ 1703 | showhide(c->snext); 1704 | XMoveWindow(dpy, c->win, WIDTH(c) * -2, c->y); 1705 | } 1706 | } 1707 | 1708 | void 1709 | sigchld(int unused) 1710 | { 1711 | if (signal(SIGCHLD, sigchld) == SIG_ERR) 1712 | die("can't install SIGCHLD handler:"); 1713 | while (0 < waitpid(-1, NULL, WNOHANG)); 1714 | } 1715 | 1716 | void 1717 | spawn(const Arg *arg) 1718 | { 1719 | if (arg->v == dmenucmd) 1720 | dmenumon[0] = '0' + selmon->num; 1721 | if (fork() == 0) { 1722 | if (dpy) 1723 | close(ConnectionNumber(dpy)); 1724 | setsid(); 1725 | execvp(((char **)arg->v)[0], (char **)arg->v); 1726 | fprintf(stderr, "dwm: execvp %s", ((char **)arg->v)[0]); 1727 | perror(" failed"); 1728 | exit(EXIT_SUCCESS); 1729 | } 1730 | } 1731 | 1732 | void 1733 | tag(const Arg *arg) 1734 | { 1735 | if (selmon->sel && arg->ui & TAGMASK) { 1736 | selmon->sel->tags = arg->ui & TAGMASK; 1737 | focus(NULL); 1738 | arrange(selmon); 1739 | } 1740 | } 1741 | 1742 | void 1743 | tagmon(const Arg *arg) 1744 | { 1745 | if (!selmon->sel || !mons->next) 1746 | return; 1747 | sendmon(selmon->sel, dirtomon(arg->i)); 1748 | } 1749 | 1750 | void 1751 | tile(Monitor *m) 1752 | { 1753 | unsigned int i, n, h, mw, my, ty; 1754 | Client *c; 1755 | 1756 | for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); 1757 | if (n == 0) 1758 | return; 1759 | 1760 | if (n > m->nmaster) 1761 | mw = m->nmaster ? m->ww * m->mfact : 0; 1762 | else 1763 | mw = m->ww; 1764 | for (i = my = ty = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) 1765 | if (i < m->nmaster) { 1766 | h = (m->wh - my) / (MIN(n, m->nmaster) - i); 1767 | resize(c, m->wx, m->wy + my, mw - (2*c->bw), h - (2*c->bw), 0); 1768 | if (my + HEIGHT(c) < m->wh) 1769 | my += HEIGHT(c); 1770 | } else { 1771 | h = (m->wh - ty) / (n - i); 1772 | resize(c, m->wx + mw, m->wy + ty, m->ww - mw - (2*c->bw), h - (2*c->bw), 0); 1773 | if (ty + HEIGHT(c) < m->wh) 1774 | ty += HEIGHT(c); 1775 | } 1776 | } 1777 | 1778 | void 1779 | magicgrid(Monitor *m) 1780 | { 1781 | grid(m, 12, 12); 1782 | } 1783 | 1784 | void 1785 | overview(Monitor *m) 1786 | { 1787 | grid(m, overviewgappo, overviewgappi); 1788 | } 1789 | 1790 | // 显示所有tag 或 跳转到聚焦窗口的tag 1791 | void 1792 | toggleoverview(const Arg *arg) 1793 | { 1794 | uint target = selmon->sel ? selmon->sel->tags : selmon->tagset[selmon->seltags]; 1795 | selmon->isoverview ^= 1; 1796 | view(&(Arg){ .ui = target }); 1797 | } 1798 | 1799 | void 1800 | grid(Monitor *m, uint gappo, uint gappi) 1801 | { 1802 | unsigned int i, n; 1803 | unsigned int cx, cy, cw, ch; 1804 | unsigned int dx; 1805 | unsigned int cols, rows, overcols; 1806 | Client *c; 1807 | 1808 | for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); 1809 | if (n == 0) return; 1810 | if (n == 1) { 1811 | c = nexttiled(m->clients); 1812 | cw = (m->ww - 2 * gappo) * 0.6; 1813 | ch = (m->wh - 2 * gappo) * 0.6; 1814 | resize(c, 1815 | m->mx + (m->mw - cw) / 2 + gappo, 1816 | m->my + (m->mh - ch) / 2 + gappo, 1817 | cw - 2 * c->bw, 1818 | ch - 2 * c->bw, 1819 | 0); 1820 | return; 1821 | } 1822 | if (n == 2) { 1823 | c = nexttiled(m->clients); 1824 | cw = (m->ww - 2 * gappo - gappi) / 2; 1825 | ch = (m->wh - 2 * gappo) * 0.6; 1826 | resize(c, 1827 | m->mx + gappo, 1828 | m->my + (m->mh - ch) / 2 + gappo, 1829 | cw - 2 * c->bw, 1830 | ch - 2 * c->bw, 1831 | 0); 1832 | resize(nexttiled(c->next), 1833 | m->mx + cw + gappo + gappi, 1834 | m->my + (m->mh - ch) / 2 + gappo, 1835 | cw - 2 * c->bw, 1836 | ch - 2 * c->bw, 1837 | 0); 1838 | return; 1839 | } 1840 | 1841 | for (cols = 0; cols <= n / 2; cols++) 1842 | if (cols * cols >= n) 1843 | break; 1844 | rows = (cols && (cols - 1) * cols >= n) ? cols - 1 : cols; 1845 | ch = (m->wh - 2 * gappo - (rows - 1) * gappi) / rows; 1846 | cw = (m->ww - 2 * gappo - (cols - 1) * gappi) / cols; 1847 | 1848 | overcols = n % cols; 1849 | if (overcols) 1850 | dx = (m->ww - overcols * cw - (overcols - 1) * gappi) / 2 - gappo; 1851 | for(i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) { 1852 | cx = m->wx + (i % cols) * (cw + gappi); 1853 | cy = m->wy + (i / cols) * (ch + gappi); 1854 | if (overcols && i >= n - overcols) { 1855 | cx += dx; 1856 | } 1857 | resize(c, 1858 | cx + gappo, 1859 | cy + gappo, 1860 | cw - 2 * c->bw, 1861 | ch - 2 * c->bw, 1862 | 0); 1863 | } 1864 | } 1865 | 1866 | void 1867 | togglebar(const Arg *arg) 1868 | { 1869 | selmon->showbar = !selmon->showbar; 1870 | updatebarpos(selmon); 1871 | XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); 1872 | arrange(selmon); 1873 | } 1874 | 1875 | void 1876 | togglefloating(const Arg *arg) 1877 | { 1878 | if (!selmon->sel) 1879 | return; 1880 | if (selmon->sel->isfullscreen) /* no support for fullscreen windows */ 1881 | return; 1882 | selmon->sel->isfloating = !selmon->sel->isfloating || selmon->sel->isfixed; 1883 | if (selmon->sel->isfloating) { 1884 | selmon->sel->x = selmon->wx + selmon->ww / 6, 1885 | selmon->sel->y = selmon->wy + selmon->wh / 6, 1886 | managefloating(selmon->sel); 1887 | resize(selmon->sel, selmon->sel->x, selmon->sel->y, selmon->ww / 3 * 2, selmon->wh / 3 * 2, 0); 1888 | } 1889 | arrange(selmon); 1890 | } 1891 | 1892 | void 1893 | toggletag(const Arg *arg) 1894 | { 1895 | unsigned int newtags; 1896 | 1897 | if (!selmon->sel) 1898 | return; 1899 | newtags = selmon->sel->tags ^ (arg->ui & TAGMASK); 1900 | if (newtags) { 1901 | selmon->sel->tags = newtags; 1902 | focus(NULL); 1903 | arrange(selmon); 1904 | } 1905 | } 1906 | 1907 | void 1908 | toggleview(const Arg *arg) 1909 | { 1910 | unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK); 1911 | 1912 | if (newtagset) { 1913 | selmon->tagset[selmon->seltags] = newtagset; 1914 | focus(NULL); 1915 | arrange(selmon); 1916 | } 1917 | } 1918 | 1919 | void 1920 | unfocus(Client *c, int setfocus) 1921 | { 1922 | if (!c) 1923 | return; 1924 | grabbuttons(c, 0); 1925 | XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel); 1926 | if (setfocus) { 1927 | XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); 1928 | XDeleteProperty(dpy, root, netatom[NetActiveWindow]); 1929 | } 1930 | } 1931 | 1932 | void 1933 | unmanage(Client *c, int destroyed) 1934 | { 1935 | Monitor *m = c->mon; 1936 | XWindowChanges wc; 1937 | 1938 | detach(c); 1939 | detachstack(c); 1940 | if (!destroyed) { 1941 | wc.border_width = c->oldbw; 1942 | XGrabServer(dpy); /* avoid race conditions */ 1943 | XSetErrorHandler(xerrordummy); 1944 | XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */ 1945 | XUngrabButton(dpy, AnyButton, AnyModifier, c->win); 1946 | setclientstate(c, WithdrawnState); 1947 | XSync(dpy, False); 1948 | XSetErrorHandler(xerror); 1949 | XUngrabServer(dpy); 1950 | } 1951 | free(c); 1952 | focus(NULL); 1953 | updateclientlist(); 1954 | arrange(m); 1955 | } 1956 | 1957 | void 1958 | unmapnotify(XEvent *e) 1959 | { 1960 | Client *c; 1961 | XUnmapEvent *ev = &e->xunmap; 1962 | 1963 | if ((c = wintoclient(ev->window))) { 1964 | if (ev->send_event) 1965 | setclientstate(c, WithdrawnState); 1966 | else 1967 | unmanage(c, 0); 1968 | } 1969 | } 1970 | 1971 | void 1972 | updatebars(void) 1973 | { 1974 | Monitor *m; 1975 | XSetWindowAttributes wa = { 1976 | .override_redirect = True, 1977 | .background_pixmap = ParentRelative, 1978 | .event_mask = ButtonPressMask|ExposureMask 1979 | }; 1980 | XClassHint ch = {"dwm", "dwm"}; 1981 | for (m = mons; m; m = m->next) { 1982 | if (m->barwin) 1983 | continue; 1984 | m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen), 1985 | CopyFromParent, DefaultVisual(dpy, screen), 1986 | CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); 1987 | XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); 1988 | XMapRaised(dpy, m->barwin); 1989 | XSetClassHint(dpy, m->barwin, &ch); 1990 | } 1991 | } 1992 | 1993 | void 1994 | updatebarpos(Monitor *m) 1995 | { 1996 | m->wy = m->my; 1997 | m->wh = m->mh; 1998 | if (m->showbar) { 1999 | m->wh -= bh; 2000 | m->by = m->topbar ? m->wy : m->wy + m->wh; 2001 | m->wy = m->topbar ? m->wy + bh : m->wy; 2002 | } else 2003 | m->by = -bh; 2004 | } 2005 | 2006 | void 2007 | updateclientlist() 2008 | { 2009 | Client *c; 2010 | Monitor *m; 2011 | 2012 | XDeleteProperty(dpy, root, netatom[NetClientList]); 2013 | for (m = mons; m; m = m->next) 2014 | for (c = m->clients; c; c = c->next) 2015 | XChangeProperty(dpy, root, netatom[NetClientList], 2016 | XA_WINDOW, 32, PropModeAppend, 2017 | (unsigned char *) &(c->win), 1); 2018 | } 2019 | 2020 | int 2021 | updategeom(void) 2022 | { 2023 | int dirty = 0; 2024 | 2025 | #ifdef XINERAMA 2026 | if (XineramaIsActive(dpy)) { 2027 | int i, j, n, nn; 2028 | Client *c; 2029 | Monitor *m; 2030 | XineramaScreenInfo *info = XineramaQueryScreens(dpy, &nn); 2031 | XineramaScreenInfo *unique = NULL; 2032 | 2033 | for (n = 0, m = mons; m; m = m->next, n++); 2034 | /* only consider unique geometries as separate screens */ 2035 | unique = ecalloc(nn, sizeof(XineramaScreenInfo)); 2036 | for (i = 0, j = 0; i < nn; i++) 2037 | if (isuniquegeom(unique, j, &info[i])) 2038 | memcpy(&unique[j++], &info[i], sizeof(XineramaScreenInfo)); 2039 | XFree(info); 2040 | nn = j; 2041 | 2042 | /* new monitors if nn > n */ 2043 | for (i = n; i < nn; i++) { 2044 | for (m = mons; m && m->next; m = m->next); 2045 | if (m) 2046 | m->next = createmon(); 2047 | else 2048 | mons = createmon(); 2049 | } 2050 | for (i = 0, m = mons; i < nn && m; m = m->next, i++) 2051 | if (i >= n 2052 | || unique[i].x_org != m->mx || unique[i].y_org != m->my 2053 | || unique[i].width != m->mw || unique[i].height != m->mh) 2054 | { 2055 | dirty = 1; 2056 | m->num = i; 2057 | m->mx = m->wx = unique[i].x_org; 2058 | m->my = m->wy = unique[i].y_org; 2059 | m->mw = m->ww = unique[i].width; 2060 | m->mh = m->wh = unique[i].height; 2061 | updatebarpos(m); 2062 | } 2063 | /* removed monitors if n > nn */ 2064 | for (i = nn; i < n; i++) { 2065 | for (m = mons; m && m->next; m = m->next); 2066 | while ((c = m->clients)) { 2067 | dirty = 1; 2068 | m->clients = c->next; 2069 | detachstack(c); 2070 | c->mon = mons; 2071 | attach(c); 2072 | attachstack(c); 2073 | } 2074 | if (m == selmon) 2075 | selmon = mons; 2076 | cleanupmon(m); 2077 | } 2078 | free(unique); 2079 | } else 2080 | #endif /* XINERAMA */ 2081 | { /* default monitor setup */ 2082 | if (!mons) 2083 | mons = createmon(); 2084 | if (mons->mw != sw || mons->mh != sh) { 2085 | dirty = 1; 2086 | mons->mw = mons->ww = sw; 2087 | mons->mh = mons->wh = sh; 2088 | updatebarpos(mons); 2089 | } 2090 | } 2091 | if (dirty) { 2092 | selmon = mons; 2093 | selmon = wintomon(root); 2094 | } 2095 | return dirty; 2096 | } 2097 | 2098 | void 2099 | updatenumlockmask(void) 2100 | { 2101 | unsigned int i, j; 2102 | XModifierKeymap *modmap; 2103 | 2104 | numlockmask = 0; 2105 | modmap = XGetModifierMapping(dpy); 2106 | for (i = 0; i < 8; i++) 2107 | for (j = 0; j < modmap->max_keypermod; j++) 2108 | if (modmap->modifiermap[i * modmap->max_keypermod + j] 2109 | == XKeysymToKeycode(dpy, XK_Num_Lock)) 2110 | numlockmask = (1 << i); 2111 | XFreeModifiermap(modmap); 2112 | } 2113 | 2114 | void 2115 | updatesizehints(Client *c) 2116 | { 2117 | long msize; 2118 | XSizeHints size; 2119 | 2120 | if (!XGetWMNormalHints(dpy, c->win, &size, &msize)) 2121 | /* size is uninitialized, ensure that size.flags aren't used */ 2122 | size.flags = PSize; 2123 | if (size.flags & PBaseSize) { 2124 | c->basew = size.base_width; 2125 | c->baseh = size.base_height; 2126 | } else if (size.flags & PMinSize) { 2127 | c->basew = size.min_width; 2128 | c->baseh = size.min_height; 2129 | } else 2130 | c->basew = c->baseh = 0; 2131 | if (size.flags & PResizeInc) { 2132 | c->incw = size.width_inc; 2133 | c->inch = size.height_inc; 2134 | } else 2135 | c->incw = c->inch = 0; 2136 | if (size.flags & PMaxSize) { 2137 | c->maxw = size.max_width; 2138 | c->maxh = size.max_height; 2139 | } else 2140 | c->maxw = c->maxh = 0; 2141 | if (size.flags & PMinSize) { 2142 | c->minw = size.min_width; 2143 | c->minh = size.min_height; 2144 | } else if (size.flags & PBaseSize) { 2145 | c->minw = size.base_width; 2146 | c->minh = size.base_height; 2147 | } else 2148 | c->minw = c->minh = 0; 2149 | if (size.flags & PAspect) { 2150 | c->mina = (float)size.min_aspect.y / size.min_aspect.x; 2151 | c->maxa = (float)size.max_aspect.x / size.max_aspect.y; 2152 | } else 2153 | c->maxa = c->mina = 0.0; 2154 | c->isfixed = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh); 2155 | c->hintsvalid = 1; 2156 | } 2157 | 2158 | void 2159 | updatestatus(void) 2160 | { 2161 | if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) 2162 | strcpy(stext, "dwm-"VERSION); 2163 | drawbar(selmon); 2164 | } 2165 | 2166 | void 2167 | updatetitle(Client *c) 2168 | { 2169 | if (!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name)) 2170 | gettextprop(c->win, XA_WM_NAME, c->name, sizeof c->name); 2171 | if (c->name[0] == '\0') /* hack to mark broken clients */ 2172 | strcpy(c->name, broken); 2173 | } 2174 | 2175 | void 2176 | updatewindowtype(Client *c) 2177 | { 2178 | Atom state = getatomprop(c, netatom[NetWMState]); 2179 | Atom wtype = getatomprop(c, netatom[NetWMWindowType]); 2180 | 2181 | if (state == netatom[NetWMFullscreen]) 2182 | setfullscreen(c, 1); 2183 | if (wtype == netatom[NetWMWindowTypeDialog]) 2184 | c->isfloating = 1; 2185 | } 2186 | 2187 | void 2188 | updatewmhints(Client *c) 2189 | { 2190 | XWMHints *wmh; 2191 | 2192 | if ((wmh = XGetWMHints(dpy, c->win))) { 2193 | if (c == selmon->sel && wmh->flags & XUrgencyHint) { 2194 | wmh->flags &= ~XUrgencyHint; 2195 | XSetWMHints(dpy, c->win, wmh); 2196 | } else 2197 | c->isurgent = (wmh->flags & XUrgencyHint) ? 1 : 0; 2198 | if (wmh->flags & InputHint) 2199 | c->neverfocus = !wmh->input; 2200 | else 2201 | c->neverfocus = 0; 2202 | XFree(wmh); 2203 | } 2204 | } 2205 | 2206 | void 2207 | view(const Arg *arg) 2208 | { 2209 | if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) { 2210 | arrange(selmon); 2211 | return; 2212 | } 2213 | selmon->seltags ^= 1; /* toggle sel tagset */ 2214 | if (arg->ui & TAGMASK) 2215 | selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; 2216 | focus(NULL); 2217 | arrange(selmon); 2218 | } 2219 | 2220 | Client * 2221 | wintoclient(Window w) 2222 | { 2223 | Client *c; 2224 | Monitor *m; 2225 | 2226 | for (m = mons; m; m = m->next) 2227 | for (c = m->clients; c; c = c->next) 2228 | if (c->win == w) 2229 | return c; 2230 | return NULL; 2231 | } 2232 | 2233 | Monitor * 2234 | wintomon(Window w) 2235 | { 2236 | int x, y; 2237 | Client *c; 2238 | Monitor *m; 2239 | 2240 | if (w == root && getrootptr(&x, &y)) 2241 | return recttomon(x, y, 1, 1); 2242 | for (m = mons; m; m = m->next) 2243 | if (w == m->barwin) 2244 | return m; 2245 | if ((c = wintoclient(w))) 2246 | return c->mon; 2247 | return selmon; 2248 | } 2249 | 2250 | /* There's no way to check accesses to destroyed windows, thus those cases are 2251 | * ignored (especially on UnmapNotify's). Other types of errors call Xlibs 2252 | * default error handler, which may call exit. */ 2253 | int 2254 | xerror(Display *dpy, XErrorEvent *ee) 2255 | { 2256 | if (ee->error_code == BadWindow 2257 | || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch) 2258 | || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable) 2259 | || (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable) 2260 | || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable) 2261 | || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch) 2262 | || (ee->request_code == X_GrabButton && ee->error_code == BadAccess) 2263 | || (ee->request_code == X_GrabKey && ee->error_code == BadAccess) 2264 | || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable)) 2265 | return 0; 2266 | fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n", 2267 | ee->request_code, ee->error_code); 2268 | return xerrorxlib(dpy, ee); /* may call exit */ 2269 | } 2270 | 2271 | int 2272 | xerrordummy(Display *dpy, XErrorEvent *ee) 2273 | { 2274 | return 0; 2275 | } 2276 | 2277 | /* Startup Error handler to check if another window manager 2278 | * is already running. */ 2279 | int 2280 | xerrorstart(Display *dpy, XErrorEvent *ee) 2281 | { 2282 | die("dwm: another window manager is already running"); 2283 | return -1; 2284 | } 2285 | 2286 | void 2287 | zoom(const Arg *arg) 2288 | { 2289 | Client *c = selmon->sel; 2290 | 2291 | if (!selmon->lt[selmon->sellt]->arrange 2292 | || (selmon->sel && selmon->sel->isfloating)) 2293 | return; 2294 | if (c == nexttiled(selmon->clients)) 2295 | if (!c || !(c = nexttiled(c->next))) 2296 | return; 2297 | pop(c); 2298 | } 2299 | 2300 | int 2301 | main(int argc, char *argv[]) 2302 | { 2303 | if (argc == 2 && !strcmp("-v", argv[1])) 2304 | die("dwm-"VERSION); 2305 | else if (argc != 1) 2306 | die("usage: dwm [-v]"); 2307 | if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) 2308 | fputs("warning: no locale support\n", stderr); 2309 | if (!(dpy = XOpenDisplay(NULL))) 2310 | die("dwm: cannot open display"); 2311 | checkotherwm(); 2312 | setup(); 2313 | #ifdef __OpenBSD__ 2314 | if (pledge("stdio rpath proc exec", NULL) == -1) 2315 | die("pledge"); 2316 | #endif /* __OpenBSD__ */ 2317 | scan(); 2318 | run(); 2319 | cleanup(); 2320 | XCloseDisplay(dpy); 2321 | return EXIT_SUCCESS; 2322 | } 2323 | -------------------------------------------------------------------------------- /dwm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yaocccc/dwms/f438b3bd3d9e79fb0fa11072d6f5fe1afc5ef495/dwm.png -------------------------------------------------------------------------------- /transient.c: -------------------------------------------------------------------------------- 1 | /* cc transient.c -o transient -lX11 */ 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int main(void) { 9 | Display *d; 10 | Window r, f, t = None; 11 | XSizeHints h; 12 | XEvent e; 13 | 14 | d = XOpenDisplay(NULL); 15 | if (!d) 16 | exit(1); 17 | r = DefaultRootWindow(d); 18 | 19 | f = XCreateSimpleWindow(d, r, 100, 100, 400, 400, 0, 0, 0); 20 | h.min_width = h.max_width = h.min_height = h.max_height = 400; 21 | h.flags = PMinSize | PMaxSize; 22 | XSetWMNormalHints(d, f, &h); 23 | XStoreName(d, f, "floating"); 24 | XMapWindow(d, f); 25 | 26 | XSelectInput(d, f, ExposureMask); 27 | while (1) { 28 | XNextEvent(d, &e); 29 | 30 | if (t == None) { 31 | sleep(5); 32 | t = XCreateSimpleWindow(d, r, 50, 50, 100, 100, 0, 0, 0); 33 | XSetTransientForHint(d, t, f); 34 | XStoreName(d, t, "transient"); 35 | XMapWindow(d, t); 36 | XSelectInput(d, t, ExposureMask); 37 | } 38 | } 39 | 40 | XCloseDisplay(d); 41 | exit(0); 42 | } 43 | -------------------------------------------------------------------------------- /util.c: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "util.h" 8 | 9 | void * 10 | ecalloc(size_t nmemb, size_t size) 11 | { 12 | void *p; 13 | 14 | if (!(p = calloc(nmemb, size))) 15 | die("calloc:"); 16 | return p; 17 | } 18 | 19 | void 20 | die(const char *fmt, ...) { 21 | va_list ap; 22 | 23 | va_start(ap, fmt); 24 | vfprintf(stderr, fmt, ap); 25 | va_end(ap); 26 | 27 | if (fmt[0] && fmt[strlen(fmt)-1] == ':') { 28 | fputc(' ', stderr); 29 | perror(NULL); 30 | } else { 31 | fputc('\n', stderr); 32 | } 33 | 34 | exit(1); 35 | } 36 | -------------------------------------------------------------------------------- /util.h: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | 3 | #define MAX(A, B) ((A) > (B) ? (A) : (B)) 4 | #define MIN(A, B) ((A) < (B) ? (A) : (B)) 5 | #define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B)) 6 | 7 | void die(const char *fmt, ...); 8 | void *ecalloc(size_t nmemb, size_t size); 9 | --------------------------------------------------------------------------------