From e46fd11730ef3f5bfaeb794096544e2c50fe765b Mon Sep 17 00:00:00 2001 From: TolaMironcenko Date: Mon, 1 Jul 2024 09:11:12 +0700 Subject: [PATCH] minimal dwm added --- dwm/LICENSE | 38 + dwm/Makefile | 45 + dwm/README | 48 + dwm/config.def.h | 116 +++ dwm/config.h | 117 +++ dwm/config.mk | 39 + dwm/drw.c | 450 ++++++++++ dwm/drw.h | 58 ++ dwm/drw.o | Bin 0 -> 11000 bytes dwm/dwm | Bin 0 -> 63216 bytes dwm/dwm.1 | 176 ++++ dwm/dwm.c | 2165 ++++++++++++++++++++++++++++++++++++++++++++++ dwm/dwm.o | Bin 0 -> 57760 bytes dwm/dwm.png | Bin 0 -> 373 bytes dwm/startdwm.sh | 10 + dwm/transient.c | 42 + dwm/util.c | 36 + dwm/util.h | 8 + dwm/util.o | Bin 0 -> 2224 bytes 19 files changed, 3348 insertions(+) create mode 100644 dwm/LICENSE create mode 100644 dwm/Makefile create mode 100644 dwm/README create mode 100644 dwm/config.def.h create mode 100644 dwm/config.h create mode 100644 dwm/config.mk create mode 100644 dwm/drw.c create mode 100644 dwm/drw.h create mode 100644 dwm/drw.o create mode 100755 dwm/dwm create mode 100644 dwm/dwm.1 create mode 100644 dwm/dwm.c create mode 100644 dwm/dwm.o create mode 100644 dwm/dwm.png create mode 100755 dwm/startdwm.sh create mode 100644 dwm/transient.c create mode 100644 dwm/util.c create mode 100644 dwm/util.h create mode 100644 dwm/util.o diff --git a/dwm/LICENSE b/dwm/LICENSE new file mode 100644 index 0000000..995172f --- /dev/null +++ b/dwm/LICENSE @@ -0,0 +1,38 @@ +MIT/X Consortium License + +© 2006-2019 Anselm R Garbe +© 2006-2009 Jukka Salmi +© 2006-2007 Sander van Dijk +© 2007-2011 Peter Hartlich +© 2007-2009 Szabolcs Nagy +© 2007-2009 Christof Musik +© 2007-2009 Premysl Hruby +© 2007-2008 Enno Gottox Boland +© 2008 Martin Hurton +© 2008 Neale Pickett +© 2009 Mate Nagy +© 2010-2016 Hiltjo Posthuma +© 2010-2012 Connor Lane Smith +© 2011 Christoph Lohmann <20h@r-36.net> +© 2015-2016 Quentin Rameau +© 2015-2016 Eric Pruitt +© 2016-2017 Markus Teich +© 2020-2022 Chris Down + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/dwm/Makefile b/dwm/Makefile new file mode 100644 index 0000000..ffa69b4 --- /dev/null +++ b/dwm/Makefile @@ -0,0 +1,45 @@ +# dwm - dynamic window manager +# See LICENSE file for copyright and license details. + +include config.mk + +SRC = drw.c dwm.c util.c +OBJ = ${SRC:.c=.o} + +all: dwm + +.c.o: + ${CC} -c ${CFLAGS} $< + +${OBJ}: config.h config.mk + +config.h: + cp config.def.h $@ + +dwm: ${OBJ} + ${CC} -o $@ ${OBJ} ${LDFLAGS} + +clean: + rm -f dwm ${OBJ} dwm-${VERSION}.tar.gz + +dist: clean + mkdir -p dwm-${VERSION} + cp -R LICENSE Makefile README config.def.h config.mk\ + dwm.1 drw.h util.h ${SRC} dwm.png transient.c dwm-${VERSION} + tar -cf dwm-${VERSION}.tar dwm-${VERSION} + gzip dwm-${VERSION}.tar + rm -rf dwm-${VERSION} + +install: all + mkdir -p ${DESTDIR}${PREFIX}/bin + cp -f dwm ${DESTDIR}${PREFIX}/bin + chmod 755 ${DESTDIR}${PREFIX}/bin/dwm + mkdir -p ${DESTDIR}${MANPREFIX}/man1 + sed "s/VERSION/${VERSION}/g" < dwm.1 > ${DESTDIR}${MANPREFIX}/man1/dwm.1 + chmod 644 ${DESTDIR}${MANPREFIX}/man1/dwm.1 + +uninstall: + rm -f ${DESTDIR}${PREFIX}/bin/dwm\ + ${DESTDIR}${MANPREFIX}/man1/dwm.1 + +.PHONY: all clean dist install uninstall diff --git a/dwm/README b/dwm/README new file mode 100644 index 0000000..95d4fd0 --- /dev/null +++ b/dwm/README @@ -0,0 +1,48 @@ +dwm - dynamic window manager +============================ +dwm is an extremely fast, small, and dynamic window manager for X. + + +Requirements +------------ +In order to build dwm you need the Xlib header files. + + +Installation +------------ +Edit config.mk to match your local setup (dwm is installed into +the /usr/local namespace by default). + +Afterwards enter the following command to build and install dwm (if +necessary as root): + + make clean install + + +Running dwm +----------- +Add the following line to your .xinitrc to start dwm using startx: + + exec dwm + +In order to connect dwm to a specific display, make sure that +the DISPLAY environment variable is set correctly, e.g.: + + DISPLAY=foo.bar:1 exec dwm + +(This will start dwm on display :1 of the host foo.bar.) + +In order to display status info in the bar, you can do something +like this in your .xinitrc: + + while xsetroot -name "`date` `uptime | sed 's/.*,//'`" + do + sleep 1 + done & + exec dwm + + +Configuration +------------- +The configuration of dwm is done by creating a custom config.h +and (re)compiling the source code. diff --git a/dwm/config.def.h b/dwm/config.def.h new file mode 100644 index 0000000..9efa774 --- /dev/null +++ b/dwm/config.def.h @@ -0,0 +1,116 @@ +/* See LICENSE file for copyright and license details. */ + +/* appearance */ +static const unsigned int borderpx = 1; /* border pixel of windows */ +static const unsigned int snap = 32; /* snap pixel */ +static const int showbar = 1; /* 0 means no bar */ +static const int topbar = 1; /* 0 means bottom bar */ +static const char *fonts[] = { "monospace:size=10" }; +static const char dmenufont[] = "monospace:size=10"; +static const char col_gray1[] = "#222222"; +static const char col_gray2[] = "#444444"; +static const char col_gray3[] = "#bbbbbb"; +static const char col_gray4[] = "#eeeeee"; +static const char col_cyan[] = "#005577"; +static const char *colors[][3] = { + /* fg bg border */ + [SchemeNorm] = { col_gray3, col_gray1, col_gray2 }, + [SchemeSel] = { col_gray4, col_cyan, col_cyan }, +}; + +/* tagging */ +static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; + +static const Rule rules[] = { + /* xprop(1): + * WM_CLASS(STRING) = instance, class + * WM_NAME(STRING) = title + */ + /* class instance title tags mask isfloating monitor */ + { "Gimp", NULL, NULL, 0, 1, -1 }, + { "Firefox", NULL, NULL, 1 << 8, 0, -1 }, +}; + +/* layout(s) */ +static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */ +static const int nmaster = 1; /* number of clients in master area */ +static const int resizehints = 1; /* 1 means respect size hints in tiled resizals */ +static const int lockfullscreen = 1; /* 1 will force focus on the fullscreen window */ + +static const Layout layouts[] = { + /* symbol arrange function */ + { "[]=", tile }, /* first entry is default */ + { "><>", NULL }, /* no layout function means floating behavior */ + { "[M]", monocle }, +}; + +/* key definitions */ +#define MODKEY Mod1Mask +#define TAGKEYS(KEY,TAG) \ + { MODKEY, KEY, view, {.ui = 1 << TAG} }, \ + { MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \ + { MODKEY|ShiftMask, KEY, tag, {.ui = 1 << TAG} }, \ + { MODKEY|ControlMask|ShiftMask, KEY, toggletag, {.ui = 1 << TAG} }, + +/* helper for spawning shell commands in the pre dwm-5.0 fashion */ +#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } } + +/* commands */ +static char dmenumon[2] = "0"; /* component of dmenucmd, manipulated in spawn() */ +static const char *dmenucmd[] = { "dmenu_run", "-m", dmenumon, "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL }; +static const char *termcmd[] = { "st", NULL }; + +static const Key keys[] = { + /* modifier key function argument */ + { MODKEY, XK_p, spawn, {.v = dmenucmd } }, + { MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } }, + { MODKEY, XK_b, togglebar, {0} }, + { MODKEY, XK_j, focusstack, {.i = +1 } }, + { MODKEY, XK_k, focusstack, {.i = -1 } }, + { MODKEY, XK_i, incnmaster, {.i = +1 } }, + { MODKEY, XK_d, incnmaster, {.i = -1 } }, + { MODKEY, XK_h, setmfact, {.f = -0.05} }, + { MODKEY, XK_l, setmfact, {.f = +0.05} }, + { MODKEY, XK_Return, zoom, {0} }, + { MODKEY, XK_Tab, view, {0} }, + { MODKEY|ShiftMask, XK_c, killclient, {0} }, + { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, + { MODKEY, XK_f, setlayout, {.v = &layouts[1]} }, + { MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, + { MODKEY, XK_space, setlayout, {0} }, + { MODKEY|ShiftMask, XK_space, togglefloating, {0} }, + { MODKEY, XK_0, view, {.ui = ~0 } }, + { MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } }, + { MODKEY, XK_comma, focusmon, {.i = -1 } }, + { MODKEY, XK_period, focusmon, {.i = +1 } }, + { MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } }, + { MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } }, + TAGKEYS( XK_1, 0) + TAGKEYS( XK_2, 1) + TAGKEYS( XK_3, 2) + TAGKEYS( XK_4, 3) + TAGKEYS( XK_5, 4) + TAGKEYS( XK_6, 5) + TAGKEYS( XK_7, 6) + TAGKEYS( XK_8, 7) + TAGKEYS( XK_9, 8) + { MODKEY|ShiftMask, XK_q, quit, {0} }, +}; + +/* button definitions */ +/* click can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */ +static const Button buttons[] = { + /* click event mask button function argument */ + { ClkLtSymbol, 0, Button1, setlayout, {0} }, + { ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} }, + { ClkWinTitle, 0, Button2, zoom, {0} }, + { ClkStatusText, 0, Button2, spawn, {.v = termcmd } }, + { ClkClientWin, MODKEY, Button1, movemouse, {0} }, + { ClkClientWin, MODKEY, Button2, togglefloating, {0} }, + { ClkClientWin, MODKEY, Button3, resizemouse, {0} }, + { ClkTagBar, 0, Button1, view, {0} }, + { ClkTagBar, 0, Button3, toggleview, {0} }, + { ClkTagBar, MODKEY, Button1, tag, {0} }, + { ClkTagBar, MODKEY, Button3, toggletag, {0} }, +}; + diff --git a/dwm/config.h b/dwm/config.h new file mode 100644 index 0000000..30273f0 --- /dev/null +++ b/dwm/config.h @@ -0,0 +1,117 @@ +/* See LICENSE file for copyright and license details. */ + +/* appearance */ +static const unsigned int borderpx = 2; /* border pixel of windows */ +static const unsigned int snap = 32; /* snap pixel */ +static const int showbar = 1; /* 0 means no bar */ +static const int topbar = 1; /* 0 means bottom bar */ +static const char *fonts[] = { "JetBrains Mono Nerd:size=10" }; +static const char dmenufont[] = "JetBrains Mono Nerd:size=10"; +static const char col_gray1[] = "#222222"; +static const char col_gray2[] = "#444444"; +static const char col_gray3[] = "#bbbbbb"; +static const char col_gray4[] = "#eeeeee"; +static const char col_cyan[] = "#005577"; +static const char *colors[][3] = { + /* fg bg border */ + [SchemeNorm] = { col_gray3, col_gray1, col_gray2 }, + [SchemeSel] = { col_gray4, col_cyan, col_cyan }, +}; + +/* tagging */ +static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; + +static const Rule rules[] = { + /* xprop(1): + * WM_CLASS(STRING) = instance, class + * WM_NAME(STRING) = title + */ + /* class instance title tags mask isfloating monitor */ + { "Gimp", NULL, NULL, 0, 1, -1 }, + { "Firefox", NULL, NULL, 1 << 8, 0, -1 }, +}; + +/* layout(s) */ +static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */ +static const int nmaster = 1; /* number of clients in master area */ +static const int resizehints = 1; /* 1 means respect size hints in tiled resizals */ +static const int lockfullscreen = 1; /* 1 will force focus on the fullscreen window */ + +static const Layout layouts[] = { + /* symbol arrange function */ + { "[]=", tile }, /* first entry is default */ + { "><>", NULL }, /* no layout function means floating behavior */ + { "[M]", monocle }, +}; + +/* key definitions */ +#define MODKEY Mod1Mask +#define TAGKEYS(KEY,TAG) \ + { MODKEY, KEY, view, {.ui = 1 << TAG} }, \ + { MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \ + { MODKEY|ShiftMask, KEY, tag, {.ui = 1 << TAG} }, \ + { MODKEY|ControlMask|ShiftMask, KEY, toggletag, {.ui = 1 << TAG} }, + +/* helper for spawning shell commands in the pre dwm-5.0 fashion */ +#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } } + +/* commands */ +static char dmenumon[2] = "0"; /* component of dmenucmd, manipulated in spawn() */ +static const char *dmenucmd[] = { "dmenu_run", "-m", dmenumon, "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL }; +static const char *termcmd[] = { "kitty", NULL }; +static const char *roficmd[] = { "/home/tola/dotfiles/rofi/launcher/launcher.sh" }; + +static const Key keys[] = { + /* modifier key function argument */ + { MODKEY, XK_p, spawn, {.v = roficmd } }, + { MODKEY, XK_Return, spawn, {.v = termcmd } }, + { MODKEY, XK_b, togglebar, {0} }, + { MODKEY, XK_j, focusstack, {.i = +1 } }, + { MODKEY, XK_k, focusstack, {.i = -1 } }, + { MODKEY, XK_i, incnmaster, {.i = +1 } }, + { MODKEY, XK_d, incnmaster, {.i = -1 } }, + { MODKEY, XK_h, setmfact, {.f = -0.05} }, + { MODKEY, XK_l, setmfact, {.f = +0.05} }, + // { MODKEY, XK_Return, zoom, {0} }, + { MODKEY, XK_Tab, view, {0} }, + { MODKEY, XK_q, killclient, {0} }, + { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, + { MODKEY, XK_f, setlayout, {.v = &layouts[1]} }, + { MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, + { MODKEY, XK_space, setlayout, {0} }, + { MODKEY|ShiftMask, XK_space, togglefloating, {0} }, + { MODKEY, XK_0, view, {.ui = ~0 } }, + { MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } }, + { MODKEY, XK_comma, focusmon, {.i = -1 } }, + { MODKEY, XK_period, focusmon, {.i = +1 } }, + { MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } }, + { MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } }, + TAGKEYS( XK_1, 0) + TAGKEYS( XK_2, 1) + TAGKEYS( XK_3, 2) + TAGKEYS( XK_4, 3) + TAGKEYS( XK_5, 4) + TAGKEYS( XK_6, 5) + TAGKEYS( XK_7, 6) + TAGKEYS( XK_8, 7) + TAGKEYS( XK_9, 8) + { MODKEY|ShiftMask, XK_q, quit, {0} }, +}; + +/* button definitions */ +/* click can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */ +static const Button buttons[] = { + /* click event mask button function argument */ + { ClkLtSymbol, 0, Button1, setlayout, {0} }, + { ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} }, + { ClkWinTitle, 0, Button2, zoom, {0} }, + { ClkStatusText, 0, Button2, spawn, {.v = termcmd } }, + { ClkClientWin, MODKEY, Button1, movemouse, {0} }, + { ClkClientWin, MODKEY, Button2, togglefloating, {0} }, + { ClkClientWin, MODKEY, Button3, resizemouse, {0} }, + { ClkTagBar, 0, Button1, view, {0} }, + { ClkTagBar, 0, Button3, toggleview, {0} }, + { ClkTagBar, MODKEY, Button1, tag, {0} }, + { ClkTagBar, MODKEY, Button3, toggletag, {0} }, +}; + diff --git a/dwm/config.mk b/dwm/config.mk new file mode 100644 index 0000000..8efca9a --- /dev/null +++ b/dwm/config.mk @@ -0,0 +1,39 @@ +# dwm version +VERSION = 6.5 + +# Customize below to fit your system + +# paths +PREFIX = /usr/local +MANPREFIX = ${PREFIX}/share/man + +X11INC = /usr/X11R6/include +X11LIB = /usr/X11R6/lib + +# Xinerama, comment if you don't want it +XINERAMALIBS = -lXinerama +XINERAMAFLAGS = -DXINERAMA + +# freetype +FREETYPELIBS = -lfontconfig -lXft +FREETYPEINC = /usr/include/freetype2 +# OpenBSD (uncomment) +#FREETYPEINC = ${X11INC}/freetype2 +#MANPREFIX = ${PREFIX}/man + +# includes and libs +INCS = -I${X11INC} -I${FREETYPEINC} +LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} + +# flags +CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} +#CFLAGS = -g -std=c99 -pedantic -Wall -O0 ${INCS} ${CPPFLAGS} +CFLAGS = -std=c99 -pedantic -Wall -Wno-deprecated-declarations -Os ${INCS} ${CPPFLAGS} +LDFLAGS = ${LIBS} + +# Solaris +#CFLAGS = -fast ${INCS} -DVERSION=\"${VERSION}\" +#LDFLAGS = ${LIBS} + +# compiler and linker +CC = cc diff --git a/dwm/drw.c b/dwm/drw.c new file mode 100644 index 0000000..a58a2b4 --- /dev/null +++ b/dwm/drw.c @@ -0,0 +1,450 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include +#include +#include +#include + +#include "drw.h" +#include "util.h" + +#define UTF_INVALID 0xFFFD +#define UTF_SIZ 4 + +static const unsigned char utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; +static const unsigned char utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; +static const long utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000}; +static const long utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; + +static long +utf8decodebyte(const char c, size_t *i) +{ + for (*i = 0; *i < (UTF_SIZ + 1); ++(*i)) + if (((unsigned char)c & utfmask[*i]) == utfbyte[*i]) + return (unsigned char)c & ~utfmask[*i]; + return 0; +} + +static size_t +utf8validate(long *u, size_t i) +{ + if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF)) + *u = UTF_INVALID; + for (i = 1; *u > utfmax[i]; ++i) + ; + return i; +} + +static size_t +utf8decode(const char *c, long *u, size_t clen) +{ + size_t i, j, len, type; + long udecoded; + + *u = UTF_INVALID; + if (!clen) + return 0; + udecoded = utf8decodebyte(c[0], &len); + if (!BETWEEN(len, 1, UTF_SIZ)) + return 1; + for (i = 1, j = 1; i < clen && j < len; ++i, ++j) { + udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type); + if (type) + return j; + } + if (j < len) + return 0; + *u = udecoded; + utf8validate(u, len); + + return len; +} + +Drw * +drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h) +{ + Drw *drw = ecalloc(1, sizeof(Drw)); + + drw->dpy = dpy; + drw->screen = screen; + drw->root = root; + drw->w = w; + drw->h = h; + drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen)); + drw->gc = XCreateGC(dpy, root, 0, NULL); + XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter); + + return drw; +} + +void +drw_resize(Drw *drw, unsigned int w, unsigned int h) +{ + if (!drw) + return; + + drw->w = w; + drw->h = h; + if (drw->drawable) + XFreePixmap(drw->dpy, drw->drawable); + drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen)); +} + +void +drw_free(Drw *drw) +{ + XFreePixmap(drw->dpy, drw->drawable); + XFreeGC(drw->dpy, drw->gc); + drw_fontset_free(drw->fonts); + free(drw); +} + +/* This function is an implementation detail. Library users should use + * drw_fontset_create instead. + */ +static Fnt * +xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern) +{ + Fnt *font; + XftFont *xfont = NULL; + FcPattern *pattern = NULL; + + if (fontname) { + /* Using the pattern found at font->xfont->pattern does not yield the + * same substitution results as using the pattern returned by + * FcNameParse; using the latter results in the desired fallback + * behaviour whereas the former just results in missing-character + * rectangles being drawn, at least with some fonts. */ + if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fontname))) { + fprintf(stderr, "error, cannot load font from name: '%s'\n", fontname); + return NULL; + } + if (!(pattern = FcNameParse((FcChar8 *) fontname))) { + fprintf(stderr, "error, cannot parse font name to pattern: '%s'\n", fontname); + XftFontClose(drw->dpy, xfont); + return NULL; + } + } else if (fontpattern) { + if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) { + fprintf(stderr, "error, cannot load font from pattern.\n"); + return NULL; + } + } else { + die("no font specified."); + } + + font = ecalloc(1, sizeof(Fnt)); + font->xfont = xfont; + font->pattern = pattern; + font->h = xfont->ascent + xfont->descent; + font->dpy = drw->dpy; + + return font; +} + +static void +xfont_free(Fnt *font) +{ + if (!font) + return; + if (font->pattern) + FcPatternDestroy(font->pattern); + XftFontClose(font->dpy, font->xfont); + free(font); +} + +Fnt* +drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount) +{ + Fnt *cur, *ret = NULL; + size_t i; + + if (!drw || !fonts) + return NULL; + + for (i = 1; i <= fontcount; i++) { + if ((cur = xfont_create(drw, fonts[fontcount - i], NULL))) { + cur->next = ret; + ret = cur; + } + } + return (drw->fonts = ret); +} + +void +drw_fontset_free(Fnt *font) +{ + if (font) { + drw_fontset_free(font->next); + xfont_free(font); + } +} + +void +drw_clr_create(Drw *drw, Clr *dest, const char *clrname) +{ + if (!drw || !dest || !clrname) + return; + + if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen), + DefaultColormap(drw->dpy, drw->screen), + clrname, dest)) + die("error, cannot allocate color '%s'", clrname); +} + +/* Wrapper to create color schemes. The caller has to call free(3) on the + * returned color scheme when done using it. */ +Clr * +drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount) +{ + size_t i; + Clr *ret; + + /* need at least two colors for a scheme */ + if (!drw || !clrnames || clrcount < 2 || !(ret = ecalloc(clrcount, sizeof(XftColor)))) + return NULL; + + for (i = 0; i < clrcount; i++) + drw_clr_create(drw, &ret[i], clrnames[i]); + return ret; +} + +void +drw_setfontset(Drw *drw, Fnt *set) +{ + if (drw) + drw->fonts = set; +} + +void +drw_setscheme(Drw *drw, Clr *scm) +{ + if (drw) + drw->scheme = scm; +} + +void +drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert) +{ + if (!drw || !drw->scheme) + return; + XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme[ColBg].pixel : drw->scheme[ColFg].pixel); + if (filled) + XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); + else + XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w - 1, h - 1); +} + +int +drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert) +{ + int i, ty, ellipsis_x = 0; + unsigned int tmpw, ew, ellipsis_w = 0, ellipsis_len; + XftDraw *d = NULL; + Fnt *usedfont, *curfont, *nextfont; + int utf8strlen, utf8charlen, render = x || y || w || h; + long utf8codepoint = 0; + const char *utf8str; + FcCharSet *fccharset; + FcPattern *fcpattern; + FcPattern *match; + XftResult result; + int charexists = 0, overflow = 0; + /* keep track of a couple codepoints for which we have no match. */ + enum { nomatches_len = 64 }; + static struct { long codepoint[nomatches_len]; unsigned int idx; } nomatches; + static unsigned int ellipsis_width = 0; + + if (!drw || (render && (!drw->scheme || !w)) || !text || !drw->fonts) + return 0; + + if (!render) { + w = invert ? invert : ~invert; + } else { + XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel); + XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); + d = XftDrawCreate(drw->dpy, drw->drawable, + DefaultVisual(drw->dpy, drw->screen), + DefaultColormap(drw->dpy, drw->screen)); + x += lpad; + w -= lpad; + } + + usedfont = drw->fonts; + if (!ellipsis_width && render) + ellipsis_width = drw_fontset_getwidth(drw, "..."); + while (1) { + ew = ellipsis_len = utf8strlen = 0; + utf8str = text; + nextfont = NULL; + while (*text) { + utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ); + for (curfont = drw->fonts; curfont; curfont = curfont->next) { + charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint); + if (charexists) { + drw_font_getexts(curfont, text, utf8charlen, &tmpw, NULL); + if (ew + ellipsis_width <= w) { + /* keep track where the ellipsis still fits */ + ellipsis_x = x + ew; + ellipsis_w = w - ew; + ellipsis_len = utf8strlen; + } + + if (ew + tmpw > w) { + overflow = 1; + /* called from drw_fontset_getwidth_clamp(): + * it wants the width AFTER the overflow + */ + if (!render) + x += tmpw; + else + utf8strlen = ellipsis_len; + } else if (curfont == usedfont) { + utf8strlen += utf8charlen; + text += utf8charlen; + ew += tmpw; + } else { + nextfont = curfont; + } + break; + } + } + + if (overflow || !charexists || nextfont) + break; + else + charexists = 0; + } + + if (utf8strlen) { + if (render) { + ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent; + XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg], + usedfont->xfont, x, ty, (XftChar8 *)utf8str, utf8strlen); + } + x += ew; + w -= ew; + } + if (render && overflow) + drw_text(drw, ellipsis_x, y, ellipsis_w, h, 0, "...", invert); + + if (!*text || overflow) { + break; + } else if (nextfont) { + charexists = 0; + usedfont = nextfont; + } else { + /* Regardless of whether or not a fallback font is found, the + * character must be drawn. */ + charexists = 1; + + for (i = 0; i < nomatches_len; ++i) { + /* avoid calling XftFontMatch if we know we won't find a match */ + if (utf8codepoint == nomatches.codepoint[i]) + goto no_match; + } + + fccharset = FcCharSetCreate(); + FcCharSetAddChar(fccharset, utf8codepoint); + + if (!drw->fonts->pattern) { + /* Refer to the comment in xfont_create for more information. */ + die("the first font in the cache must be loaded from a font string."); + } + + fcpattern = FcPatternDuplicate(drw->fonts->pattern); + FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset); + FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue); + + FcConfigSubstitute(NULL, fcpattern, FcMatchPattern); + FcDefaultSubstitute(fcpattern); + match = XftFontMatch(drw->dpy, drw->screen, fcpattern, &result); + + FcCharSetDestroy(fccharset); + FcPatternDestroy(fcpattern); + + if (match) { + usedfont = xfont_create(drw, NULL, match); + if (usedfont && XftCharExists(drw->dpy, usedfont->xfont, utf8codepoint)) { + for (curfont = drw->fonts; curfont->next; curfont = curfont->next) + ; /* NOP */ + curfont->next = usedfont; + } else { + xfont_free(usedfont); + nomatches.codepoint[++nomatches.idx % nomatches_len] = utf8codepoint; +no_match: + usedfont = drw->fonts; + } + } + } + } + if (d) + XftDrawDestroy(d); + + return x + (render ? w : 0); +} + +void +drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h) +{ + if (!drw) + return; + + XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y); + XSync(drw->dpy, False); +} + +unsigned int +drw_fontset_getwidth(Drw *drw, const char *text) +{ + if (!drw || !drw->fonts || !text) + return 0; + return drw_text(drw, 0, 0, 0, 0, 0, text, 0); +} + +unsigned int +drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n) +{ + unsigned int tmp = 0; + if (drw && drw->fonts && text && n) + tmp = drw_text(drw, 0, 0, 0, 0, 0, text, n); + return MIN(n, tmp); +} + +void +drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h) +{ + XGlyphInfo ext; + + if (!font || !text) + return; + + XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext); + if (w) + *w = ext.xOff; + if (h) + *h = font->h; +} + +Cur * +drw_cur_create(Drw *drw, int shape) +{ + Cur *cur; + + if (!drw || !(cur = ecalloc(1, sizeof(Cur)))) + return NULL; + + cur->cursor = XCreateFontCursor(drw->dpy, shape); + + return cur; +} + +void +drw_cur_free(Drw *drw, Cur *cursor) +{ + if (!cursor) + return; + + XFreeCursor(drw->dpy, cursor->cursor); + free(cursor); +} diff --git a/dwm/drw.h b/dwm/drw.h new file mode 100644 index 0000000..6471431 --- /dev/null +++ b/dwm/drw.h @@ -0,0 +1,58 @@ +/* See LICENSE file for copyright and license details. */ + +typedef struct { + Cursor cursor; +} Cur; + +typedef struct Fnt { + Display *dpy; + unsigned int h; + XftFont *xfont; + FcPattern *pattern; + struct Fnt *next; +} Fnt; + +enum { ColFg, ColBg, ColBorder }; /* Clr scheme index */ +typedef XftColor Clr; + +typedef struct { + unsigned int w, h; + Display *dpy; + int screen; + Window root; + Drawable drawable; + GC gc; + Clr *scheme; + Fnt *fonts; +} Drw; + +/* Drawable abstraction */ +Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h); +void drw_resize(Drw *drw, unsigned int w, unsigned int h); +void drw_free(Drw *drw); + +/* Fnt abstraction */ +Fnt *drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount); +void drw_fontset_free(Fnt* set); +unsigned int drw_fontset_getwidth(Drw *drw, const char *text); +unsigned int drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n); +void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h); + +/* Colorscheme abstraction */ +void drw_clr_create(Drw *drw, Clr *dest, const char *clrname); +Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount); + +/* Cursor abstraction */ +Cur *drw_cur_create(Drw *drw, int shape); +void drw_cur_free(Drw *drw, Cur *cursor); + +/* Drawing context manipulation */ +void drw_setfontset(Drw *drw, Fnt *set); +void drw_setscheme(Drw *drw, Clr *scm); + +/* Drawing functions */ +void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert); +int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert); + +/* Map functions */ +void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h); diff --git a/dwm/drw.o b/dwm/drw.o new file mode 100644 index 0000000000000000000000000000000000000000..6e37c6a5203a60c6d1452401519e3c9d2046144c GIT binary patch literal 11000 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef XINERAMA +#include +#endif /* XINERAMA */ +#include + +#include "drw.h" +#include "util.h" + +/* macros */ +#define BUTTONMASK (ButtonPressMask|ButtonReleaseMask) +#define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)) +#define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \ + * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy))) +#define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags])) +#define LENGTH(X) (sizeof X / sizeof X[0]) +#define MOUSEMASK (BUTTONMASK|PointerMotionMask) +#define WIDTH(X) ((X)->w + 2 * (X)->bw) +#define HEIGHT(X) ((X)->h + 2 * (X)->bw) +#define TAGMASK ((1 << LENGTH(tags)) - 1) +#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) + +/* enums */ +enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ +enum { SchemeNorm, SchemeSel }; /* color schemes */ +enum { NetSupported, NetWMName, NetWMState, NetWMCheck, + NetWMFullscreen, NetActiveWindow, NetWMWindowType, + NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ +enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ +enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, + ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ + +typedef union { + int i; + unsigned int ui; + float f; + const void *v; +} Arg; + +typedef struct { + unsigned int click; + unsigned int mask; + unsigned int button; + void (*func)(const Arg *arg); + const Arg arg; +} Button; + +typedef struct Monitor Monitor; +typedef struct Client Client; +struct Client { + char name[256]; + float mina, maxa; + int x, y, w, h; + int oldx, oldy, oldw, oldh; + int basew, baseh, incw, inch, maxw, maxh, minw, minh, hintsvalid; + int bw, oldbw; + unsigned int tags; + int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen; + Client *next; + Client *snext; + Monitor *mon; + Window win; +}; + +typedef struct { + unsigned int mod; + KeySym keysym; + void (*func)(const Arg *); + const Arg arg; +} Key; + +typedef struct { + const char *symbol; + void (*arrange)(Monitor *); +} Layout; + +struct Monitor { + char ltsymbol[16]; + float mfact; + int nmaster; + int num; + int by; /* bar geometry */ + int mx, my, mw, mh; /* screen size */ + int wx, wy, ww, wh; /* window area */ + unsigned int seltags; + unsigned int sellt; + unsigned int tagset[2]; + int showbar; + int topbar; + Client *clients; + Client *sel; + Client *stack; + Monitor *next; + Window barwin; + const Layout *lt[2]; +}; + +typedef struct { + const char *class; + const char *instance; + const char *title; + unsigned int tags; + int isfloating; + int monitor; +} Rule; + +/* function declarations */ +static void applyrules(Client *c); +static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact); +static void arrange(Monitor *m); +static void arrangemon(Monitor *m); +static void attach(Client *c); +static void attachstack(Client *c); +static void buttonpress(XEvent *e); +static void checkotherwm(void); +static void cleanup(void); +static void cleanupmon(Monitor *mon); +static void clientmessage(XEvent *e); +static void configure(Client *c); +static void configurenotify(XEvent *e); +static void configurerequest(XEvent *e); +static Monitor *createmon(void); +static void destroynotify(XEvent *e); +static void detach(Client *c); +static void detachstack(Client *c); +static Monitor *dirtomon(int dir); +static void drawbar(Monitor *m); +static void drawbars(void); +static void enternotify(XEvent *e); +static void expose(XEvent *e); +static void focus(Client *c); +static void focusin(XEvent *e); +static void focusmon(const Arg *arg); +static void focusstack(const Arg *arg); +static Atom getatomprop(Client *c, Atom prop); +static int getrootptr(int *x, int *y); +static long getstate(Window w); +static int gettextprop(Window w, Atom atom, char *text, unsigned int size); +static void grabbuttons(Client *c, int focused); +static void grabkeys(void); +static void incnmaster(const Arg *arg); +static void keypress(XEvent *e); +static void killclient(const Arg *arg); +static void manage(Window w, XWindowAttributes *wa); +static void mappingnotify(XEvent *e); +static void maprequest(XEvent *e); +static void monocle(Monitor *m); +static void motionnotify(XEvent *e); +static void movemouse(const Arg *arg); +static Client *nexttiled(Client *c); +static void pop(Client *c); +static void propertynotify(XEvent *e); +static void quit(const Arg *arg); +static Monitor *recttomon(int x, int y, int w, int h); +static void resize(Client *c, int x, int y, int w, int h, int interact); +static void resizeclient(Client *c, int x, int y, int w, int h); +static void resizemouse(const Arg *arg); +static void restack(Monitor *m); +static void run(void); +static void scan(void); +static int sendevent(Client *c, Atom proto); +static void sendmon(Client *c, Monitor *m); +static void setclientstate(Client *c, long state); +static void setfocus(Client *c); +static void setfullscreen(Client *c, int fullscreen); +static void setlayout(const Arg *arg); +static void setmfact(const Arg *arg); +static void setup(void); +static void seturgent(Client *c, int urg); +static void showhide(Client *c); +static void spawn(const Arg *arg); +static void tag(const Arg *arg); +static void tagmon(const Arg *arg); +static void tile(Monitor *m); +static void togglebar(const Arg *arg); +static void togglefloating(const Arg *arg); +static void toggletag(const Arg *arg); +static void toggleview(const Arg *arg); +static void unfocus(Client *c, int setfocus); +static void unmanage(Client *c, int destroyed); +static void unmapnotify(XEvent *e); +static void updatebarpos(Monitor *m); +static void updatebars(void); +static void updateclientlist(void); +static int updategeom(void); +static void updatenumlockmask(void); +static void updatesizehints(Client *c); +static void updatestatus(void); +static void updatetitle(Client *c); +static void updatewindowtype(Client *c); +static void updatewmhints(Client *c); +static void view(const Arg *arg); +static Client *wintoclient(Window w); +static Monitor *wintomon(Window w); +static int xerror(Display *dpy, XErrorEvent *ee); +static int xerrordummy(Display *dpy, XErrorEvent *ee); +static int xerrorstart(Display *dpy, XErrorEvent *ee); +static void zoom(const Arg *arg); + +/* variables */ +static const char broken[] = "broken"; +static char stext[256]; +static int screen; +static int sw, sh; /* X display screen geometry width, height */ +static int bh; /* bar height */ +static int lrpad; /* sum of left and right padding for text */ +static int (*xerrorxlib)(Display *, XErrorEvent *); +static unsigned int numlockmask = 0; +static void (*handler[LASTEvent]) (XEvent *) = { + [ButtonPress] = buttonpress, + [ClientMessage] = clientmessage, + [ConfigureRequest] = configurerequest, + [ConfigureNotify] = configurenotify, + [DestroyNotify] = destroynotify, + [EnterNotify] = enternotify, + [Expose] = expose, + [FocusIn] = focusin, + [KeyPress] = keypress, + [MappingNotify] = mappingnotify, + [MapRequest] = maprequest, + [MotionNotify] = motionnotify, + [PropertyNotify] = propertynotify, + [UnmapNotify] = unmapnotify +}; +static Atom wmatom[WMLast], netatom[NetLast]; +static int running = 1; +static Cur *cursor[CurLast]; +static Clr **scheme; +static Display *dpy; +static Drw *drw; +static Monitor *mons, *selmon; +static Window root, wmcheckwin; + +/* configuration, allows nested code to access above variables */ +#include "config.h" + +/* compile-time check if all tags fit into an unsigned int bit array. */ +struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; + +/* function implementations */ +void +applyrules(Client *c) +{ + const char *class, *instance; + unsigned int i; + const Rule *r; + Monitor *m; + XClassHint ch = { NULL, NULL }; + + /* rule matching */ + c->isfloating = 0; + c->tags = 0; + XGetClassHint(dpy, c->win, &ch); + class = ch.res_class ? ch.res_class : broken; + instance = ch.res_name ? ch.res_name : broken; + + for (i = 0; i < LENGTH(rules); i++) { + r = &rules[i]; + if ((!r->title || strstr(c->name, r->title)) + && (!r->class || strstr(class, r->class)) + && (!r->instance || strstr(instance, r->instance))) + { + c->isfloating = r->isfloating; + c->tags |= r->tags; + for (m = mons; m && m->num != r->monitor; m = m->next); + if (m) + c->mon = m; + } + } + if (ch.res_class) + XFree(ch.res_class); + if (ch.res_name) + XFree(ch.res_name); + c->tags = c->tags & TAGMASK ? c->tags & TAGMASK : c->mon->tagset[c->mon->seltags]; +} + +int +applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact) +{ + int baseismin; + Monitor *m = c->mon; + + /* set minimum possible */ + *w = MAX(1, *w); + *h = MAX(1, *h); + if (interact) { + if (*x > sw) + *x = sw - WIDTH(c); + if (*y > sh) + *y = sh - HEIGHT(c); + if (*x + *w + 2 * c->bw < 0) + *x = 0; + if (*y + *h + 2 * c->bw < 0) + *y = 0; + } else { + if (*x >= m->wx + m->ww) + *x = m->wx + m->ww - WIDTH(c); + if (*y >= m->wy + m->wh) + *y = m->wy + m->wh - HEIGHT(c); + if (*x + *w + 2 * c->bw <= m->wx) + *x = m->wx; + if (*y + *h + 2 * c->bw <= m->wy) + *y = m->wy; + } + if (*h < bh) + *h = bh; + if (*w < bh) + *w = bh; + if (resizehints || c->isfloating || !c->mon->lt[c->mon->sellt]->arrange) { + if (!c->hintsvalid) + updatesizehints(c); + /* see last two sentences in ICCCM 4.1.2.3 */ + baseismin = c->basew == c->minw && c->baseh == c->minh; + if (!baseismin) { /* temporarily remove base dimensions */ + *w -= c->basew; + *h -= c->baseh; + } + /* adjust for aspect limits */ + if (c->mina > 0 && c->maxa > 0) { + if (c->maxa < (float)*w / *h) + *w = *h * c->maxa + 0.5; + else if (c->mina < (float)*h / *w) + *h = *w * c->mina + 0.5; + } + if (baseismin) { /* increment calculation requires this */ + *w -= c->basew; + *h -= c->baseh; + } + /* adjust for increment value */ + if (c->incw) + *w -= *w % c->incw; + if (c->inch) + *h -= *h % c->inch; + /* restore base dimensions */ + *w = MAX(*w + c->basew, c->minw); + *h = MAX(*h + c->baseh, c->minh); + if (c->maxw) + *w = MIN(*w, c->maxw); + if (c->maxh) + *h = MIN(*h, c->maxh); + } + return *x != c->x || *y != c->y || *w != c->w || *h != c->h; +} + +void +arrange(Monitor *m) +{ + if (m) + showhide(m->stack); + else for (m = mons; m; m = m->next) + showhide(m->stack); + if (m) { + arrangemon(m); + restack(m); + } else for (m = mons; m; m = m->next) + arrangemon(m); +} + +void +arrangemon(Monitor *m) +{ + strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol); + if (m->lt[m->sellt]->arrange) + m->lt[m->sellt]->arrange(m); +} + +void +attach(Client *c) +{ + c->next = c->mon->clients; + c->mon->clients = c; +} + +void +attachstack(Client *c) +{ + c->snext = c->mon->stack; + c->mon->stack = c; +} + +void +buttonpress(XEvent *e) +{ + unsigned int i, x, click; + Arg arg = {0}; + Client *c; + Monitor *m; + XButtonPressedEvent *ev = &e->xbutton; + + click = ClkRootWin; + /* focus monitor if necessary */ + if ((m = wintomon(ev->window)) && m != selmon) { + unfocus(selmon->sel, 1); + selmon = m; + focus(NULL); + } + if (ev->window == selmon->barwin) { + i = x = 0; + do + x += TEXTW(tags[i]); + while (ev->x >= x && ++i < LENGTH(tags)); + if (i < LENGTH(tags)) { + click = ClkTagBar; + arg.ui = 1 << i; + } else if (ev->x < x + TEXTW(selmon->ltsymbol)) + click = ClkLtSymbol; + else if (ev->x > selmon->ww - (int)TEXTW(stext)) + click = ClkStatusText; + else + click = ClkWinTitle; + } else if ((c = wintoclient(ev->window))) { + focus(c); + restack(selmon); + XAllowEvents(dpy, ReplayPointer, CurrentTime); + click = ClkClientWin; + } + for (i = 0; i < LENGTH(buttons); i++) + if (click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button + && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) + buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg); +} + +void +checkotherwm(void) +{ + xerrorxlib = XSetErrorHandler(xerrorstart); + /* this causes an error if some other window manager is running */ + XSelectInput(dpy, DefaultRootWindow(dpy), SubstructureRedirectMask); + XSync(dpy, False); + XSetErrorHandler(xerror); + XSync(dpy, False); +} + +void +cleanup(void) +{ + Arg a = {.ui = ~0}; + Layout foo = { "", NULL }; + Monitor *m; + size_t i; + + view(&a); + selmon->lt[selmon->sellt] = &foo; + for (m = mons; m; m = m->next) + while (m->stack) + unmanage(m->stack, 0); + XUngrabKey(dpy, AnyKey, AnyModifier, root); + while (mons) + cleanupmon(mons); + for (i = 0; i < CurLast; i++) + drw_cur_free(drw, cursor[i]); + for (i = 0; i < LENGTH(colors); i++) + free(scheme[i]); + free(scheme); + XDestroyWindow(dpy, wmcheckwin); + drw_free(drw); + XSync(dpy, False); + XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); + XDeleteProperty(dpy, root, netatom[NetActiveWindow]); +} + +void +cleanupmon(Monitor *mon) +{ + Monitor *m; + + if (mon == mons) + mons = mons->next; + else { + for (m = mons; m && m->next != mon; m = m->next); + m->next = mon->next; + } + XUnmapWindow(dpy, mon->barwin); + XDestroyWindow(dpy, mon->barwin); + free(mon); +} + +void +clientmessage(XEvent *e) +{ + XClientMessageEvent *cme = &e->xclient; + Client *c = wintoclient(cme->window); + + if (!c) + return; + if (cme->message_type == netatom[NetWMState]) { + if (cme->data.l[1] == netatom[NetWMFullscreen] + || cme->data.l[2] == netatom[NetWMFullscreen]) + setfullscreen(c, (cme->data.l[0] == 1 /* _NET_WM_STATE_ADD */ + || (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */ && !c->isfullscreen))); + } else if (cme->message_type == netatom[NetActiveWindow]) { + if (c != selmon->sel && !c->isurgent) + seturgent(c, 1); + } +} + +void +configure(Client *c) +{ + XConfigureEvent ce; + + ce.type = ConfigureNotify; + ce.display = dpy; + ce.event = c->win; + ce.window = c->win; + ce.x = c->x; + ce.y = c->y; + ce.width = c->w; + ce.height = c->h; + ce.border_width = c->bw; + ce.above = None; + ce.override_redirect = False; + XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce); +} + +void +configurenotify(XEvent *e) +{ + Monitor *m; + Client *c; + XConfigureEvent *ev = &e->xconfigure; + int dirty; + + /* TODO: updategeom handling sucks, needs to be simplified */ + if (ev->window == root) { + dirty = (sw != ev->width || sh != ev->height); + sw = ev->width; + sh = ev->height; + if (updategeom() || dirty) { + drw_resize(drw, sw, bh); + updatebars(); + for (m = mons; m; m = m->next) { + for (c = m->clients; c; c = c->next) + if (c->isfullscreen) + resizeclient(c, m->mx, m->my, m->mw, m->mh); + XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); + } + focus(NULL); + arrange(NULL); + } + } +} + +void +configurerequest(XEvent *e) +{ + Client *c; + Monitor *m; + XConfigureRequestEvent *ev = &e->xconfigurerequest; + XWindowChanges wc; + + if ((c = wintoclient(ev->window))) { + if (ev->value_mask & CWBorderWidth) + c->bw = ev->border_width; + else if (c->isfloating || !selmon->lt[selmon->sellt]->arrange) { + m = c->mon; + if (ev->value_mask & CWX) { + c->oldx = c->x; + c->x = m->mx + ev->x; + } + if (ev->value_mask & CWY) { + c->oldy = c->y; + c->y = m->my + ev->y; + } + if (ev->value_mask & CWWidth) { + c->oldw = c->w; + c->w = ev->width; + } + if (ev->value_mask & CWHeight) { + c->oldh = c->h; + c->h = ev->height; + } + if ((c->x + c->w) > m->mx + m->mw && c->isfloating) + c->x = m->mx + (m->mw / 2 - WIDTH(c) / 2); /* center in x direction */ + if ((c->y + c->h) > m->my + m->mh && c->isfloating) + c->y = m->my + (m->mh / 2 - HEIGHT(c) / 2); /* center in y direction */ + if ((ev->value_mask & (CWX|CWY)) && !(ev->value_mask & (CWWidth|CWHeight))) + configure(c); + if (ISVISIBLE(c)) + XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); + } else + configure(c); + } else { + wc.x = ev->x; + wc.y = ev->y; + wc.width = ev->width; + wc.height = ev->height; + wc.border_width = ev->border_width; + wc.sibling = ev->above; + wc.stack_mode = ev->detail; + XConfigureWindow(dpy, ev->window, ev->value_mask, &wc); + } + XSync(dpy, False); +} + +Monitor * +createmon(void) +{ + Monitor *m; + + m = ecalloc(1, sizeof(Monitor)); + m->tagset[0] = m->tagset[1] = 1; + m->mfact = mfact; + m->nmaster = nmaster; + m->showbar = showbar; + m->topbar = topbar; + m->lt[0] = &layouts[0]; + m->lt[1] = &layouts[1 % LENGTH(layouts)]; + strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); + return m; +} + +void +destroynotify(XEvent *e) +{ + Client *c; + XDestroyWindowEvent *ev = &e->xdestroywindow; + + if ((c = wintoclient(ev->window))) + unmanage(c, 1); +} + +void +detach(Client *c) +{ + Client **tc; + + for (tc = &c->mon->clients; *tc && *tc != c; tc = &(*tc)->next); + *tc = c->next; +} + +void +detachstack(Client *c) +{ + Client **tc, *t; + + for (tc = &c->mon->stack; *tc && *tc != c; tc = &(*tc)->snext); + *tc = c->snext; + + if (c == c->mon->sel) { + for (t = c->mon->stack; t && !ISVISIBLE(t); t = t->snext); + c->mon->sel = t; + } +} + +Monitor * +dirtomon(int dir) +{ + Monitor *m = NULL; + + if (dir > 0) { + if (!(m = selmon->next)) + m = mons; + } else if (selmon == mons) + for (m = mons; m->next; m = m->next); + else + for (m = mons; m->next != selmon; m = m->next); + return m; +} + +void +drawbar(Monitor *m) +{ + int x, w, tw = 0; + int boxs = drw->fonts->h / 9; + int boxw = drw->fonts->h / 6 + 2; + unsigned int i, occ = 0, urg = 0; + Client *c; + + if (!m->showbar) + return; + + /* draw status first so it can be overdrawn by tags later */ + if (m == selmon) { /* status is only drawn on selected monitor */ + drw_setscheme(drw, scheme[SchemeNorm]); + tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ + drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0); + } + + for (c = m->clients; c; c = c->next) { + occ |= c->tags; + if (c->isurgent) + urg |= c->tags; + } + x = 0; + for (i = 0; i < LENGTH(tags); i++) { + w = TEXTW(tags[i]); + drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]); + drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i); + if (occ & 1 << i) + drw_rect(drw, x + boxs, boxs, boxw, boxw, + m == selmon && selmon->sel && selmon->sel->tags & 1 << i, + urg & 1 << i); + x += w; + } + w = TEXTW(m->ltsymbol); + drw_setscheme(drw, scheme[SchemeNorm]); + x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); + + if ((w = m->ww - tw - x) > bh) { + if (m->sel) { + drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); + drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); + if (m->sel->isfloating) + drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0); + } else { + drw_setscheme(drw, scheme[SchemeNorm]); + drw_rect(drw, x, 0, w, bh, 1, 1); + } + } + drw_map(drw, m->barwin, 0, 0, m->ww, bh); +} + +void +drawbars(void) +{ + Monitor *m; + + for (m = mons; m; m = m->next) + drawbar(m); +} + +void +enternotify(XEvent *e) +{ + Client *c; + Monitor *m; + XCrossingEvent *ev = &e->xcrossing; + + if ((ev->mode != NotifyNormal || ev->detail == NotifyInferior) && ev->window != root) + return; + c = wintoclient(ev->window); + m = c ? c->mon : wintomon(ev->window); + if (m != selmon) { + unfocus(selmon->sel, 1); + selmon = m; + } else if (!c || c == selmon->sel) + return; + focus(c); +} + +void +expose(XEvent *e) +{ + Monitor *m; + XExposeEvent *ev = &e->xexpose; + + if (ev->count == 0 && (m = wintomon(ev->window))) + drawbar(m); +} + +void +focus(Client *c) +{ + if (!c || !ISVISIBLE(c)) + for (c = selmon->stack; c && !ISVISIBLE(c); c = c->snext); + if (selmon->sel && selmon->sel != c) + unfocus(selmon->sel, 0); + if (c) { + if (c->mon != selmon) + selmon = c->mon; + if (c->isurgent) + seturgent(c, 0); + detachstack(c); + attachstack(c); + grabbuttons(c, 1); + XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel); + setfocus(c); + } else { + XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); + XDeleteProperty(dpy, root, netatom[NetActiveWindow]); + } + selmon->sel = c; + drawbars(); +} + +/* there are some broken focus acquiring clients needing extra handling */ +void +focusin(XEvent *e) +{ + XFocusChangeEvent *ev = &e->xfocus; + + if (selmon->sel && ev->window != selmon->sel->win) + setfocus(selmon->sel); +} + +void +focusmon(const Arg *arg) +{ + Monitor *m; + + if (!mons->next) + return; + if ((m = dirtomon(arg->i)) == selmon) + return; + unfocus(selmon->sel, 0); + selmon = m; + focus(NULL); +} + +void +focusstack(const Arg *arg) +{ + Client *c = NULL, *i; + + if (!selmon->sel || (selmon->sel->isfullscreen && lockfullscreen)) + return; + if (arg->i > 0) { + for (c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next); + if (!c) + for (c = selmon->clients; c && !ISVISIBLE(c); c = c->next); + } else { + for (i = selmon->clients; i != selmon->sel; i = i->next) + if (ISVISIBLE(i)) + c = i; + if (!c) + for (; i; i = i->next) + if (ISVISIBLE(i)) + c = i; + } + if (c) { + focus(c); + restack(selmon); + } +} + +Atom +getatomprop(Client *c, Atom prop) +{ + int di; + unsigned long dl; + unsigned char *p = NULL; + Atom da, atom = None; + + if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM, + &da, &di, &dl, &dl, &p) == Success && p) { + atom = *(Atom *)p; + XFree(p); + } + return atom; +} + +int +getrootptr(int *x, int *y) +{ + int di; + unsigned int dui; + Window dummy; + + return XQueryPointer(dpy, root, &dummy, &dummy, x, y, &di, &di, &dui); +} + +long +getstate(Window w) +{ + int format; + long result = -1; + unsigned char *p = NULL; + unsigned long n, extra; + Atom real; + + if (XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState], + &real, &format, &n, &extra, (unsigned char **)&p) != Success) + return -1; + if (n != 0) + result = *p; + XFree(p); + return result; +} + +int +gettextprop(Window w, Atom atom, char *text, unsigned int size) +{ + char **list = NULL; + int n; + XTextProperty name; + + if (!text || size == 0) + return 0; + text[0] = '\0'; + if (!XGetTextProperty(dpy, w, &name, atom) || !name.nitems) + return 0; + if (name.encoding == XA_STRING) { + strncpy(text, (char *)name.value, size - 1); + } else if (XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success && n > 0 && *list) { + strncpy(text, *list, size - 1); + XFreeStringList(list); + } + text[size - 1] = '\0'; + XFree(name.value); + return 1; +} + +void +grabbuttons(Client *c, int focused) +{ + updatenumlockmask(); + { + unsigned int i, j; + unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; + XUngrabButton(dpy, AnyButton, AnyModifier, c->win); + if (!focused) + XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, + BUTTONMASK, GrabModeSync, GrabModeSync, None, None); + for (i = 0; i < LENGTH(buttons); i++) + if (buttons[i].click == ClkClientWin) + for (j = 0; j < LENGTH(modifiers); j++) + XGrabButton(dpy, buttons[i].button, + buttons[i].mask | modifiers[j], + c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + } +} + +void +grabkeys(void) +{ + updatenumlockmask(); + { + unsigned int i, j, k; + unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; + int start, end, skip; + KeySym *syms; + + XUngrabKey(dpy, AnyKey, AnyModifier, root); + XDisplayKeycodes(dpy, &start, &end); + syms = XGetKeyboardMapping(dpy, start, end - start + 1, &skip); + if (!syms) + return; + for (k = start; k <= end; k++) + for (i = 0; i < LENGTH(keys); i++) + /* skip modifier codes, we do that ourselves */ + if (keys[i].keysym == syms[(k - start) * skip]) + for (j = 0; j < LENGTH(modifiers); j++) + XGrabKey(dpy, k, + keys[i].mod | modifiers[j], + root, True, + GrabModeAsync, GrabModeAsync); + XFree(syms); + } +} + +void +incnmaster(const Arg *arg) +{ + selmon->nmaster = MAX(selmon->nmaster + arg->i, 0); + arrange(selmon); +} + +#ifdef XINERAMA +static int +isuniquegeom(XineramaScreenInfo *unique, size_t n, XineramaScreenInfo *info) +{ + while (n--) + if (unique[n].x_org == info->x_org && unique[n].y_org == info->y_org + && unique[n].width == info->width && unique[n].height == info->height) + return 0; + return 1; +} +#endif /* XINERAMA */ + +void +keypress(XEvent *e) +{ + unsigned int i; + KeySym keysym; + XKeyEvent *ev; + + ev = &e->xkey; + keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0); + for (i = 0; i < LENGTH(keys); i++) + if (keysym == keys[i].keysym + && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state) + && keys[i].func) + keys[i].func(&(keys[i].arg)); +} + +void +killclient(const Arg *arg) +{ + if (!selmon->sel) + return; + if (!sendevent(selmon->sel, wmatom[WMDelete])) { + XGrabServer(dpy); + XSetErrorHandler(xerrordummy); + XSetCloseDownMode(dpy, DestroyAll); + XKillClient(dpy, selmon->sel->win); + XSync(dpy, False); + XSetErrorHandler(xerror); + XUngrabServer(dpy); + } +} + +void +manage(Window w, XWindowAttributes *wa) +{ + Client *c, *t = NULL; + Window trans = None; + XWindowChanges wc; + + c = ecalloc(1, sizeof(Client)); + c->win = w; + /* geometry */ + c->x = c->oldx = wa->x; + c->y = c->oldy = wa->y; + c->w = c->oldw = wa->width; + c->h = c->oldh = wa->height; + c->oldbw = wa->border_width; + + updatetitle(c); + if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) { + c->mon = t->mon; + c->tags = t->tags; + } else { + c->mon = selmon; + applyrules(c); + } + + if (c->x + WIDTH(c) > c->mon->wx + c->mon->ww) + c->x = c->mon->wx + c->mon->ww - WIDTH(c); + if (c->y + HEIGHT(c) > c->mon->wy + c->mon->wh) + c->y = c->mon->wy + c->mon->wh - HEIGHT(c); + c->x = MAX(c->x, c->mon->wx); + c->y = MAX(c->y, c->mon->wy); + c->bw = borderpx; + + wc.border_width = c->bw; + XConfigureWindow(dpy, w, CWBorderWidth, &wc); + XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel); + configure(c); /* propagates border_width, if size doesn't change */ + updatewindowtype(c); + updatesizehints(c); + updatewmhints(c); + XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask); + grabbuttons(c, 0); + if (!c->isfloating) + c->isfloating = c->oldstate = trans != None || c->isfixed; + if (c->isfloating) + XRaiseWindow(dpy, c->win); + attach(c); + attachstack(c); + XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, + (unsigned char *) &(c->win), 1); + XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */ + setclientstate(c, NormalState); + if (c->mon == selmon) + unfocus(selmon->sel, 0); + c->mon->sel = c; + arrange(c->mon); + XMapWindow(dpy, c->win); + focus(NULL); +} + +void +mappingnotify(XEvent *e) +{ + XMappingEvent *ev = &e->xmapping; + + XRefreshKeyboardMapping(ev); + if (ev->request == MappingKeyboard) + grabkeys(); +} + +void +maprequest(XEvent *e) +{ + static XWindowAttributes wa; + XMapRequestEvent *ev = &e->xmaprequest; + + if (!XGetWindowAttributes(dpy, ev->window, &wa) || wa.override_redirect) + return; + if (!wintoclient(ev->window)) + manage(ev->window, &wa); +} + +void +monocle(Monitor *m) +{ + unsigned int n = 0; + Client *c; + + for (c = m->clients; c; c = c->next) + if (ISVISIBLE(c)) + n++; + if (n > 0) /* override layout symbol */ + snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n); + for (c = nexttiled(m->clients); c; c = nexttiled(c->next)) + resize(c, m->wx, m->wy, m->ww - 2 * c->bw, m->wh - 2 * c->bw, 0); +} + +void +motionnotify(XEvent *e) +{ + static Monitor *mon = NULL; + Monitor *m; + XMotionEvent *ev = &e->xmotion; + + if (ev->window != root) + return; + if ((m = recttomon(ev->x_root, ev->y_root, 1, 1)) != mon && mon) { + unfocus(selmon->sel, 1); + selmon = m; + focus(NULL); + } + mon = m; +} + +void +movemouse(const Arg *arg) +{ + int x, y, ocx, ocy, nx, ny; + Client *c; + Monitor *m; + XEvent ev; + Time lasttime = 0; + + if (!(c = selmon->sel)) + return; + if (c->isfullscreen) /* no support moving fullscreen windows by mouse */ + return; + restack(selmon); + ocx = c->x; + ocy = c->y; + if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, + None, cursor[CurMove]->cursor, CurrentTime) != GrabSuccess) + return; + if (!getrootptr(&x, &y)) + return; + do { + XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); + switch(ev.type) { + case ConfigureRequest: + case Expose: + case MapRequest: + handler[ev.type](&ev); + break; + case MotionNotify: + if ((ev.xmotion.time - lasttime) <= (1000 / 60)) + continue; + lasttime = ev.xmotion.time; + + nx = ocx + (ev.xmotion.x - x); + ny = ocy + (ev.xmotion.y - y); + if (abs(selmon->wx - nx) < snap) + nx = selmon->wx; + else if (abs((selmon->wx + selmon->ww) - (nx + WIDTH(c))) < snap) + nx = selmon->wx + selmon->ww - WIDTH(c); + if (abs(selmon->wy - ny) < snap) + ny = selmon->wy; + else if (abs((selmon->wy + selmon->wh) - (ny + HEIGHT(c))) < snap) + ny = selmon->wy + selmon->wh - HEIGHT(c); + if (!c->isfloating && selmon->lt[selmon->sellt]->arrange + && (abs(nx - c->x) > snap || abs(ny - c->y) > snap)) + togglefloating(NULL); + if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) + resize(c, nx, ny, c->w, c->h, 1); + break; + } + } while (ev.type != ButtonRelease); + XUngrabPointer(dpy, CurrentTime); + if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { + sendmon(c, m); + selmon = m; + focus(NULL); + } +} + +Client * +nexttiled(Client *c) +{ + for (; c && (c->isfloating || !ISVISIBLE(c)); c = c->next); + return c; +} + +void +pop(Client *c) +{ + detach(c); + attach(c); + focus(c); + arrange(c->mon); +} + +void +propertynotify(XEvent *e) +{ + Client *c; + Window trans; + XPropertyEvent *ev = &e->xproperty; + + if ((ev->window == root) && (ev->atom == XA_WM_NAME)) + updatestatus(); + else if (ev->state == PropertyDelete) + return; /* ignore */ + else if ((c = wintoclient(ev->window))) { + switch(ev->atom) { + default: break; + case XA_WM_TRANSIENT_FOR: + if (!c->isfloating && (XGetTransientForHint(dpy, c->win, &trans)) && + (c->isfloating = (wintoclient(trans)) != NULL)) + arrange(c->mon); + break; + case XA_WM_NORMAL_HINTS: + c->hintsvalid = 0; + break; + case XA_WM_HINTS: + updatewmhints(c); + drawbars(); + break; + } + if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { + updatetitle(c); + if (c == c->mon->sel) + drawbar(c->mon); + } + if (ev->atom == netatom[NetWMWindowType]) + updatewindowtype(c); + } +} + +void +quit(const Arg *arg) +{ + running = 0; +} + +Monitor * +recttomon(int x, int y, int w, int h) +{ + Monitor *m, *r = selmon; + int a, area = 0; + + for (m = mons; m; m = m->next) + if ((a = INTERSECT(x, y, w, h, m)) > area) { + area = a; + r = m; + } + return r; +} + +void +resize(Client *c, int x, int y, int w, int h, int interact) +{ + if (applysizehints(c, &x, &y, &w, &h, interact)) + resizeclient(c, x, y, w, h); +} + +void +resizeclient(Client *c, int x, int y, int w, int h) +{ + XWindowChanges wc; + + c->oldx = c->x; c->x = wc.x = x; + c->oldy = c->y; c->y = wc.y = y; + c->oldw = c->w; c->w = wc.width = w; + c->oldh = c->h; c->h = wc.height = h; + wc.border_width = c->bw; + XConfigureWindow(dpy, c->win, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc); + configure(c); + XSync(dpy, False); +} + +void +resizemouse(const Arg *arg) +{ + int ocx, ocy, nw, nh; + Client *c; + Monitor *m; + XEvent ev; + Time lasttime = 0; + + if (!(c = selmon->sel)) + return; + if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */ + return; + restack(selmon); + ocx = c->x; + ocy = c->y; + if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, + None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess) + return; + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); + do { + XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); + switch(ev.type) { + case ConfigureRequest: + case Expose: + case MapRequest: + handler[ev.type](&ev); + break; + case MotionNotify: + if ((ev.xmotion.time - lasttime) <= (1000 / 60)) + continue; + lasttime = ev.xmotion.time; + + nw = MAX(ev.xmotion.x - ocx - 2 * c->bw + 1, 1); + nh = MAX(ev.xmotion.y - ocy - 2 * c->bw + 1, 1); + if (c->mon->wx + nw >= selmon->wx && c->mon->wx + nw <= selmon->wx + selmon->ww + && c->mon->wy + nh >= selmon->wy && c->mon->wy + nh <= selmon->wy + selmon->wh) + { + if (!c->isfloating && selmon->lt[selmon->sellt]->arrange + && (abs(nw - c->w) > snap || abs(nh - c->h) > snap)) + togglefloating(NULL); + } + if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) + resize(c, c->x, c->y, nw, nh, 1); + break; + } + } while (ev.type != ButtonRelease); + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); + XUngrabPointer(dpy, CurrentTime); + while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); + if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { + sendmon(c, m); + selmon = m; + focus(NULL); + } +} + +void +restack(Monitor *m) +{ + Client *c; + XEvent ev; + XWindowChanges wc; + + drawbar(m); + if (!m->sel) + return; + if (m->sel->isfloating || !m->lt[m->sellt]->arrange) + XRaiseWindow(dpy, m->sel->win); + if (m->lt[m->sellt]->arrange) { + wc.stack_mode = Below; + wc.sibling = m->barwin; + for (c = m->stack; c; c = c->snext) + if (!c->isfloating && ISVISIBLE(c)) { + XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc); + wc.sibling = c->win; + } + } + XSync(dpy, False); + while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); +} + +void +run(void) +{ + XEvent ev; + /* main event loop */ + XSync(dpy, False); + while (running && !XNextEvent(dpy, &ev)) + if (handler[ev.type]) + handler[ev.type](&ev); /* call handler */ +} + +void +scan(void) +{ + unsigned int i, num; + Window d1, d2, *wins = NULL; + XWindowAttributes wa; + + if (XQueryTree(dpy, root, &d1, &d2, &wins, &num)) { + for (i = 0; i < num; i++) { + if (!XGetWindowAttributes(dpy, wins[i], &wa) + || wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1)) + continue; + if (wa.map_state == IsViewable || getstate(wins[i]) == IconicState) + manage(wins[i], &wa); + } + for (i = 0; i < num; i++) { /* now the transients */ + if (!XGetWindowAttributes(dpy, wins[i], &wa)) + continue; + if (XGetTransientForHint(dpy, wins[i], &d1) + && (wa.map_state == IsViewable || getstate(wins[i]) == IconicState)) + manage(wins[i], &wa); + } + if (wins) + XFree(wins); + } +} + +void +sendmon(Client *c, Monitor *m) +{ + if (c->mon == m) + return; + unfocus(c, 1); + detach(c); + detachstack(c); + c->mon = m; + c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ + attach(c); + attachstack(c); + focus(NULL); + arrange(NULL); +} + +void +setclientstate(Client *c, long state) +{ + long data[] = { state, None }; + + XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32, + PropModeReplace, (unsigned char *)data, 2); +} + +int +sendevent(Client *c, Atom proto) +{ + int n; + Atom *protocols; + int exists = 0; + XEvent ev; + + if (XGetWMProtocols(dpy, c->win, &protocols, &n)) { + while (!exists && n--) + exists = protocols[n] == proto; + XFree(protocols); + } + if (exists) { + ev.type = ClientMessage; + ev.xclient.window = c->win; + ev.xclient.message_type = wmatom[WMProtocols]; + ev.xclient.format = 32; + ev.xclient.data.l[0] = proto; + ev.xclient.data.l[1] = CurrentTime; + XSendEvent(dpy, c->win, False, NoEventMask, &ev); + } + return exists; +} + +void +setfocus(Client *c) +{ + if (!c->neverfocus) { + XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); + XChangeProperty(dpy, root, netatom[NetActiveWindow], + XA_WINDOW, 32, PropModeReplace, + (unsigned char *) &(c->win), 1); + } + sendevent(c, wmatom[WMTakeFocus]); +} + +void +setfullscreen(Client *c, int fullscreen) +{ + if (fullscreen && !c->isfullscreen) { + XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, + PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1); + c->isfullscreen = 1; + c->oldstate = c->isfloating; + c->oldbw = c->bw; + c->bw = 0; + c->isfloating = 1; + resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); + XRaiseWindow(dpy, c->win); + } else if (!fullscreen && c->isfullscreen){ + XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, + PropModeReplace, (unsigned char*)0, 0); + c->isfullscreen = 0; + c->isfloating = c->oldstate; + c->bw = c->oldbw; + c->x = c->oldx; + c->y = c->oldy; + c->w = c->oldw; + c->h = c->oldh; + resizeclient(c, c->x, c->y, c->w, c->h); + arrange(c->mon); + } +} + +void +setlayout(const Arg *arg) +{ + if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) + selmon->sellt ^= 1; + if (arg && arg->v) + selmon->lt[selmon->sellt] = (Layout *)arg->v; + strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol); + if (selmon->sel) + arrange(selmon); + else + drawbar(selmon); +} + +/* arg > 1.0 will set mfact absolutely */ +void +setmfact(const Arg *arg) +{ + float f; + + if (!arg || !selmon->lt[selmon->sellt]->arrange) + return; + f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0; + if (f < 0.05 || f > 0.95) + return; + selmon->mfact = f; + arrange(selmon); +} + +void +setup(void) +{ + int i; + XSetWindowAttributes wa; + Atom utf8string; + struct sigaction sa; + + /* do not transform children into zombies when they terminate */ + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_NOCLDSTOP | SA_NOCLDWAIT | SA_RESTART; + sa.sa_handler = SIG_IGN; + sigaction(SIGCHLD, &sa, NULL); + + /* clean up any zombies (inherited from .xinitrc etc) immediately */ + while (waitpid(-1, NULL, WNOHANG) > 0); + + /* init screen */ + screen = DefaultScreen(dpy); + sw = DisplayWidth(dpy, screen); + sh = DisplayHeight(dpy, screen); + root = RootWindow(dpy, screen); + drw = drw_create(dpy, screen, root, sw, sh); + if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) + die("no fonts could be loaded."); + lrpad = drw->fonts->h; + bh = drw->fonts->h + 2; + updategeom(); + /* init atoms */ + utf8string = XInternAtom(dpy, "UTF8_STRING", False); + wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); + wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); + wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False); + wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False); + netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); + netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); + netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); + netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False); + netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False); + netatom[NetWMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); + netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); + netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); + netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False); + /* init cursors */ + cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr); + cursor[CurResize] = drw_cur_create(drw, XC_sizing); + cursor[CurMove] = drw_cur_create(drw, XC_fleur); + /* init appearance */ + scheme = ecalloc(LENGTH(colors), sizeof(Clr *)); + for (i = 0; i < LENGTH(colors); i++) + scheme[i] = drw_scm_create(drw, colors[i], 3); + /* init bars */ + updatebars(); + updatestatus(); + /* supporting window for NetWMCheck */ + wmcheckwin = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 0, 0, 0); + XChangeProperty(dpy, wmcheckwin, netatom[NetWMCheck], XA_WINDOW, 32, + PropModeReplace, (unsigned char *) &wmcheckwin, 1); + XChangeProperty(dpy, wmcheckwin, netatom[NetWMName], utf8string, 8, + PropModeReplace, (unsigned char *) "dwm", 3); + XChangeProperty(dpy, root, netatom[NetWMCheck], XA_WINDOW, 32, + PropModeReplace, (unsigned char *) &wmcheckwin, 1); + /* EWMH support per view */ + XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32, + PropModeReplace, (unsigned char *) netatom, NetLast); + XDeleteProperty(dpy, root, netatom[NetClientList]); + /* select events */ + wa.cursor = cursor[CurNormal]->cursor; + wa.event_mask = SubstructureRedirectMask|SubstructureNotifyMask + |ButtonPressMask|PointerMotionMask|EnterWindowMask + |LeaveWindowMask|StructureNotifyMask|PropertyChangeMask; + XChangeWindowAttributes(dpy, root, CWEventMask|CWCursor, &wa); + XSelectInput(dpy, root, wa.event_mask); + grabkeys(); + focus(NULL); +} + +void +seturgent(Client *c, int urg) +{ + XWMHints *wmh; + + c->isurgent = urg; + if (!(wmh = XGetWMHints(dpy, c->win))) + return; + wmh->flags = urg ? (wmh->flags | XUrgencyHint) : (wmh->flags & ~XUrgencyHint); + XSetWMHints(dpy, c->win, wmh); + XFree(wmh); +} + +void +showhide(Client *c) +{ + if (!c) + return; + if (ISVISIBLE(c)) { + /* show clients top down */ + XMoveWindow(dpy, c->win, c->x, c->y); + if ((!c->mon->lt[c->mon->sellt]->arrange || c->isfloating) && !c->isfullscreen) + resize(c, c->x, c->y, c->w, c->h, 0); + showhide(c->snext); + } else { + /* hide clients bottom up */ + showhide(c->snext); + XMoveWindow(dpy, c->win, WIDTH(c) * -2, c->y); + } +} + +void +spawn(const Arg *arg) +{ + struct sigaction sa; + + if (arg->v == dmenucmd) + dmenumon[0] = '0' + selmon->num; + if (fork() == 0) { + if (dpy) + close(ConnectionNumber(dpy)); + setsid(); + + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sa.sa_handler = SIG_DFL; + sigaction(SIGCHLD, &sa, NULL); + + execvp(((char **)arg->v)[0], (char **)arg->v); + die("dwm: execvp '%s' failed:", ((char **)arg->v)[0]); + } +} + +void +tag(const Arg *arg) +{ + if (selmon->sel && arg->ui & TAGMASK) { + selmon->sel->tags = arg->ui & TAGMASK; + focus(NULL); + arrange(selmon); + } +} + +void +tagmon(const Arg *arg) +{ + if (!selmon->sel || !mons->next) + return; + sendmon(selmon->sel, dirtomon(arg->i)); +} + +void +tile(Monitor *m) +{ + unsigned int i, n, h, mw, my, ty; + Client *c; + + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); + if (n == 0) + return; + + if (n > m->nmaster) + mw = m->nmaster ? m->ww * m->mfact : 0; + else + mw = m->ww; + for (i = my = ty = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) + if (i < m->nmaster) { + h = (m->wh - my) / (MIN(n, m->nmaster) - i); + resize(c, m->wx, m->wy + my, mw - (2*c->bw), h - (2*c->bw), 0); + if (my + HEIGHT(c) < m->wh) + my += HEIGHT(c); + } else { + h = (m->wh - ty) / (n - i); + resize(c, m->wx + mw, m->wy + ty, m->ww - mw - (2*c->bw), h - (2*c->bw), 0); + if (ty + HEIGHT(c) < m->wh) + ty += HEIGHT(c); + } +} + +void +togglebar(const Arg *arg) +{ + selmon->showbar = !selmon->showbar; + updatebarpos(selmon); + XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); + arrange(selmon); +} + +void +togglefloating(const Arg *arg) +{ + if (!selmon->sel) + return; + if (selmon->sel->isfullscreen) /* no support for fullscreen windows */ + return; + selmon->sel->isfloating = !selmon->sel->isfloating || selmon->sel->isfixed; + if (selmon->sel->isfloating) + resize(selmon->sel, selmon->sel->x, selmon->sel->y, + selmon->sel->w, selmon->sel->h, 0); + arrange(selmon); +} + +void +toggletag(const Arg *arg) +{ + unsigned int newtags; + + if (!selmon->sel) + return; + newtags = selmon->sel->tags ^ (arg->ui & TAGMASK); + if (newtags) { + selmon->sel->tags = newtags; + focus(NULL); + arrange(selmon); + } +} + +void +toggleview(const Arg *arg) +{ + unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK); + + if (newtagset) { + selmon->tagset[selmon->seltags] = newtagset; + focus(NULL); + arrange(selmon); + } +} + +void +unfocus(Client *c, int setfocus) +{ + if (!c) + return; + grabbuttons(c, 0); + XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel); + if (setfocus) { + XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); + XDeleteProperty(dpy, root, netatom[NetActiveWindow]); + } +} + +void +unmanage(Client *c, int destroyed) +{ + Monitor *m = c->mon; + XWindowChanges wc; + + detach(c); + detachstack(c); + if (!destroyed) { + wc.border_width = c->oldbw; + XGrabServer(dpy); /* avoid race conditions */ + XSetErrorHandler(xerrordummy); + XSelectInput(dpy, c->win, NoEventMask); + XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */ + XUngrabButton(dpy, AnyButton, AnyModifier, c->win); + setclientstate(c, WithdrawnState); + XSync(dpy, False); + XSetErrorHandler(xerror); + XUngrabServer(dpy); + } + free(c); + focus(NULL); + updateclientlist(); + arrange(m); +} + +void +unmapnotify(XEvent *e) +{ + Client *c; + XUnmapEvent *ev = &e->xunmap; + + if ((c = wintoclient(ev->window))) { + if (ev->send_event) + setclientstate(c, WithdrawnState); + else + unmanage(c, 0); + } +} + +void +updatebars(void) +{ + Monitor *m; + XSetWindowAttributes wa = { + .override_redirect = True, + .background_pixmap = ParentRelative, + .event_mask = ButtonPressMask|ExposureMask + }; + XClassHint ch = {"dwm", "dwm"}; + for (m = mons; m; m = m->next) { + if (m->barwin) + continue; + m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen), + CopyFromParent, DefaultVisual(dpy, screen), + CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); + XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); + XMapRaised(dpy, m->barwin); + XSetClassHint(dpy, m->barwin, &ch); + } +} + +void +updatebarpos(Monitor *m) +{ + m->wy = m->my; + m->wh = m->mh; + if (m->showbar) { + m->wh -= bh; + m->by = m->topbar ? m->wy : m->wy + m->wh; + m->wy = m->topbar ? m->wy + bh : m->wy; + } else + m->by = -bh; +} + +void +updateclientlist(void) +{ + Client *c; + Monitor *m; + + XDeleteProperty(dpy, root, netatom[NetClientList]); + for (m = mons; m; m = m->next) + for (c = m->clients; c; c = c->next) + XChangeProperty(dpy, root, netatom[NetClientList], + XA_WINDOW, 32, PropModeAppend, + (unsigned char *) &(c->win), 1); +} + +int +updategeom(void) +{ + int dirty = 0; + +#ifdef XINERAMA + if (XineramaIsActive(dpy)) { + int i, j, n, nn; + Client *c; + Monitor *m; + XineramaScreenInfo *info = XineramaQueryScreens(dpy, &nn); + XineramaScreenInfo *unique = NULL; + + for (n = 0, m = mons; m; m = m->next, n++); + /* only consider unique geometries as separate screens */ + unique = ecalloc(nn, sizeof(XineramaScreenInfo)); + for (i = 0, j = 0; i < nn; i++) + if (isuniquegeom(unique, j, &info[i])) + memcpy(&unique[j++], &info[i], sizeof(XineramaScreenInfo)); + XFree(info); + nn = j; + + /* new monitors if nn > n */ + for (i = n; i < nn; i++) { + for (m = mons; m && m->next; m = m->next); + if (m) + m->next = createmon(); + else + mons = createmon(); + } + for (i = 0, m = mons; i < nn && m; m = m->next, i++) + if (i >= n + || unique[i].x_org != m->mx || unique[i].y_org != m->my + || unique[i].width != m->mw || unique[i].height != m->mh) + { + dirty = 1; + m->num = i; + m->mx = m->wx = unique[i].x_org; + m->my = m->wy = unique[i].y_org; + m->mw = m->ww = unique[i].width; + m->mh = m->wh = unique[i].height; + updatebarpos(m); + } + /* removed monitors if n > nn */ + for (i = nn; i < n; i++) { + for (m = mons; m && m->next; m = m->next); + while ((c = m->clients)) { + dirty = 1; + m->clients = c->next; + detachstack(c); + c->mon = mons; + attach(c); + attachstack(c); + } + if (m == selmon) + selmon = mons; + cleanupmon(m); + } + free(unique); + } else +#endif /* XINERAMA */ + { /* default monitor setup */ + if (!mons) + mons = createmon(); + if (mons->mw != sw || mons->mh != sh) { + dirty = 1; + mons->mw = mons->ww = sw; + mons->mh = mons->wh = sh; + updatebarpos(mons); + } + } + if (dirty) { + selmon = mons; + selmon = wintomon(root); + } + return dirty; +} + +void +updatenumlockmask(void) +{ + unsigned int i, j; + XModifierKeymap *modmap; + + numlockmask = 0; + modmap = XGetModifierMapping(dpy); + for (i = 0; i < 8; i++) + for (j = 0; j < modmap->max_keypermod; j++) + if (modmap->modifiermap[i * modmap->max_keypermod + j] + == XKeysymToKeycode(dpy, XK_Num_Lock)) + numlockmask = (1 << i); + XFreeModifiermap(modmap); +} + +void +updatesizehints(Client *c) +{ + long msize; + XSizeHints size; + + if (!XGetWMNormalHints(dpy, c->win, &size, &msize)) + /* size is uninitialized, ensure that size.flags aren't used */ + size.flags = PSize; + if (size.flags & PBaseSize) { + c->basew = size.base_width; + c->baseh = size.base_height; + } else if (size.flags & PMinSize) { + c->basew = size.min_width; + c->baseh = size.min_height; + } else + c->basew = c->baseh = 0; + if (size.flags & PResizeInc) { + c->incw = size.width_inc; + c->inch = size.height_inc; + } else + c->incw = c->inch = 0; + if (size.flags & PMaxSize) { + c->maxw = size.max_width; + c->maxh = size.max_height; + } else + c->maxw = c->maxh = 0; + if (size.flags & PMinSize) { + c->minw = size.min_width; + c->minh = size.min_height; + } else if (size.flags & PBaseSize) { + c->minw = size.base_width; + c->minh = size.base_height; + } else + c->minw = c->minh = 0; + if (size.flags & PAspect) { + c->mina = (float)size.min_aspect.y / size.min_aspect.x; + c->maxa = (float)size.max_aspect.x / size.max_aspect.y; + } else + c->maxa = c->mina = 0.0; + c->isfixed = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh); + c->hintsvalid = 1; +} + +void +updatestatus(void) +{ + if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) + strcpy(stext, "dwm-"VERSION); + drawbar(selmon); +} + +void +updatetitle(Client *c) +{ + if (!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name)) + gettextprop(c->win, XA_WM_NAME, c->name, sizeof c->name); + if (c->name[0] == '\0') /* hack to mark broken clients */ + strcpy(c->name, broken); +} + +void +updatewindowtype(Client *c) +{ + Atom state = getatomprop(c, netatom[NetWMState]); + Atom wtype = getatomprop(c, netatom[NetWMWindowType]); + + if (state == netatom[NetWMFullscreen]) + setfullscreen(c, 1); + if (wtype == netatom[NetWMWindowTypeDialog]) + c->isfloating = 1; +} + +void +updatewmhints(Client *c) +{ + XWMHints *wmh; + + if ((wmh = XGetWMHints(dpy, c->win))) { + if (c == selmon->sel && wmh->flags & XUrgencyHint) { + wmh->flags &= ~XUrgencyHint; + XSetWMHints(dpy, c->win, wmh); + } else + c->isurgent = (wmh->flags & XUrgencyHint) ? 1 : 0; + if (wmh->flags & InputHint) + c->neverfocus = !wmh->input; + else + c->neverfocus = 0; + XFree(wmh); + } +} + +void +view(const Arg *arg) +{ + if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) + return; + selmon->seltags ^= 1; /* toggle sel tagset */ + if (arg->ui & TAGMASK) + selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; + focus(NULL); + arrange(selmon); +} + +Client * +wintoclient(Window w) +{ + Client *c; + Monitor *m; + + for (m = mons; m; m = m->next) + for (c = m->clients; c; c = c->next) + if (c->win == w) + return c; + return NULL; +} + +Monitor * +wintomon(Window w) +{ + int x, y; + Client *c; + Monitor *m; + + if (w == root && getrootptr(&x, &y)) + return recttomon(x, y, 1, 1); + for (m = mons; m; m = m->next) + if (w == m->barwin) + return m; + if ((c = wintoclient(w))) + return c->mon; + return selmon; +} + +/* There's no way to check accesses to destroyed windows, thus those cases are + * ignored (especially on UnmapNotify's). Other types of errors call Xlibs + * default error handler, which may call exit. */ +int +xerror(Display *dpy, XErrorEvent *ee) +{ + if (ee->error_code == BadWindow + || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch) + || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable) + || (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable) + || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable) + || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch) + || (ee->request_code == X_GrabButton && ee->error_code == BadAccess) + || (ee->request_code == X_GrabKey && ee->error_code == BadAccess) + || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable)) + return 0; + fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n", + ee->request_code, ee->error_code); + return xerrorxlib(dpy, ee); /* may call exit */ +} + +int +xerrordummy(Display *dpy, XErrorEvent *ee) +{ + return 0; +} + +/* Startup Error handler to check if another window manager + * is already running. */ +int +xerrorstart(Display *dpy, XErrorEvent *ee) +{ + die("dwm: another window manager is already running"); + return -1; +} + +void +zoom(const Arg *arg) +{ + Client *c = selmon->sel; + + if (!selmon->lt[selmon->sellt]->arrange || !c || c->isfloating) + return; + if (c == nexttiled(selmon->clients) && !(c = nexttiled(c->next))) + return; + pop(c); +} + +int +main(int argc, char *argv[]) +{ + if (argc == 2 && !strcmp("-v", argv[1])) + die("dwm-"VERSION); + else if (argc != 1) + die("usage: dwm [-v]"); + if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) + fputs("warning: no locale support\n", stderr); + if (!(dpy = XOpenDisplay(NULL))) + die("dwm: cannot open display"); + checkotherwm(); + setup(); +#ifdef __OpenBSD__ + if (pledge("stdio rpath proc exec", NULL) == -1) + die("pledge"); +#endif /* __OpenBSD__ */ + scan(); + run(); + cleanup(); + XCloseDisplay(dpy); + return EXIT_SUCCESS; +} diff --git a/dwm/dwm.o b/dwm/dwm.o new file mode 100644 index 0000000000000000000000000000000000000000..3038054e45c9b27324d95bd7cb7885f44809dc1b GIT binary patch literal 57760 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 literal 0 HcmV?d00001 diff --git a/dwm/dwm.png b/dwm/dwm.png new file mode 100644 index 0000000000000000000000000000000000000000..b1f9ba7e5f4cc7350ee2392ebcea5fcbe00fb49b GIT binary patch literal 373 zcmeAS@N?(olHy`uVBq!ia0vp^2Y@($g9*gC@m3f}u_bxCyDx`7I;J! zGca%iWx0hJ8D`Cq01C2~c>21sUt<^MF=V?Ztt9{yk}YwKC~?lu%}vcKVQ?-=O)N=G zQ7F$W$xsN%NL6t6^bL5QqM8R(c+=CxF{I+w+q;fj4F)_6j>`Z3pZ>_($QEQ&92OXP z%lpEKGwG8$G-U1H{@Y%;mx-mNK|p|siBVAj$Z~Mt-~h6K0!}~{PyozQ07(f5fTdVi zm=-zT`NweeJ#%S&{fequZGmkDDC*%x$$Sa*fAP=$`nJkhx1Y~k<8b2;Hq)FOdV=P$ q&oWzoxz_&nv&n0)xBzV8k*jsxheTIy&cCY600f?{elF{r5}E*x)opSB literal 0 HcmV?d00001 diff --git a/dwm/startdwm.sh b/dwm/startdwm.sh new file mode 100755 index 0000000..34e486c --- /dev/null +++ b/dwm/startdwm.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +setxkbmap "us,ru" -option 'grp:win_space_toggle' +feh --bg-fill ~/Pictures/hyprpaper/images/beautiful-nature-landscape-with-mountains-lake.png +picom -b + +while true; do + dwm 2> ~/.dwm.log +done + diff --git a/dwm/transient.c b/dwm/transient.c new file mode 100644 index 0000000..040adb5 --- /dev/null +++ b/dwm/transient.c @@ -0,0 +1,42 @@ +/* cc transient.c -o transient -lX11 */ + +#include +#include +#include +#include + +int main(void) { + Display *d; + Window r, f, t = None; + XSizeHints h; + XEvent e; + + d = XOpenDisplay(NULL); + if (!d) + exit(1); + r = DefaultRootWindow(d); + + f = XCreateSimpleWindow(d, r, 100, 100, 400, 400, 0, 0, 0); + h.min_width = h.max_width = h.min_height = h.max_height = 400; + h.flags = PMinSize | PMaxSize; + XSetWMNormalHints(d, f, &h); + XStoreName(d, f, "floating"); + XMapWindow(d, f); + + XSelectInput(d, f, ExposureMask); + while (1) { + XNextEvent(d, &e); + + if (t == None) { + sleep(5); + t = XCreateSimpleWindow(d, r, 50, 50, 100, 100, 0, 0, 0); + XSetTransientForHint(d, t, f); + XStoreName(d, t, "transient"); + XMapWindow(d, t); + XSelectInput(d, t, ExposureMask); + } + } + + XCloseDisplay(d); + exit(0); +} diff --git a/dwm/util.c b/dwm/util.c new file mode 100644 index 0000000..96b82c9 --- /dev/null +++ b/dwm/util.c @@ -0,0 +1,36 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include +#include +#include + +#include "util.h" + +void +die(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + if (fmt[0] && fmt[strlen(fmt)-1] == ':') { + fputc(' ', stderr); + perror(NULL); + } else { + fputc('\n', stderr); + } + + exit(1); +} + +void * +ecalloc(size_t nmemb, size_t size) +{ + void *p; + + if (!(p = calloc(nmemb, size))) + die("calloc:"); + return p; +} diff --git a/dwm/util.h b/dwm/util.h new file mode 100644 index 0000000..f633b51 --- /dev/null +++ b/dwm/util.h @@ -0,0 +1,8 @@ +/* See LICENSE file for copyright and license details. */ + +#define MAX(A, B) ((A) > (B) ? (A) : (B)) +#define MIN(A, B) ((A) < (B) ? (A) : (B)) +#define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B)) + +void die(const char *fmt, ...); +void *ecalloc(size_t nmemb, size_t size); diff --git a/dwm/util.o b/dwm/util.o new file mode 100644 index 0000000000000000000000000000000000000000..aa3779b8a8359ef45e7e6119d6f763f0e4c68e40 GIT binary patch literal 2224 zcmbtUO=uHQ5T0$TjWtbT?V(Z+E3A@0U6Rm3NmZhac9kHtR@7cJO*S@|KR26_s$j%= z2qDmeM-Lu6D0nF7QE1SESC5|5qfig^ASe~$% zBmox*PQk9nQGoECT{~`>aqz()T-F-f+QaW(5z-o_GO0D@mAKYODq6CUP-c^8U0ZFN zF@Gqb-1LW%O4%QJqSTS@4?R`ZEd5-0X6aYTx~1PJZA-sZKBCTO&Ay{SG&C$2jI_1p zNp#TrthH8^EfyHjTJuUkYbBKMhlCRFAdGrA%);_FG@w2OT62Odb4+V|p@uzHKx?8^ zzu()5RS#~+7`MitB zcqJr9#?^=#k)z@0cz7Zj#TtFMWL$KtNP!-Z+Jy15mzci~F_K|hfpm8cq*_24_4a$# zkUWC;VHW#M`m`_bsON%ji^V2c>=27x^?4ZLu4w;0_|b8T_U?4I8?~zmmxF}{Q4a}x zt2e|B;0iE~sOUS~@FQLDU>98Ng45L9>f@ZOK6!uEIUzRHg%4FTmsiuECa1%Y$>~rr zGrD2Ga<**bie?slBd-@BTdtaEC}XH(fL_a)pxYM%>ylJ$g>$#>z@LsSFBwjEVJ|L@ zzh=0T|4!id3;a(4PBY?lz6dzoEsk#rxXAx0;PeVOe_O!40^Uy-2!;CW#l`tvhSPV^ z;<$`93Gr!hIl&B$^Z9^USt*#QMbxHY>m_a(dOoF^dd&nilQL7FE>#i@EGD$^I!8L zm`gS3A1DgnpZCjSe1ERv7$BQ|?4m3%0{<8C7|-MSJO;SUIi#1}Ko{3#Ej`oEI6?M?VEw*Ms4^roq# jGoNZYXO9qs8F+>qL0GvRg#Q0-`RlCzdpk_qaOVF7i)##a literal 0 HcmV?d00001