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