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