From 535cd754f19f11ecbd6c108dcfc38e99281a9949 Mon Sep 17 00:00:00 2001 From: Tucker Johnson Date: Tue, 4 Feb 2025 08:08:23 -0500 Subject: [PATCH] init --- LICENSE | 38 + Makefile | 52 + PKGBUILD | 44 + README.md | 13 + TuCLS.7 | 77 ++ config.h | 338 +++++++ config.mk | 39 + drw.c | 453 +++++++++ drw.h | 58 ++ drw.o | Bin 0 -> 11120 bytes dwm | Bin 0 -> 105200 bytes dwm.1 | 176 ++++ dwm.c | 2732 ++++++++++++++++++++++++++++++++++++++++++++++++++ dwm.o | Bin 0 -> 101072 bytes shiftview.c | 65 ++ transient.c | 42 + util.c | 36 + util.h | 8 + util.o | Bin 0 -> 2224 bytes vanitygaps.c | 550 ++++++++++ 20 files changed, 4721 insertions(+) create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 PKGBUILD create mode 100644 README.md create mode 100644 TuCLS.7 create mode 100644 config.h create mode 100644 config.mk create mode 100644 drw.c create mode 100644 drw.h create mode 100644 drw.o create mode 100755 dwm create mode 100644 dwm.1 create mode 100644 dwm.c create mode 100644 dwm.o create mode 100644 shiftview.c create mode 100644 transient.c create mode 100644 util.c create mode 100644 util.h create mode 100644 util.o create mode 100644 vanitygaps.c diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..1e1b5a4 --- /dev/null +++ b/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 +© 2019-2020 Luke Smith + +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/Makefile b/Makefile new file mode 100644 index 0000000..d2abfd1 --- /dev/null +++ b/Makefile @@ -0,0 +1,52 @@ +# 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: options dwm + +options: + @echo dwm build options: + @echo "CFLAGS = ${CFLAGS}" + @echo "LDFLAGS = ${LDFLAGS}" + @echo "CC = ${CC}" + +.c.o: + ${CC} -c ${CFLAGS} $< + +${OBJ}: config.h config.mk + +dwm: ${OBJ} + ${CC} -o $@ ${OBJ} ${LDFLAGS} + +clean: + rm -f dwm ${OBJ} dwm-${VERSION}.tar.gz *.orig *.rej + +dist: clean + mkdir -p dwm-${VERSION} + cp -R LICENSE Makefile README config.mk\ + dwm.1 drw.h util.h ${SRC} 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 + mkdir -p ${DESTDIR}${MANPREFIX}/man7 + cp -f TuCLS.7 ${DESTDIR}${MANPREFIX}/man7 + chmod 644 ${DESTDIR}${MANPREFIX}/man7/TuCLS.7 + +uninstall: + rm -f ${DESTDIR}${PREFIX}/bin/dwm\ + ${DESTDIR}${MANPREFIX}/man7/TuCLS.7\ + ${DESTDIR}${MANPREFIX}/man1/dwm.1 + +.PHONY: all options clean dist install uninstall diff --git a/PKGBUILD b/PKGBUILD new file mode 100644 index 0000000..1903c72 --- /dev/null +++ b/PKGBUILD @@ -0,0 +1,44 @@ +_pkgname=dwm +pkgname=$_pkgname-larbs-git +pkgver=6.2.r1888.0ac09e0 +pkgrel=1 +pkgdesc="Luke's build of dwm" +url=https://github.com/LukeSmithxyz/dwm +arch=(i686 x86_64) +license=(MIT) +makedepends=(git) +depends=(freetype2 libx11 libxft) +optdepends=( + 'dmenu: program launcher' + 'st: terminal emulator') +provides=($_pkgname) +conflicts=($_pkgname) +source=(git+https://github.com/LukeSmithxyz/dwm) +sha256sums=('SKIP') + +pkgver() { + cd "$_pkgname" + echo "$(awk '/^VERSION =/ {print $3}' config.mk)".r"$(git rev-list --count HEAD)"."$(git rev-parse --short HEAD)" +} + +prepare() { + cd "$_pkgname" + echo "CPPFLAGS+=${CPPFLAGS}" >> config.mk + echo "CFLAGS+=${CFLAGS}" >> config.mk + echo "LDFLAGS+=${LDFLAGS}" >> config.mk + # to use a custom config.h, place it in the package directory + if [[ -f ${SRCDEST}/config.h ]]; then + cp "${SRCDEST}/config.h" . + fi +} + +build() { + cd "$_pkgname" + make X11INC=/usr/include/X11 X11LIB=/usr/lib/X11 +} + +package() { + cd "$_pkgname" + make PREFIX=/usr DESTDIR="$pkgdir" install + install -Dm644 LICENSE "$pkgdir/usr/share/licenses/$pkgname/LICENSE" +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..cf67303 --- /dev/null +++ b/README.md @@ -0,0 +1,13 @@ +### included patches + +- [Clickable statusbar](https://dwm.suckless.org/patches/statuscmd/) +- Reads [xresources](https://dwm.suckless.org/patches/xresources/) colors/variables (i.e. works with `pywal`, etc.). +- scratchpad: Accessible with mod+shift+enter. +- New layouts: bstack, fibonacci, deck, centered master and more. All bound to keys super+(shift+)t/y/u/i. +- True fullscreen (super+f) and prevents focus shifting. +- Windows can be made sticky (super+s). +- [hide vacant tags](https://dwm.suckless.org/patches/hide_vacant_tags/) hides tags with no windows. +- [stacker](https://dwm.suckless.org/patches/stacker/): Move windows up the stack manually (super-K/J). +- [shiftview](https://dwm.suckless.org/patches/nextprev/): Cycle through tags (super+g/;). +- [vanitygaps](https://dwm.suckless.org/patches/vanitygaps/): Gaps allowed across all layouts. +- [swallow patch](https://dwm.suckless.org/patches/swallow/): if a program run from a terminal would make it inoperable, it temporarily takes its place to save space. diff --git a/TuCLS.7 b/TuCLS.7 new file mode 100644 index 0000000..8530de3 --- /dev/null +++ b/TuCLS.7 @@ -0,0 +1,77 @@ +.TH "TuCLS" 7 +.SH Name +TuCLS \- Tucker's Configured Linux System +.IP +\(bu view this document with +.B "MOD + ?" +\~\~ +.RB "("MOD +is the Super Key or Windows Key) +.SH Prolepsis +.P +.B TuCLS +is my personal, configured linux system built on +.I Artix Linux. +It is built around a set of light-weight, modifiable, and replaceable programs that are easy to customize. +At the center of this web of programs are the suckless programs +.MR dwm 1 +(window manager), +.MR dwmblocks 1 +(status bar), +.MR st 1 +(terminal emulator), and +.MR dmenu 1 +(menu), which are incredibly customizable. +Most other programs are terminal or ncurses programs that are significantly extensible. +.MR Nvim 1 +is included with a custom configuration including many plugins, lua scripts, and key-bound shell scripts. +A more complete list of programs is listed at the bottom of this document. +.P +I am a musician, composer, music technologist, and educator. +Thus, the priorities and configurations of the system are built around my work. +The big tasks here are music engraving \(en handled with +.MR Lilypond 1 , +audio work \(en involving +.IR "SuperCollider", +.MR pd 1 , +.MR SoX 1 , +and more, in addition to compositionally related computation using any number of tools. +.SH Key Mappings and Bindings +Any connected keyboard is remapped using the script +.I remaps +in +.BR "~/.local/bin/". +Two keys are remapped in the process: +the key between Alt and Ctrl (either windows or command) becomes a Mod key +and the Capslock key becomes escape when tapped and another Mod key when held. +Capslock becomes disabled. +.SS dwm Bindings +.P +Windows in dwm are ordered in a 'stack' that can be cycled through and rearranged. +They are also placed in different tags which can be navigated like alternate desktops. +(N.B. the key bindings here overwrite those in the +.MR dwm 1 +man page) +.TP +.B Navigation +.TS +tab(|) nospaces; +l l. +Mod + j/k|move down/up through windows in the stack +Mod + Space|move selected window to top (or switch second window to top) +Mod + h/l|diminish/augment width of main window +Mod + z/x|diminish/augment gaps between windows +Mod + a|toggle gaps on/off +Mod + A|reset gaps to default scale +Mod + Shift + Space|make a window float +Mod + s|toggle a 'sticky' window that follows you through tags +Mod + b|toggle status bar +Mod + v|jump to top window +.TE +.SM N.B. move/resize windows with 'Mod + left/right click' +. +.TP +.B Layouts +. +.SH Author +Tucker Johnson (tucker@newer.systems, https://newer.systems) diff --git a/config.h b/config.h new file mode 100644 index 0000000..e2decb4 --- /dev/null +++ b/config.h @@ -0,0 +1,338 @@ +/* See LICENSE file for copyright and license details. */ + +/* Constants */ +#define TERMINAL "st" +#define TERMCLASS "St" +#define BROWSER "librewolf" + +/* appearance */ +static unsigned int borderpx = 3; /* border pixel of windows */ +static unsigned int snap = 32; /* snap pixel */ +static unsigned int gappih = 20; /* horiz inner gap between windows */ +static unsigned int gappiv = 10; /* vert inner gap between windows */ +static unsigned int gappoh = 10; /* horiz outer gap between windows and screen edge */ +static unsigned int gappov = 30; /* vert outer gap between windows and screen edge */ +static int swallowfloating = 0; /* 1 means swallow floating windows by default */ +static int smartgaps = 0; /* 1 means no outer gap when there is only one window */ +static int showbar = 1; /* 0 means no bar */ +static int topbar = 1; /* 0 means bottom bar */ +static char *fonts[] = { "monospace:size=10", "NotoColorEmoji:pixelsize=10:antialias=true:autohint=true" }; +static char normbgcolor[] = "#222222"; +static char normbordercolor[] = "#444444"; +static char normfgcolor[] = "#bbbbbb"; +static char selfgcolor[] = "#eeeeee"; +static char selbordercolor[] = "#888888"; +static char selbgcolor[] = "#005577"; +static char *colors[][3] = { + /* fg bg border */ + [SchemeNorm] = { normfgcolor, normbgcolor, normbordercolor }, + [SchemeSel] = { selfgcolor, selbgcolor, selbordercolor }, +}; + +typedef struct { + const char *name; + const void *cmd; +} Sp; +const char *spcmd1[] = {TERMINAL, "-n", "spterm", "-g", "120x34", NULL }; +const char *spcmd2[] = {TERMINAL, "-n", "spcalc", "-f", "monospace:size=16", "-g", "50x20", "-e", "bc", "-lq", NULL }; +static Sp scratchpads[] = { + /* name cmd */ + {"spterm", spcmd1}, + {"spcalc", spcmd2}, +}; + +/* 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 isterminal noswallow monitor */ + { "Gimp", NULL, NULL, 1 << 8, 0, 0, 0, -1 }, + { TERMCLASS, NULL, NULL, 0, 0, 1, 0, -1 }, + { NULL, NULL, "Event Tester", 0, 0, 0, 1, -1 }, + { TERMCLASS, "floatterm", NULL, 0, 1, 1, 0, -1 }, + { TERMCLASS, "bg", NULL, 1 << 7, 0, 1, 0, -1 }, + { TERMCLASS, "spterm", NULL, SPTAG(0), 1, 1, 0, -1 }, + { TERMCLASS, "spcalc", NULL, SPTAG(1), 1, 1, 0, -1 }, +}; + +/* layout(s) */ +static float mfact = 0.55; /* factor of master area size [0.05..0.95] */ +static int nmaster = 1; /* number of clients in master area */ +static int resizehints = 0; /* 1 means respect size hints in tiled resizals */ +static const int lockfullscreen = 1; /* 1 will force focus on the fullscreen window */ +#define FORCE_VSPLIT 1 /* nrowgrid layout: force two clients to always split vertically */ +#include "vanitygaps.c" +static const Layout layouts[] = { + /* symbol arrange function */ + { "tl", tile }, /* Default: Master on left, slaves on right */ + { "bst", bstack }, /* Master on top, slaves on bottom */ + + { "spr", spiral }, /* Fibonacci spiral */ + { "dwdl", dwindle }, /* Decreasing in size right and leftward */ + + { "dck", deck }, /* Master on left, slaves in monocle-like mode on right */ + { "mcl", monocle }, /* All windows on top of eachother */ + + { "cen", centeredmaster }, /* Master in middle, slaves on sides */ + { "cfm", centeredfloatingmaster }, /* Same but master floats */ + + { "flt", NULL }, /* no layout function means floating behavior */ + { NULL, NULL }, +}; + +/* key definitions */ +#define MODKEY Mod4Mask +#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} }, +#define STACKKEYS(MOD,ACTION) \ + { MOD, XK_j, ACTION##stack, {.i = INC(+1) } }, \ + { MOD, XK_k, ACTION##stack, {.i = INC(-1) } }, \ + { MOD, XK_v, ACTION##stack, {.i = 0 } }, \ + /* { MOD, XK_grave, ACTION##stack, {.i = PREVSEL } }, \ */ + /* { MOD, XK_a, ACTION##stack, {.i = 1 } }, \ */ + /* { MOD, XK_z, ACTION##stack, {.i = 2 } }, \ */ + /* { MOD, XK_x, ACTION##stack, {.i = -1 } }, */ + +/* 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 const char *termcmd[] = { TERMINAL, NULL }; + +/* + * Xresources preferences to load at startup + */ +ResourcePref resources[] = { + { "color0", STRING, &normbordercolor }, + { "color8", STRING, &selbordercolor }, + { "color0", STRING, &normbgcolor }, + { "color4", STRING, &normfgcolor }, + { "color0", STRING, &selfgcolor }, + { "color4", STRING, &selbgcolor }, + { "borderpx", INTEGER, &borderpx }, + { "snap", INTEGER, &snap }, + { "showbar", INTEGER, &showbar }, + { "topbar", INTEGER, &topbar }, + { "nmaster", INTEGER, &nmaster }, + { "resizehints", INTEGER, &resizehints }, + { "mfact", FLOAT, &mfact }, + { "gappih", INTEGER, &gappih }, + { "gappiv", INTEGER, &gappiv }, + { "gappoh", INTEGER, &gappoh }, + { "gappov", INTEGER, &gappov }, + { "swallowfloating", INTEGER, &swallowfloating }, + { "smartgaps", INTEGER, &smartgaps }, +}; + +#include +#include "shiftview.c" + +static const Key keys[] = { + /* modifier key function argument */ + STACKKEYS(MODKEY, focus) + STACKKEYS(MODKEY|ShiftMask, push) + /* { MODKEY|ShiftMask, XK_Escape, spawn, SHCMD("") }, */ + { MODKEY, XK_grave, spawn, {.v = (const char*[]){ "dmenuunicode", NULL } } }, + /* { MODKEY|ShiftMask, XK_grave, togglescratch, SHCMD("") }, */ + 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, XK_0, view, {.ui = ~0 } }, + { MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } }, + { MODKEY, XK_minus, spawn, SHCMD("wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-; kill -44 $(pidof dwmblocks)") }, + { MODKEY|ShiftMask, XK_minus, spawn, SHCMD("wpctl set-volume @DEFAULT_AUDIO_SINK@ 15%-; kill -44 $(pidof dwmblocks)") }, + { MODKEY, XK_equal, spawn, SHCMD("wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+; kill -44 $(pidof dwmblocks)") }, + { MODKEY|ShiftMask, XK_equal, spawn, SHCMD("wpctl set-volume @DEFAULT_AUDIO_SINK@ 15%+; kill -44 $(pidof dwmblocks)") }, + { MODKEY, XK_BackSpace, spawn, {.v = (const char*[]){ "sysact", NULL } } }, + { MODKEY|ShiftMask, XK_BackSpace, spawn, {.v = (const char*[]){ "sysact", NULL } } }, + + { MODKEY, XK_Tab, view, {0} }, + /* { MODKEY|ShiftMask, XK_Tab, spawn, SHCMD("") }, */ + { MODKEY, XK_q, killclient, {0} }, + { MODKEY|ShiftMask, XK_q, spawn, {.v = (const char*[]){ "sysact", NULL } } }, + { MODKEY, XK_w, spawn, {.v = (const char*[]){ BROWSER, NULL } } }, + { MODKEY|ShiftMask, XK_w, spawn, {.v = (const char*[]){ TERMINAL, "-e", "sudo", "nmtui", NULL } } }, + { MODKEY, XK_e, spawn, SHCMD(TERMINAL " -e neomutt ; pkill -RTMIN+12 dwmblocks; rmdir ~/.abook 2>/dev/null") }, + { MODKEY|ShiftMask, XK_e, spawn, SHCMD(TERMINAL " -e abook -C ~/.config/abook/abookrc --datafile ~/.config/abook/addressbook") }, + { MODKEY, XK_r, spawn, {.v = (const char*[]){ TERMINAL, "-e", "lfub", NULL } } }, + { MODKEY|ShiftMask, XK_r, spawn, {.v = (const char*[]){ TERMINAL, "-e", "htop", NULL } } }, + { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, /* tile */ + { MODKEY|ShiftMask, XK_t, setlayout, {.v = &layouts[1]} }, /* bstack */ + { MODKEY, XK_y, setlayout, {.v = &layouts[2]} }, /* spiral */ + { MODKEY|ShiftMask, XK_y, setlayout, {.v = &layouts[3]} }, /* dwindle */ + { MODKEY, XK_u, setlayout, {.v = &layouts[4]} }, /* deck */ + { MODKEY|ShiftMask, XK_u, setlayout, {.v = &layouts[5]} }, /* monocle */ + { MODKEY, XK_i, setlayout, {.v = &layouts[6]} }, /* centeredmaster */ + { MODKEY|ShiftMask, XK_i, setlayout, {.v = &layouts[7]} }, /* centeredfloatingmaster */ + { MODKEY, XK_o, incnmaster, {.i = +1 } }, + { MODKEY|ShiftMask, XK_o, incnmaster, {.i = -1 } }, + { MODKEY, XK_p, spawn, {.v = (const char*[]){ "mpc", "toggle", NULL } } }, + { MODKEY|ShiftMask, XK_p, spawn, SHCMD("mpc pause; pauseallmpv") }, + { MODKEY, XK_bracketleft, spawn, {.v = (const char*[]){ "mpc", "seek", "-10", NULL } } }, + { MODKEY|ShiftMask, XK_bracketleft, spawn, {.v = (const char*[]){ "mpc", "seek", "-60", NULL } } }, + { MODKEY, XK_bracketright, spawn, {.v = (const char*[]){ "mpc", "seek", "+10", NULL } } }, + { MODKEY|ShiftMask, XK_bracketright, spawn, {.v = (const char*[]){ "mpc", "seek", "+60", NULL } } }, + { MODKEY, XK_backslash, view, {0} }, + /* { MODKEY|ShiftMask, XK_backslash, spawn, SHCMD("") }, */ + + { MODKEY, XK_a, togglegaps, {0} }, + { MODKEY|ShiftMask, XK_a, defaultgaps, {0} }, + { MODKEY, XK_s, togglesticky, {0} }, + /* { MODKEY|ShiftMask, XK_s, spawn, SHCMD("") }, */ + { MODKEY, XK_d, spawn, {.v = (const char*[]){ "dmenu_run", NULL } } }, + { MODKEY|ShiftMask, XK_d, spawn, {.v = (const char*[]){ "passmenu", NULL } } }, + { MODKEY, XK_f, togglefullscr, {0} }, + { MODKEY|ShiftMask, XK_f, setlayout, {.v = &layouts[8]} }, + { MODKEY, XK_g, shiftview, { .i = -1 } }, + { MODKEY|ShiftMask, XK_g, shifttag, { .i = -1 } }, + { MODKEY, XK_h, setmfact, {.f = -0.05} }, + /* J and K are automatically bound above in STACKEYS */ + { MODKEY, XK_l, setmfact, {.f = +0.05} }, + { MODKEY, XK_semicolon, shiftview, { .i = 1 } }, + { MODKEY|ShiftMask, XK_semicolon, shifttag, { .i = 1 } }, + { MODKEY, XK_apostrophe, togglescratch, {.ui = 1} }, + /* { MODKEY|ShiftMask, XK_apostrophe, spawn, SHCMD("") }, */ + { MODKEY|ShiftMask, XK_apostrophe, togglesmartgaps, {0} }, + { MODKEY, XK_Return, spawn, {.v = termcmd } }, + { MODKEY|ShiftMask, XK_Return, togglescratch, {.ui = 0} }, + + { MODKEY, XK_z, incrgaps, {.i = +3 } }, + /* { MODKEY|ShiftMask, XK_z, spawn, SHCMD("") }, */ + { MODKEY, XK_x, incrgaps, {.i = -3 } }, + /* { MODKEY|ShiftMask, XK_x, spawn, SHCMD("") }, */ + { MODKEY, XK_c, spawn, {.v = (const char*[]){ TERMINAL, "-e", "profanity", NULL } } }, + /* { MODKEY|ShiftMask, XK_c, spawn, SHCMD("") }, */ + /* V is automatically bound above in STACKKEYS */ + { MODKEY, XK_b, togglebar, {0} }, + /* { MODKEY|ShiftMask, XK_b, spawn, SHCMD("") }, */ + { MODKEY, XK_n, spawn, {.v = (const char*[]){ TERMINAL, "-e", "nvim", "-c", "VimwikiIndex", NULL } } }, + { MODKEY|ShiftMask, XK_n, spawn, SHCMD(TERMINAL " -e newsboat ; pkill -RTMIN+6 dwmblocks") }, + { MODKEY, XK_m, spawn, {.v = (const char*[]){ TERMINAL, "-e", "ncmpcpp", NULL } } }, + { MODKEY|ShiftMask, XK_m, spawn, SHCMD("wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle; kill -44 $(pidof dwmblocks)") }, + { MODKEY, XK_comma, spawn, {.v = (const char*[]){ "mpc", "prev", NULL } } }, + { MODKEY|ShiftMask, XK_comma, spawn, {.v = (const char*[]){ "mpc", "seek", "0%", NULL } } }, + { MODKEY, XK_period, spawn, {.v = (const char*[]){ "mpc", "next", NULL } } }, + { MODKEY|ShiftMask, XK_period, spawn, {.v = (const char*[]){ "mpc", "repeat", NULL } } }, + { MODKEY|ShiftMask, XK_slash, spawn, SHCMD(TERMINAL " -e man TuCLS") }, + + { MODKEY, XK_Left, focusmon, {.i = -1 } }, + { MODKEY|ShiftMask, XK_Left, tagmon, {.i = -1 } }, + { MODKEY, XK_Right, focusmon, {.i = +1 } }, + { MODKEY|ShiftMask, XK_Right, tagmon, {.i = +1 } }, + + { MODKEY, XK_Page_Up, shiftview, { .i = -1 } }, + { MODKEY|ShiftMask, XK_Page_Up, shifttag, { .i = -1 } }, + { MODKEY, XK_Page_Down, shiftview, { .i = +1 } }, + { MODKEY|ShiftMask, XK_Page_Down, shifttag, { .i = +1 } }, + { MODKEY, XK_Insert, spawn, SHCMD("xdotool type $(grep -v '^#' ~/.local/share/larbs/snippets | dmenu -i -l 50 | cut -d' ' -f1)") }, + + { MODKEY, XK_F1, spawn, SHCMD(TERMINAL " -e pulsemixer; kill -44 $(pidof dwmblocks)") }, + { MODKEY, XK_F2, spawn, {.v = (const char*[]){ "maimpick", NULL } } }, + { MODKEY, XK_F3, spawn, {.v = (const char*[]){ "displayselect", NULL } } }, + { MODKEY, XK_F4, spawn, SHCMD(TERMINAL " -e pulsemixer; kill -44 $(pidof dwmblocks)") }, + { MODKEY, XK_F5, xrdb, {.v = NULL } }, + { MODKEY, XK_F6, spawn, {.v = (const char*[]){ "torwrap", NULL } } }, + { MODKEY, XK_F7, spawn, {.v = (const char*[]){ "td-toggle", NULL } } }, + { MODKEY, XK_F8, spawn, {.v = (const char*[]){ "mailsync", NULL } } }, + { MODKEY, XK_F9, spawn, {.v = (const char*[]){ "mounter", NULL } } }, + { MODKEY, XK_F10, spawn, {.v = (const char*[]){ "unmounter", NULL } } }, + { MODKEY, XK_F11, spawn, SHCMD("mpv --untimed --no-cache --no-osc --no-input-default-bindings --profile=low-latency --input-conf=/dev/null --title=webcam $(ls /dev/video[0,2,4,6,8] | tail -n 1)") }, + { MODKEY, XK_F12, spawn, SHCMD("remaps") }, + { MODKEY, XK_space, zoom, {0} }, + { MODKEY|ShiftMask, XK_space, togglefloating, {0} }, + + { 0, XK_Print, spawn, SHCMD("maim pic-full-$(date '+%y%m%d-%H%M-%S').png") }, + { ShiftMask, XK_Print, spawn, {.v = (const char*[]){ "maimpick", NULL } } }, + { MODKEY, XK_Print, spawn, {.v = (const char*[]){ "dmenurecord", NULL } } }, + { MODKEY|ShiftMask, XK_Print, spawn, {.v = (const char*[]){ "dmenurecord", "kill", NULL } } }, + { MODKEY, XK_Delete, spawn, {.v = (const char*[]){ "dmenurecord", "kill", NULL } } }, + { MODKEY, XK_Scroll_Lock, spawn, SHCMD("killall screenkey || screenkey &") }, + + { 0, XF86XK_AudioMute, spawn, SHCMD("wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle; kill -44 $(pidof dwmblocks)") }, + { 0, XF86XK_AudioRaiseVolume, spawn, SHCMD("wpctl set-volume @DEFAULT_AUDIO_SINK@ 0%- && wpctl set-volume @DEFAULT_AUDIO_SINK@ 3%+; kill -44 $(pidof dwmblocks)") }, + { 0, XF86XK_AudioLowerVolume, spawn, SHCMD("wpctl set-volume @DEFAULT_AUDIO_SINK@ 0%+ && wpctl set-volume @DEFAULT_AUDIO_SINK@ 3%-; kill -44 $(pidof dwmblocks)") }, + { 0, XF86XK_AudioPrev, spawn, {.v = (const char*[]){ "mpc", "prev", NULL } } }, + { 0, XF86XK_AudioNext, spawn, {.v = (const char*[]){ "mpc", "next", NULL } } }, + { 0, XF86XK_AudioPause, spawn, {.v = (const char*[]){ "mpc", "pause", NULL } } }, + { 0, XF86XK_AudioPlay, spawn, {.v = (const char*[]){ "mpc", "play", NULL } } }, + { 0, XF86XK_AudioStop, spawn, {.v = (const char*[]){ "mpc", "stop", NULL } } }, + { 0, XF86XK_AudioRewind, spawn, {.v = (const char*[]){ "mpc", "seek", "-10", NULL } } }, + { 0, XF86XK_AudioForward, spawn, {.v = (const char*[]){ "mpc", "seek", "+10", NULL } } }, + { 0, XF86XK_AudioMedia, spawn, {.v = (const char*[]){ TERMINAL, "-e", "ncmpcpp", NULL } } }, + { 0, XF86XK_AudioMicMute, spawn, SHCMD("pactl set-source-mute @DEFAULT_SOURCE@ toggle") }, + /* { 0, XF86XK_PowerOff, spawn, {.v = (const char*[]){ "sysact", NULL } } }, */ + { 0, XF86XK_Calculator, spawn, {.v = (const char*[]){ TERMINAL, "-e", "bc", "-l", NULL } } }, + { 0, XF86XK_Sleep, spawn, {.v = (const char*[]){ "sudo", "-A", "zzz", NULL } } }, + { 0, XF86XK_WWW, spawn, {.v = (const char*[]){ BROWSER, NULL } } }, + { 0, XF86XK_DOS, spawn, {.v = termcmd } }, + { 0, XF86XK_ScreenSaver, spawn, SHCMD("slock & xset dpms force off; mpc pause; pauseallmpv") }, + { 0, XF86XK_TaskPane, spawn, {.v = (const char*[]){ TERMINAL, "-e", "htop", NULL } } }, + { 0, XF86XK_Mail, spawn, SHCMD(TERMINAL " -e neomutt ; pkill -RTMIN+12 dwmblocks") }, + { 0, XF86XK_MyComputer, spawn, {.v = (const char*[]){ TERMINAL, "-e", "lfub", "/", NULL } } }, + /* { 0, XF86XK_Battery, spawn, SHCMD("") }, */ + { 0, XF86XK_Launch1, spawn, {.v = (const char*[]){ "xset", "dpms", "force", "off", NULL } } }, + { 0, XF86XK_TouchpadToggle, spawn, SHCMD("(synclient | grep 'TouchpadOff.*1' && synclient TouchpadOff=0) || synclient TouchpadOff=1") }, + { 0, XF86XK_TouchpadOff, spawn, {.v = (const char*[]){ "synclient", "TouchpadOff=1", NULL } } }, + { 0, XF86XK_TouchpadOn, spawn, {.v = (const char*[]){ "synclient", "TouchpadOff=0", NULL } } }, + { 0, XF86XK_MonBrightnessUp, spawn, {.v = (const char*[]){ "xbacklight", "-inc", "15", NULL } } }, + { 0, XF86XK_MonBrightnessDown, spawn, {.v = (const char*[]){ "xbacklight", "-dec", "15", NULL } } }, + + /* { MODKEY|Mod4Mask, XK_h, incrgaps, {.i = +1 } }, */ + /* { MODKEY|Mod4Mask, XK_l, incrgaps, {.i = -1 } }, */ + /* { MODKEY|Mod4Mask|ShiftMask, XK_h, incrogaps, {.i = +1 } }, */ + /* { MODKEY|Mod4Mask|ShiftMask, XK_l, incrogaps, {.i = -1 } }, */ + /* { MODKEY|Mod4Mask|ControlMask, XK_h, incrigaps, {.i = +1 } }, */ + /* { MODKEY|Mod4Mask|ControlMask, XK_l, incrigaps, {.i = -1 } }, */ + /* { MODKEY|Mod4Mask|ShiftMask, XK_0, defaultgaps, {0} }, */ + /* { MODKEY, XK_y, incrihgaps, {.i = +1 } }, */ + /* { MODKEY, XK_o, incrihgaps, {.i = -1 } }, */ + /* { MODKEY|ControlMask, XK_y, incrivgaps, {.i = +1 } }, */ + /* { MODKEY|ControlMask, XK_o, incrivgaps, {.i = -1 } }, */ + /* { MODKEY|Mod4Mask, XK_y, incrohgaps, {.i = +1 } }, */ + /* { MODKEY|Mod4Mask, XK_o, incrohgaps, {.i = -1 } }, */ + /* { MODKEY|ShiftMask, XK_y, incrovgaps, {.i = +1 } }, */ + /* { MODKEY|ShiftMask, XK_o, incrovgaps, {.i = -1 } }, */ + +}; + +/* button definitions */ +/* click can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */ +static const Button buttons[] = { + /* click event mask button function argument */ +#ifndef __OpenBSD__ + { ClkWinTitle, 0, Button2, zoom, {0} }, + { ClkStatusText, 0, Button1, sigdwmblocks, {.i = 1} }, + { ClkStatusText, 0, Button2, sigdwmblocks, {.i = 2} }, + { ClkStatusText, 0, Button3, sigdwmblocks, {.i = 3} }, + { ClkStatusText, 0, Button4, sigdwmblocks, {.i = 4} }, + { ClkStatusText, 0, Button5, sigdwmblocks, {.i = 5} }, + { ClkStatusText, ShiftMask, Button1, sigdwmblocks, {.i = 6} }, +#endif + { ClkStatusText, ShiftMask, Button3, spawn, SHCMD(TERMINAL " -e nvim ~/.local/src/dwmblocks/config.h") }, + { ClkClientWin, MODKEY, Button1, movemouse, {0} }, + { ClkClientWin, MODKEY, Button2, defaultgaps, {0} }, + { ClkClientWin, MODKEY, Button3, resizemouse, {0} }, + { ClkClientWin, MODKEY, Button4, incrgaps, {.i = +1} }, + { ClkClientWin, MODKEY, Button5, incrgaps, {.i = -1} }, + { ClkTagBar, 0, Button1, view, {0} }, + { ClkTagBar, 0, Button3, toggleview, {0} }, + { ClkTagBar, MODKEY, Button1, tag, {0} }, + { ClkTagBar, MODKEY, Button3, toggletag, {0} }, + { ClkTagBar, 0, Button4, shiftview, {.i = -1} }, + { ClkTagBar, 0, Button5, shiftview, {.i = 1} }, + { ClkRootWin, 0, Button2, togglebar, {0} }, +}; + diff --git a/config.mk b/config.mk new file mode 100644 index 0000000..0d70060 --- /dev/null +++ b/config.mk @@ -0,0 +1,39 @@ +# dwm version +VERSION = 6.4 + +# 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) +#MANPREFIX = ${PREFIX}/man +#FREETYPEINC = ${X11INC}/freetype2 + +# includes and libs +INCS = -I${X11INC} -I${FREETYPEINC} +LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} -lX11-xcb -lxcb -lxcb-res + +# 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/drw.c b/drw.c new file mode 100644 index 0000000..975ca63 --- /dev/null +++ b/drw.c @@ -0,0 +1,453 @@ +/* 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, 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); + + dest->pixel |= 0xff << 24; +} + +/* 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, 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); + FcPatternAddBool(fcpattern, FC_COLOR, FcFalse); + + 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/drw.h b/drw.h new file mode 100644 index 0000000..000fb09 --- /dev/null +++ b/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, 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, 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/drw.o b/drw.o new file mode 100644 index 0000000000000000000000000000000000000000..873b42847d1638f27955689a1d6b50c8001a562e GIT binary patch literal 11120 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 YgZR=N#zDfzYUwhKlC!I+uXFzY0U^gBasU7T literal 0 HcmV?d00001 diff --git a/dwm b/dwm new file mode 100755 index 0000000000000000000000000000000000000000..b7697b8ba3ba69a43b264d75cedb5ce430ad82a6 GIT binary patch literal 105200 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

8F!=~U6w6_Rw7_r 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 literal 0 HcmV?d00001 diff --git a/dwm.1 b/dwm.1 new file mode 100644 index 0000000..ddc8321 --- /dev/null +++ b/dwm.1 @@ -0,0 +1,176 @@ +.TH DWM 1 dwm\-VERSION +.SH NAME +dwm \- dynamic window manager +.SH SYNOPSIS +.B dwm +.RB [ \-v ] +.SH DESCRIPTION +dwm is a dynamic window manager for X. It manages windows in tiled, monocle +and floating layouts. Either layout can be applied dynamically, optimising the +environment for the application in use and the task performed. +.P +In tiled layouts windows are managed in a master and stacking area. The master +area on the left contains one window by default, and the stacking area on the +right contains all other windows. The number of master area windows can be +adjusted from zero to an arbitrary number. In monocle layout all windows are +maximised to the screen size. In floating layout windows can be resized and +moved freely. Dialog windows are always managed floating, regardless of the +layout applied. +.P +Windows are grouped by tags. Each window can be tagged with one or multiple +tags. Selecting certain tags displays all windows with these tags. +.P +Each screen contains a small status bar which displays all available tags, the +layout, the title of the focused window, and the text read from the root window +name property, if the screen is focused. A floating window is indicated with an +empty square and a maximised floating window is indicated with a filled square +before the windows title. The selected tags are indicated with a different +color. The tags of the focused window are indicated with a filled square in the +top left corner. The tags which are applied to one or more windows are +indicated with an empty square in the top left corner. +.P +dwm draws a small border around windows to indicate the focus state. +.SH OPTIONS +.TP +.B \-v +prints version information to stderr, then exits. +.SH USAGE +.SS Status bar +.TP +.B X root window name +is read and displayed in the status text area. It can be set with the +.BR xsetroot (1) +command. +.TP +.B Button1 +click on a tag label to display all windows with that tag, click on the layout +label toggles between tiled and floating layout. +.TP +.B Button3 +click on a tag label adds/removes all windows with that tag to/from the view. +.TP +.B Mod1\-Button1 +click on a tag label applies that tag to the focused window. +.TP +.B Mod1\-Button3 +click on a tag label adds/removes that tag to/from the focused window. +.SS Keyboard commands +.TP +.B Mod1\-Shift\-Return +Start +.BR st(1). +.TP +.B Mod1\-p +Spawn +.BR dmenu(1) +for launching other programs. +.TP +.B Mod1\-, +Focus previous screen, if any. +.TP +.B Mod1\-. +Focus next screen, if any. +.TP +.B Mod1\-Shift\-, +Send focused window to previous screen, if any. +.TP +.B Mod1\-Shift\-. +Send focused window to next screen, if any. +.TP +.B Mod1\-b +Toggles bar on and off. +.TP +.B Mod1\-t +Sets tiled layout. +.TP +.B Mod1\-f +Sets floating layout. +.TP +.B Mod1\-m +Sets monocle layout. +.TP +.B Mod1\-space +Toggles between current and previous layout. +.TP +.B Mod1\-j +Focus next window. +.TP +.B Mod1\-k +Focus previous window. +.TP +.B Mod1\-i +Increase number of windows in master area. +.TP +.B Mod1\-d +Decrease number of windows in master area. +.TP +.B Mod1\-l +Increase master area size. +.TP +.B Mod1\-h +Decrease master area size. +.TP +.B Mod1\-Return +Zooms/cycles focused window to/from master area (tiled layouts only). +.TP +.B Mod1\-Shift\-c +Close focused window. +.TP +.B Mod1\-Shift\-space +Toggle focused window between tiled and floating state. +.TP +.B Mod1\-Tab +Toggles to the previously selected tags. +.TP +.B Mod1\-Shift\-[1..n] +Apply nth tag to focused window. +.TP +.B Mod1\-Shift\-0 +Apply all tags to focused window. +.TP +.B Mod1\-Control\-Shift\-[1..n] +Add/remove nth tag to/from focused window. +.TP +.B Mod1\-[1..n] +View all windows with nth tag. +.TP +.B Mod1\-0 +View all windows with any tag. +.TP +.B Mod1\-Control\-[1..n] +Add/remove all windows with nth tag to/from the view. +.TP +.B Mod1\-Shift\-q +Quit dwm. +.SS Mouse commands +.TP +.B Mod1\-Button1 +Move focused window while dragging. Tiled windows will be toggled to the floating state. +.TP +.B Mod1\-Button2 +Toggles focused window between floating and tiled state. +.TP +.B Mod1\-Button3 +Resize focused window while dragging. Tiled windows will be toggled to the floating state. +.SH CUSTOMIZATION +dwm is customized by creating a custom config.h and (re)compiling the source +code. This keeps it fast, secure and simple. +.SH SEE ALSO +.BR dmenu (1), +.BR st (1) +.SH ISSUES +Java applications which use the XToolkit/XAWT backend may draw grey windows +only. The XToolkit/XAWT backend breaks ICCCM-compliance in recent JDK 1.5 and early +JDK 1.6 versions, because it assumes a reparenting window manager. Possible workarounds +are using JDK 1.4 (which doesn't contain the XToolkit/XAWT backend) or setting the +environment variable +.BR AWT_TOOLKIT=MToolkit +(to use the older Motif backend instead) or running +.B xprop -root -f _NET_WM_NAME 32a -set _NET_WM_NAME LG3D +or +.B wmname LG3D +(to pretend that a non-reparenting window manager is running that the +XToolkit/XAWT backend can recognize) or when using OpenJDK setting the environment variable +.BR _JAVA_AWT_WM_NONREPARENTING=1 . +.SH BUGS +Send all bug reports with a patch to hackers@suckless.org. diff --git a/dwm.c b/dwm.c new file mode 100644 index 0000000..3c0afbf --- /dev/null +++ b/dwm.c @@ -0,0 +1,2732 @@ +/* See LICENSE file for copyright and license details. + * + * dynamic window manager is designed like any other X client as well. It is + * driven through handling X events. In contrast to other X clients, a window + * manager selects for SubstructureRedirectMask on the root window, to receive + * events about window (dis-)appearance. Only one X connection at a time is + * allowed to select for this event mask. + * + * The event handlers of dwm are organized in an array which is accessed + * whenever a new event has been fetched. This allows event dispatching + * in O(1) time. + * + * Each child of the root window is called a client, except windows which have + * set the override_redirect flag. Clients are organized in a linked client + * list on each monitor, the focus history is remembered through a stack list + * on each monitor. Each client contains a bit array to indicate the tags of a + * client. + * + * Keys and tagging rules are organized as arrays and defined in config.h. + * + * To understand everything else, start reading main(). + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef XINERAMA +#include +#endif /* XINERAMA */ +#include +#include +#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 GETINC(X) ((X) - 2000) +#define INC(X) ((X) + 2000) +#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 ISINC(X) ((X) > 1000 && (X) < 3000) +#define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags]) || C->issticky) +#define PREVSEL 3000 +#define LENGTH(X) (sizeof X / sizeof X[0]) +#define MOUSEMASK (BUTTONMASK|PointerMotionMask) +#define MOD(N,M) ((N)%(M) < 0 ? (N)%(M) + (M) : (N)%(M)) +#define WIDTH(X) ((X)->w + 2 * (X)->bw) +#define HEIGHT(X) ((X)->h + 2 * (X)->bw) +#define NUMTAGS (LENGTH(tags) + LENGTH(scratchpads)) +#define TAGMASK ((1 << NUMTAGS) - 1) +#define SPTAG(i) ((1 << LENGTH(tags)) << (i)) +#define SPTAGMASK (((1 << LENGTH(scratchpads))-1) << LENGTH(tags)) +#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) +#define XRDB_LOAD_COLOR(R,V) if (XrmGetResource(xrdb, R, NULL, &type, &value) == True) { \ + if (value.addr != NULL && strnlen(value.addr, 8) == 7 && value.addr[0] == '#') { \ + int i = 1; \ + for (; i <= 6; i++) { \ + if (value.addr[i] < 48) break; \ + if (value.addr[i] > 57 && value.addr[i] < 65) break; \ + if (value.addr[i] > 70 && value.addr[i] < 97) break; \ + if (value.addr[i] > 102) break; \ + } \ + if (i == 7) { \ + strncpy(V, value.addr, 7); \ + V[7] = '\0'; \ + } \ + } \ + } +#define TRUNC(X,A,B) (MAX((A), MIN((X), (B)))) + +/* enums */ +enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ +enum { SchemeNorm, SchemeSel }; /* color schemes */ +enum { NetSupported, NetWMName, NetWMState, NetWMCheck, + NetWMFullscreen, NetActiveWindow, NetWMWindowType, + NetWMWindowTypeDialog, NetClientList, NetClientInfo, 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, isterminal, noswallow, issticky; + pid_t pid; + Client *next; + Client *snext; + Client *swallowing; + 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 */ + int gappih; /* horizontal gap between windows */ + int gappiv; /* vertical gap between windows */ + int gappoh; /* horizontal outer gaps */ + int gappov; /* vertical outer gaps */ + 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 isterminal; + int noswallow; + int monitor; +} Rule; + +/* Xresources preferences */ +enum resource_type { + STRING = 0, + INTEGER = 1, + FLOAT = 2 +}; + +typedef struct { + char *name; + enum resource_type type; + void *dst; +} ResourcePref; + +/* 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 void copyvalidchars(char *text, char *rawtext); +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 loadxrdb(void); +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 pushstack(const Arg *arg); +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 runAutostart(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 setclienttagprop(Client *c); +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 sigchld(int unused); +#ifndef __OpenBSD__ +static int getdwmblockspid(); +static void sigdwmblocks(const Arg *arg); +#endif +static void sighup(int unused); +static void sigterm(int unused); +static void spawn(const Arg *arg); +static int stackpos(const Arg *arg); +static void tag(const Arg *arg); +static void tagmon(const Arg *arg); +static void togglebar(const Arg *arg); +static void togglefloating(const Arg *arg); +static void togglescratch(const Arg *arg); +static void togglesticky(const Arg *arg); +static void togglefullscr(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 xrdb(const Arg *arg); +static void zoom(const Arg *arg); +static void load_xresources(void); +static void resource_load(XrmDatabase db, char *name, enum resource_type rtype, void *dst); + +static pid_t getparentprocess(pid_t p); +static int isdescprocess(pid_t p, pid_t c); +static Client *swallowingclient(Window w); +static Client *termforwin(const Client *c); +static pid_t winpid(Window w); + + +/* variables */ +static const char broken[] = "broken"; +static char stext[256]; +static char rawstext[256]; +static int dwmblockssig; +pid_t dwmblockspid = 0; +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 restart = 0; +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; + +static xcb_connection_t *xcon; + +/* 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->isterminal = r->isterminal; + c->isfloating = r->isfloating; + c->noswallow = r->noswallow; + c->tags |= r->tags; + if ((r->tags & SPTAGMASK) && r->isfloating) { + c->x = c->mon->wx + (c->mon->ww / 2 - WIDTH(c) / 2); + c->y = c->mon->wy + (c->mon->wh / 2 - HEIGHT(c) / 2); + } + + 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] & ~SPTAGMASK); +} + +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 +swallow(Client *p, Client *c) +{ + if (c->noswallow || c->isterminal) + return; + if (!swallowfloating && c->isfloating) + return; + + detach(c); + detachstack(c); + + setclientstate(c, WithdrawnState); + XUnmapWindow(dpy, p->win); + + p->swallowing = c; + c->mon = p->mon; + + Window w = p->win; + p->win = c->win; + c->win = w; + updatetitle(p); + + XWindowChanges wc; + wc.border_width = p->bw; + XConfigureWindow(dpy, p->win, CWBorderWidth, &wc); + XMoveResizeWindow(dpy, p->win, p->x, p->y, p->w, p->h); + XSetWindowBorder(dpy, p->win, scheme[SchemeNorm][ColBorder].pixel); + + arrange(p->mon); + configure(p); + updateclientlist(); +} + +void +unswallow(Client *c) +{ + c->win = c->swallowing->win; + + free(c->swallowing); + c->swallowing = NULL; + + /* unfullscreen the client */ + setfullscreen(c, 0); + updatetitle(c); + arrange(c->mon); + XMapWindow(dpy, c->win); + + XWindowChanges wc; + wc.border_width = c->bw; + XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); + XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); + XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel); + + setclientstate(c, NormalState); + focus(NULL); + arrange(c->mon); +} + +void +buttonpress(XEvent *e) +{ + unsigned int i, x, click, occ = 0; + 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; + for (c = m->clients; c; c = c->next) + occ |= c->tags == 255 ? 0 : c->tags; + do { + /* do not reserve space for vacant tags */ + if (!(occ & 1 << i || m->tagset[m->seltags] & 1 << i)) + continue; + 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 > (x = selmon->ww - (int)TEXTW(stext) + lrpad)) { + click = ClkStatusText; + + char *text = rawstext; + int i = -1; + char ch; + dwmblockssig = 0; + while (text[++i]) { + if ((unsigned char)text[i] < ' ') { + ch = text[i]; + text[i] = '\0'; + x += TEXTW(text) - lrpad; + text[i] = ch; + text += i+1; + i = -1; + if (x >= ev->x) break; + dwmblockssig = ch; + } + } + } 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); +} + +void +copyvalidchars(char *text, char *rawtext) +{ + int i = -1, j = 0; + + while(rawtext[++i]) { + if ((unsigned char)rawtext[i] >= ' ') { + text[j++] = rawtext[i]; + } + } + text[j] = '\0'; +} + +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->gappih = gappih; + m->gappiv = gappiv; + m->gappoh = gappoh; + m->gappov = gappov; + 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); + + else if ((c = swallowingclient(ev->window))) + unmanage(c->swallowing, 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 == 255 ? 0 : c->tags; + if (c->isurgent) + urg |= c->tags; + } + x = 0; + for (i = 0; i < LENGTH(tags); i++) { + /* do not draw vacant tags */ + if (!(occ & 1 << i || m->tagset[m->seltags] & 1 << i)) + continue; + + 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); + 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->issticky && !selmon->sel->issticky)); c = c->snext); + + if (!c) /* No windows found; check for available stickies */ + 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) +{ + int i = stackpos(arg); + Client *c, *p; + + if(i < 0 || !selmon->sel || (selmon->sel->isfullscreen && lockfullscreen)) + return; + + for(p = NULL, c = selmon->clients; c && (i || !ISVISIBLE(c)); + i -= ISVISIBLE(c) ? 1 : 0, p = c, c = c->next); + focus(c ? c : p); + 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; +} + +#ifndef __OpenBSD__ +int +getdwmblockspid() +{ + char buf[16]; + FILE *fp = popen("pidof -s dwmblocks", "r"); + fgets(buf, sizeof(buf), fp); + pid_t pid = strtoul(buf, NULL, 10); + pclose(fp); + dwmblockspid = pid; + return pid != 0 ? 0 : -1; +} +#endif + +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 +loadxrdb() +{ + Display *display; + char * resm; + XrmDatabase xrdb; + char *type; + XrmValue value; + + display = XOpenDisplay(NULL); + + if (display != NULL) { + resm = XResourceManagerString(display); + + if (resm != NULL) { + xrdb = XrmGetStringDatabase(resm); + + if (xrdb != NULL) { + XRDB_LOAD_COLOR("dwm.color0", normbordercolor); + XRDB_LOAD_COLOR("dwm.color0", normbgcolor); + XRDB_LOAD_COLOR("dwm.color4", normfgcolor); + XRDB_LOAD_COLOR("dwm.color8", selbordercolor); + XRDB_LOAD_COLOR("dwm.color4", selbgcolor); + XRDB_LOAD_COLOR("dwm.color0", selfgcolor); + } + } + } + + XCloseDisplay(display); +} + +void +manage(Window w, XWindowAttributes *wa) +{ + Client *c, *t = NULL, *term = NULL; + Window trans = None; + XWindowChanges wc; + + c = ecalloc(1, sizeof(Client)); + c->win = w; + c->pid = winpid(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); + term = termforwin(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); + { + int format; + unsigned long *data, n, extra; + Monitor *m; + Atom atom; + if (XGetWindowProperty(dpy, c->win, netatom[NetClientInfo], 0L, 2L, False, XA_CARDINAL, + &atom, &format, &n, &extra, (unsigned char **)&data) == Success && n == 2) { + c->tags = *data; + for (m = mons; m; m = m->next) { + if (m->num == *(data+1)) { + c->mon = m; + break; + } + } + } + if (n > 0) + XFree(data); + } + setclienttagprop(c); + + c->x = c->mon->mx + (c->mon->mw - WIDTH(c)) / 2; + c->y = c->mon->my + (c->mon->mh - HEIGHT(c)) / 2; + 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(selmon->sel && selmon->sel->isfullscreen && !c->isfloating) + setfullscreen(selmon->sel, 0); + if (c->mon == selmon) + unfocus(selmon->sel, 0); + c->mon->sel = c; + XMapWindow(dpy, c->win); + if (term) + swallow(term, c); + arrange(c->mon); + 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; + int oh, ov, ih, iv; + Client *c; + + getgaps(m, &oh, &ov, &ih, &iv, &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 + ov, m->wy + oh, m->ww - 2 * c->bw - 2 * ov, m->wh - 2 * c->bw - 2 * oh, 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 +pushstack(const Arg *arg) { + int i = stackpos(arg); + Client *sel = selmon->sel, *c, *p; + + if(i < 0 || !sel) + return; + else if(i == 0) { + detach(sel); + attach(sel); + } + else { + for(p = NULL, c = selmon->clients; c; p = c, c = c->next) + if(!(i -= (ISVISIBLE(c) && c != sel))) + break; + c = c ? c : p; + detach(sel); + sel->next = c->next; + c->next = sel; + } + arrange(selmon); +} + +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: + updatesizehints(c); + 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) +{ + if(arg->i) restart = 1; + 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 +runAutostart(void) { + system("killall -q dwmblocks; dwmblocks &"); +} + +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); + setclienttagprop(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); + } +} + +int +stackpos(const Arg *arg) { + int n, i; + Client *c, *l; + + if(!selmon->clients) + return -1; + + if(arg->i == PREVSEL) { + for(l = selmon->stack; l && (!ISVISIBLE(l) || l == selmon->sel); l = l->snext); + if(!l) + return -1; + for(i = 0, c = selmon->clients; c != l; i += ISVISIBLE(c) ? 1 : 0, c = c->next); + return i; + } + else if(ISINC(arg->i)) { + if(!selmon->sel) + return -1; + for(i = 0, c = selmon->clients; c != selmon->sel; i += ISVISIBLE(c) ? 1 : 0, c = c->next); + for(n = i; c; n += ISVISIBLE(c) ? 1 : 0, c = c->next); + return MOD(i + GETINC(arg->i), n); + } + else if(arg->i < 0) { + for(i = 0, c = selmon->clients; c; i += ISVISIBLE(c) ? 1 : 0, c = c->next); + return MAX(i + arg->i, 0); + } + else + return arg->i; +} + +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; + + /* clean up any zombies immediately */ + sigchld(0); + + signal(SIGHUP, sighup); + signal(SIGTERM, sigterm); + + /* 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); + netatom[NetClientInfo] = XInternAtom(dpy, "_NET_CLIENT_INFO", 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]); + XDeleteProperty(dpy, root, netatom[NetClientInfo]); + /* 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)) { + if ((c->tags & SPTAGMASK) && c->isfloating) { + c->x = c->mon->wx + (c->mon->ww / 2 - WIDTH(c) / 2); + c->y = c->mon->wy + (c->mon->wh / 2 - HEIGHT(c) / 2); + } + /* 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 +sighup(int unused) +{ + Arg a = {.i = 1}; + quit(&a); +} + +void +sigterm(int unused) +{ + Arg a = {.i = 0}; + quit(&a); +} + +#ifndef __OpenBSD__ +void +sigdwmblocks(const Arg *arg) +{ + union sigval sv; + sv.sival_int = 0 | (dwmblockssig << 8) | arg->i; + if (!dwmblockspid) + if (getdwmblockspid() == -1) + return; + + if (sigqueue(dwmblockspid, SIGUSR1, sv) == -1) { + if (errno == ESRCH) { + if (!getdwmblockspid()) + sigqueue(dwmblockspid, SIGUSR1, sv); + } + } +} +#endif + +void +sigchld(int unused) +{ + if (signal(SIGCHLD, sigchld) == SIG_ERR) + die("can't install SIGCHLD handler:"); + while (0 < waitpid(-1, NULL, WNOHANG)); +} + +void +spawn(const Arg *arg) +{ + if (fork() == 0) { + if (dpy) + close(ConnectionNumber(dpy)); + setsid(); + execvp(((char **)arg->v)[0], (char **)arg->v); + die("dwm: execvp '%s' failed:", ((char **)arg->v)[0]); + } +} + +void +setclienttagprop(Client *c) +{ + long data[] = { (long) c->tags, (long) c->mon->num }; + XChangeProperty(dpy, c->win, netatom[NetClientInfo], XA_CARDINAL, 32, + PropModeReplace, (unsigned char *) data, 2); +} + +void +tag(const Arg *arg) +{ + Client *c; + if (selmon->sel && arg->ui & TAGMASK) { + c = selmon->sel; + selmon->sel->tags = arg->ui & TAGMASK; + setclienttagprop(c); + focus(NULL); + arrange(selmon); + } +} + +void +tagmon(const Arg *arg) +{ + if (!selmon->sel || !mons->next) + return; + sendmon(selmon->sel, dirtomon(arg->i)); +} + +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 +togglefullscr(const Arg *arg) +{ + if(selmon->sel) + setfullscreen(selmon->sel, !selmon->sel->isfullscreen); +} + +void +togglesticky(const Arg *arg) +{ + if (!selmon->sel) + return; + selmon->sel->issticky = !selmon->sel->issticky; + arrange(selmon); +} + +void +togglescratch(const Arg *arg) +{ + Client *c; + unsigned int found = 0; + unsigned int scratchtag = SPTAG(arg->ui); + Arg sparg = {.v = scratchpads[arg->ui].cmd}; + + for (c = selmon->clients; c && !(found = c->tags & scratchtag); c = c->next); + if (found) { + unsigned int newtagset = selmon->tagset[selmon->seltags] ^ scratchtag; + if (newtagset) { + selmon->tagset[selmon->seltags] = newtagset; + focus(NULL); + arrange(selmon); + } + if (ISVISIBLE(c)) { + focus(c); + restack(selmon); + } + } else { + selmon->tagset[selmon->seltags] |= scratchtag; + spawn(&sparg); + } +} + +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; + setclienttagprop(selmon->sel); + 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; + + if (c->swallowing) { + unswallow(c); + return; + } + + Client *s = swallowingclient(c->win); + if (s) { + free(s->swallowing); + s->swallowing = NULL; + arrange(m); + focus(NULL); + return; + } + + 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); + + if (!s) { + arrange(m); + focus(NULL); + updateclientlist(); + } +} + +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() +{ + 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); +} + +void +updatestatus(void) +{ + if (!gettextprop(root, XA_WM_NAME, rawstext, sizeof(rawstext))) + strcpy(stext, "dwm-"VERSION); + else + copyvalidchars(stext, rawstext); + 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); +} + +pid_t +winpid(Window w) +{ + pid_t result = 0; + + xcb_res_client_id_spec_t spec = {0}; + spec.client = w; + spec.mask = XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID; + + xcb_generic_error_t *e = NULL; + xcb_res_query_client_ids_cookie_t c = xcb_res_query_client_ids(xcon, 1, &spec); + xcb_res_query_client_ids_reply_t *r = xcb_res_query_client_ids_reply(xcon, c, &e); + + if (!r) + return (pid_t)0; + + xcb_res_client_id_value_iterator_t i = xcb_res_query_client_ids_ids_iterator(r); + for (; i.rem; xcb_res_client_id_value_next(&i)) { + spec = i.data->spec; + if (spec.mask & XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID) { + uint32_t *t = xcb_res_client_id_value_value(i.data); + result = *t; + break; + } + } + + free(r); + + if (result == (pid_t)-1) + result = 0; + return result; +} + +pid_t +getparentprocess(pid_t p) +{ + unsigned int v = 0; + +#if defined(__linux__) + FILE *f; + char buf[256]; + snprintf(buf, sizeof(buf) - 1, "/proc/%u/stat", (unsigned)p); + + if (!(f = fopen(buf, "r"))) + return (pid_t)0; + + if (fscanf(f, "%*u %*s %*c %u", (unsigned *)&v) != 1) + v = (pid_t)0; + fclose(f); +#elif defined(__FreeBSD__) + struct kinfo_proc *proc = kinfo_getproc(p); + if (!proc) + return (pid_t)0; + + v = proc->ki_ppid; + free(proc); +#endif + return (pid_t)v; +} + +int +isdescprocess(pid_t p, pid_t c) +{ + while (p != c && c != 0) + c = getparentprocess(c); + + return (int)c; +} + +Client * +termforwin(const Client *w) +{ + Client *c; + Monitor *m; + + if (!w->pid || w->isterminal) + return NULL; + + for (m = mons; m; m = m->next) { + for (c = m->clients; c; c = c->next) { + if (c->isterminal && !c->swallowing && c->pid && isdescprocess(c->pid, w->pid)) + return c; + } + } + + return NULL; +} + +Client * +swallowingclient(Window w) +{ + Client *c; + Monitor *m; + + for (m = mons; m; m = m->next) { + for (c = m->clients; c; c = c->next) { + if (c->swallowing && c->swallowing->win == w) + return c; + } + } + + return NULL; +} + +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 +xrdb(const Arg *arg) +{ + loadxrdb(); + int i; + for (i = 0; i < LENGTH(colors); i++) + scheme[i] = drw_scm_create(drw, colors[i], 3); + focus(NULL); + arrange(NULL); +} + +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); +} + +void +resource_load(XrmDatabase db, char *name, enum resource_type rtype, void *dst) +{ + char *sdst = NULL; + int *idst = NULL; + float *fdst = NULL; + + sdst = dst; + idst = dst; + fdst = dst; + + char fullname[256]; + char *type; + XrmValue ret; + + snprintf(fullname, sizeof(fullname), "%s.%s", "dwm", name); + fullname[sizeof(fullname) - 1] = '\0'; + + XrmGetResource(db, fullname, "*", &type, &ret); + if (!(ret.addr == NULL || strncmp("String", type, 64))) + { + switch (rtype) { + case STRING: + strcpy(sdst, ret.addr); + break; + case INTEGER: + *idst = strtoul(ret.addr, NULL, 10); + break; + case FLOAT: + *fdst = strtof(ret.addr, NULL); + break; + } + } +} + +void +load_xresources(void) +{ + Display *display; + char *resm; + XrmDatabase db; + ResourcePref *p; + + display = XOpenDisplay(NULL); + resm = XResourceManagerString(display); + if (!resm) + return; + + db = XrmGetStringDatabase(resm); + for (p = resources; p < resources + LENGTH(resources); p++) + resource_load(db, p->name, p->type, p->dst); + XCloseDisplay(display); +} + +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"); + if (!(xcon = XGetXCBConnection(dpy))) + die("dwm: cannot get xcb connection\n"); + checkotherwm(); + XrmInitialize(); + load_xresources(); + setup(); +#ifdef __OpenBSD__ + if (pledge("stdio rpath proc exec", NULL) == -1) + die("pledge"); +#endif /* __OpenBSD__ */ + scan(); + runAutostart(); + run(); + if(restart) execvp(argv[0], argv); + cleanup(); + XCloseDisplay(dpy); + return EXIT_SUCCESS; +} + diff --git a/dwm.o b/dwm.o new file mode 100644 index 0000000000000000000000000000000000000000..a97391f980912692141289caf1aa4ae97fd56470 GIT binary patch literal 101072 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

fFH_8mqyb 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 b{VRISn3AtQJq#wTpS27AmuOv`^y~jGMK~ge literal 0 HcmV?d00001 diff --git a/shiftview.c b/shiftview.c new file mode 100644 index 0000000..bb43969 --- /dev/null +++ b/shiftview.c @@ -0,0 +1,65 @@ +/** Function to shift the current view to the left/right + * + * @param: "arg->i" stores the number of tags to shift right (positive value) + * or left (negative value) + */ +void +shiftview(const Arg *arg) +{ + Arg shifted; + Client *c; + unsigned int tagmask = 0; + + for (c = selmon->clients; c; c = c->next) + if (!(c->tags & SPTAGMASK)) + tagmask = tagmask | c->tags; + + shifted.ui = selmon->tagset[selmon->seltags] & ~SPTAGMASK; + if (arg->i > 0) /* left circular shift */ + do { + shifted.ui = (shifted.ui << arg->i) + | (shifted.ui >> (LENGTH(tags) - arg->i)); + shifted.ui &= ~SPTAGMASK; + } while (tagmask && !(shifted.ui & tagmask)); + else /* right circular shift */ + do { + shifted.ui = (shifted.ui >> (- arg->i) + | shifted.ui << (LENGTH(tags) + arg->i)); + shifted.ui &= ~SPTAGMASK; + } while (tagmask && !(shifted.ui & tagmask)); + + view(&shifted); +} + +void +shifttag(const Arg *arg) +{ + Arg a; + Client *c; + unsigned visible = 0; + int i = arg->i; + int count = 0; + int nextseltags, curseltags = selmon->tagset[selmon->seltags]; + + do { + if(i > 0) // left circular shift + nextseltags = (curseltags << i) | (curseltags >> (LENGTH(tags) - i)); + + else // right circular shift + nextseltags = (curseltags >> - i) | (curseltags << (LENGTH(tags) + i)); + + // Check if tag is visible + for (c = selmon->clients; c && !visible; c = c->next) + if (nextseltags & c->tags) { + visible = 1; + break; + } + i += arg->i; + } while (!visible && ++count < 10); + + if (count < 10) { + a.i = nextseltags; + tag(&a); + } +} + diff --git a/transient.c b/transient.c new file mode 100644 index 0000000..040adb5 --- /dev/null +++ b/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/util.c b/util.c new file mode 100644 index 0000000..96b82c9 --- /dev/null +++ b/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/util.h b/util.h new file mode 100644 index 0000000..f633b51 --- /dev/null +++ b/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/util.o b/util.o new file mode 100644 index 0000000000000000000000000000000000000000..26ab857d2a56ff49969dd1704810e8d70d6b9cca GIT binary patch literal 2224 zcmbtUO=uHQ5T0$TjWtbT?V(Z+E3A@0U6N3trYg}!yGoF%6}1;llZ_4L&&{T!Dj2aI zLJ0KW(SrvM3LXl26dLs4)uSitQK*M{5R?jWX7|1Jv1~3n@R*rzzL_`s=DmG*HhHc` zl7Nc@r(nn9C_s4EjvcqmIQU=?u4s)d?a}wI2x$#dna~=uN?dCs6)o9FC^Jd4t}nOE zm_L+IZuvt=rR)zqRq9Chhn^`bmVTi;xAbde)zWX3wx!=GpHOGCX5Zl;8X6W1M%r5Q zBs%DQ(OS#OCJT&ctyv|YwGvABV?qgd5Jo*5W?}go8c?4Et$B-`kB(ckce}fts9jCC94s`5dPv|~ zy&<*_SAcOuMc?6uAL@b!yWnaUoTlzpALnHC$@{a;39-p8e5jhayqX3zITeOXPKSz_ z(G3F@vt=V!G_&X%dA$hPa@9;j8ABxl^jgjY-M$!Dm!xVdoV$Go{&Z}4#c;X{yK!;+ z4a1%M_X59P;C~iynh~$FF5q;xIKCm^BLAm=(<|WoEdlomct2er6za1V7w3B!PTxg~ z<1*SL#HYpO1T#3!=L2eGsbHq&QJaRX7r15U`IKtvH51fK%1nVeU#Wmk2zcz#Q>dOoD-O?(P5)_Si)Yk$+X|6{hy%t5r#85L6%&0Zp3gWJcc;c{MY;l z=2FclE>ZaYyk8#U`*R(~0NL~p6=jYQ_`i_Hcplg1F~DukA-(7Zx^QnGM(<3#tLQ?F zgappoh = oh; + selmon->gappov = ov; + selmon->gappih = ih; + selmon->gappiv = iv; + arrange(selmon); +} + +static void +togglegaps(const Arg *arg) +{ + enablegaps = !enablegaps; + arrange(NULL); +} + +static void +togglesmartgaps(const Arg *arg) +{ + smartgaps = !smartgaps; + arrange(NULL); +} + +static void +defaultgaps(const Arg *arg) +{ + setgaps(gappoh, gappov, gappih, gappiv); +} + +static void +incrgaps(const Arg *arg) +{ + setgaps( + selmon->gappoh + arg->i, + selmon->gappov + arg->i, + selmon->gappih + arg->i, + selmon->gappiv + arg->i + ); +} + +/* static void */ +/* incrigaps(const Arg *arg) */ +/* { */ +/* setgaps( */ +/* selmon->gappoh, */ +/* selmon->gappov, */ +/* selmon->gappih + arg->i, */ +/* selmon->gappiv + arg->i */ +/* ); */ +/* } */ + +/* static void */ +/* incrogaps(const Arg *arg) */ +/* { */ +/* setgaps( */ +/* selmon->gappoh + arg->i, */ +/* selmon->gappov + arg->i, */ +/* selmon->gappih, */ +/* selmon->gappiv */ +/* ); */ +/* } */ + +/* static void */ +/* incrohgaps(const Arg *arg) */ +/* { */ +/* setgaps( */ +/* selmon->gappoh + arg->i, */ +/* selmon->gappov, */ +/* selmon->gappih, */ +/* selmon->gappiv */ +/* ); */ +/* } */ + +/* static void */ +/* incrovgaps(const Arg *arg) */ +/* { */ +/* setgaps( */ +/* selmon->gappoh, */ +/* selmon->gappov + arg->i, */ +/* selmon->gappih, */ +/* selmon->gappiv */ +/* ); */ +/* } */ + +/* static void */ +/* incrihgaps(const Arg *arg) */ +/* { */ +/* setgaps( */ +/* selmon->gappoh, */ +/* selmon->gappov, */ +/* selmon->gappih + arg->i, */ +/* selmon->gappiv */ +/* ); */ +/* } */ + +/* static void */ +/* incrivgaps(const Arg *arg) */ +/* { */ +/* setgaps( */ +/* selmon->gappoh, */ +/* selmon->gappov, */ +/* selmon->gappih, */ +/* selmon->gappiv + arg->i */ +/* ); */ +/* } */ + +static void +getgaps(Monitor *m, int *oh, int *ov, int *ih, int *iv, unsigned int *nc) +{ + unsigned int n, oe, ie; + oe = ie = enablegaps; + Client *c; + + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); + if (smartgaps && n == 1) { + oe = 0; // outer gaps disabled when only one client + } + + *oh = m->gappoh*oe; // outer horizontal gap + *ov = m->gappov*oe; // outer vertical gap + *ih = m->gappih*ie; // inner horizontal gap + *iv = m->gappiv*ie; // inner vertical gap + *nc = n; // number of clients +} + +void +getfacts(Monitor *m, int msize, int ssize, float *mf, float *sf, int *mr, int *sr) +{ + unsigned int n; + float mfacts, sfacts; + int mtotal = 0, stotal = 0; + Client *c; + + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); + mfacts = MIN(n, m->nmaster); + sfacts = n - m->nmaster; + + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) + if (n < m->nmaster) + mtotal += msize / mfacts; + else + stotal += ssize / sfacts; + + *mf = mfacts; // total factor of master area + *sf = sfacts; // total factor of stack area + *mr = msize - mtotal; // the remainder (rest) of pixels after an even master split + *sr = ssize - stotal; // the remainder (rest) of pixels after an even stack split +} + +/*** + * Layouts + */ + +/* + * Bottomstack layout + gaps + * https://dwm.suckless.org/patches/bottomstack/ + */ + +static void +bstack(Monitor *m) +{ + unsigned int i, n; + int mx = 0, my = 0, mh = 0, mw = 0; + int sx = 0, sy = 0, sh = 0, sw = 0; + float mfacts, sfacts; + int mrest, srest; + Client *c; + + int oh, ov, ih, iv; + getgaps(m, &oh, &ov, &ih, &iv, &n); + + if (n == 0) + return; + + sx = mx = m->wx + ov; + sy = my = m->wy + oh; + sh = mh = m->wh - 2*oh; + mw = m->ww - 2*ov - iv * (MIN(n, m->nmaster) - 1); + sw = m->ww - 2*ov - iv * (n - m->nmaster - 1); + + if (m->nmaster && n > m->nmaster) { + sh = (mh - ih) * (1 - m->mfact); + mh = (mh - ih) * m->mfact; + sx = mx; + sy = my + mh + ih; + } + + getfacts(m, mw, sw, &mfacts, &sfacts, &mrest, &srest); + + for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) { + if (i < m->nmaster) { + resize(c, mx, my, (mw / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), mh - (2*c->bw), 0); + mx += WIDTH(c) + iv; + } else { + resize(c, sx, sy, (sw / sfacts) + ((i - m->nmaster) < srest ? 1 : 0) - (2*c->bw), sh - (2*c->bw), 0); + sx += WIDTH(c) + iv; + } + } +} + +/* + * Centred master layout + gaps + * https://dwm.suckless.org/patches/centeredmaster/ + */ + +void +centeredmaster(Monitor *m) +{ + unsigned int i, n; + int mx = 0, my = 0, mh = 0, mw = 0; + int lx = 0, ly = 0, lw = 0, lh = 0; + int rx = 0, ry = 0, rw = 0, rh = 0; + float mfacts = 0, lfacts = 0, rfacts = 0; + int mtotal = 0, ltotal = 0, rtotal = 0; + int mrest = 0, lrest = 0, rrest = 0; + Client *c; + + int oh, ov, ih, iv; + getgaps(m, &oh, &ov, &ih, &iv, &n); + + if (n == 0) + return; + + /* initialize areas */ + mx = m->wx + ov; + my = m->wy + oh; + mh = m->wh - 2*oh - ih * ((!m->nmaster ? n : MIN(n, m->nmaster)) - 1); + mw = m->ww - 2*ov; + lh = m->wh - 2*oh - ih * (((n - m->nmaster) / 2) - 1); + rh = m->wh - 2*oh - ih * (((n - m->nmaster) / 2) - ((n - m->nmaster) % 2 ? 0 : 1)); + + if (m->nmaster && n > m->nmaster) { + /* go mfact box in the center if more than nmaster clients */ + if (n - m->nmaster > 1) { + /* ||<-S->|<---M--->|<-S->|| */ + mw = (m->ww - 2*ov - 2*iv) * m->mfact; + lw = (m->ww - mw - 2*ov - 2*iv) / 2; + mx += lw + iv; + } else { + /* ||<---M--->|<-S->|| */ + mw = (mw - iv) * m->mfact; + lw = m->ww - mw - iv - 2*ov; + } + rw = lw; + lx = m->wx + ov; + ly = m->wy + oh; + rx = mx + mw + iv; + ry = m->wy + oh; + } + + /* calculate facts */ + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) { + if (!m->nmaster || n < m->nmaster) + mfacts += 1; + else if ((n - m->nmaster) % 2) + lfacts += 1; // total factor of left hand stack area + else + rfacts += 1; // total factor of right hand stack area + } + + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) + if (!m->nmaster || n < m->nmaster) + mtotal += mh / mfacts; + else if ((n - m->nmaster) % 2) + ltotal += lh / lfacts; + else + rtotal += rh / rfacts; + + mrest = mh - mtotal; + lrest = lh - ltotal; + rrest = rh - rtotal; + + for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) { + if (!m->nmaster || i < m->nmaster) { + /* nmaster clients are stacked vertically, in the center of the screen */ + resize(c, mx, my, mw - (2*c->bw), (mh / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), 0); + my += HEIGHT(c) + ih; + } else { + /* stack clients are stacked vertically */ + if ((i - m->nmaster) % 2 ) { + resize(c, lx, ly, lw - (2*c->bw), (lh / lfacts) + ((i - 2*m->nmaster) < 2*lrest ? 1 : 0) - (2*c->bw), 0); + ly += HEIGHT(c) + ih; + } else { + resize(c, rx, ry, rw - (2*c->bw), (rh / rfacts) + ((i - 2*m->nmaster) < 2*rrest ? 1 : 0) - (2*c->bw), 0); + ry += HEIGHT(c) + ih; + } + } + } +} + +void +centeredfloatingmaster(Monitor *m) +{ + unsigned int i, n; + float mfacts, sfacts; + int mrest, srest; + int mx = 0, my = 0, mh = 0, mw = 0; + int sx = 0, sy = 0, sh = 0, sw = 0; + Client *c; + + float mivf = 1.0; // master inner vertical gap factor + int oh, ov, ih, iv; + getgaps(m, &oh, &ov, &ih, &iv, &n); + + if (n == 0) + return; + + sx = mx = m->wx + ov; + sy = my = m->wy + oh; + sh = mh = m->wh - 2*oh; + mw = m->ww - 2*ov - iv*(n - 1); + sw = m->ww - 2*ov - iv*(n - m->nmaster - 1); + + if (m->nmaster && n > m->nmaster) { + mivf = 0.8; + /* go mfact box in the center if more than nmaster clients */ + if (m->ww > m->wh) { + mw = m->ww * m->mfact - iv*mivf*(MIN(n, m->nmaster) - 1); + mh = m->wh * 0.9 - 2*oh; + } else { + mw = m->ww * 0.9 - iv*mivf*(MIN(n, m->nmaster) - 1); + mh = m->wh * m->mfact; + } + mx = m->wx + (m->ww - mw) / 2; + my = m->wy + (m->wh - mh) / 2; + + sx = m->wx + ov; + sy = m->wy + oh; + sh = m->wh - 2*oh; + } + + getfacts(m, mw, sw, &mfacts, &sfacts, &mrest, &srest); + + for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) + if (i < m->nmaster) { + /* nmaster clients are stacked horizontally, in the center of the screen */ + resize(c, mx, my, (mw / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), mh - (2*c->bw), 0); + mx += WIDTH(c) + iv*mivf; + } else { + /* stack clients are stacked horizontally */ + resize(c, sx, sy, (sw / sfacts) + ((i - m->nmaster) < srest ? 1 : 0) - (2*c->bw), sh - (2*c->bw), 0); + sx += WIDTH(c) + iv; + } +} + +/* + * Deck layout + gaps + * https://dwm.suckless.org/patches/deck/ + */ + +static void +deck(Monitor *m) +{ + unsigned int i, n; + int mx = 0, my = 0, mh = 0, mw = 0; + int sx = 0, sy = 0, sh = 0, sw = 0; + float mfacts, sfacts; + int mrest, srest; + Client *c; + + int oh, ov, ih, iv; + getgaps(m, &oh, &ov, &ih, &iv, &n); + + if (n == 0) + return; + + sx = mx = m->wx + ov; + sy = my = m->wy + oh; + sh = mh = m->wh - 2*oh - ih * (MIN(n, m->nmaster) - 1); + sw = mw = m->ww - 2*ov; + + if (m->nmaster && n > m->nmaster) { + sw = (mw - iv) * (1 - m->mfact); + mw = (mw - iv) * m->mfact; + sx = mx + mw + iv; + sh = m->wh - 2*oh; + } + + getfacts(m, mh, sh, &mfacts, &sfacts, &mrest, &srest); + + if (n - m->nmaster > 0) /* override layout symbol */ + snprintf(m->ltsymbol, sizeof m->ltsymbol, "D %d", n - m->nmaster); + + for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) + if (i < m->nmaster) { + resize(c, mx, my, mw - (2*c->bw), (mh / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), 0); + my += HEIGHT(c) + ih; + } else { + resize(c, sx, sy, sw - (2*c->bw), sh - (2*c->bw), 0); + } +} + +/* + * Fibonacci layout + gaps + * https://dwm.suckless.org/patches/fibonacci/ + */ + +static void +fibonacci(Monitor *m, int s) +{ + unsigned int i, n; + int nx, ny, nw, nh; + int oh, ov, ih, iv; + Client *c; + + getgaps(m, &oh, &ov, &ih, &iv, &n); + + if (n == 0) + return; + + nx = m->wx + ov; + ny = oh; + nw = m->ww - 2*ov; + nh = m->wh - 2*oh; + + for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next)) { + if ((i % 2 && nh / 2 > 2*c->bw) + || (!(i % 2) && nw / 2 > 2*c->bw)) { + if (i < n - 1) { + if (i % 2) + nh = (nh - ih) / 2; + else + nw = (nw - iv) / 2; + + if ((i % 4) == 2 && !s) + nx += nw + iv; + else if ((i % 4) == 3 && !s) + ny += nh + ih; + } + if ((i % 4) == 0) { + if (s) + ny += nh + ih; + else + ny -= nh + ih; + } + else if ((i % 4) == 1) + nx += nw + iv; + else if ((i % 4) == 2) + ny += nh + ih; + else if ((i % 4) == 3) { + if (s) + nx += nw + iv; + else + nx -= nw + iv; + } + if (i == 0) { + if (n != 1) + nw = (m->ww - 2*ov - iv) * m->mfact; + ny = m->wy + oh; + } + else if (i == 1) + nw = m->ww - nw - iv - 2*ov; + i++; + } + + resize(c, nx, ny, nw - (2*c->bw), nh - (2*c->bw), False); + } +} + +static void +dwindle(Monitor *m) +{ + fibonacci(m, 1); +} + +static void +spiral(Monitor *m) +{ + fibonacci(m, 0); +} + +/* + * Default tile layout + gaps + */ + +static void +tile(Monitor *m) +{ + unsigned int i, n; + int mx = 0, my = 0, mh = 0, mw = 0; + int sx = 0, sy = 0, sh = 0, sw = 0; + float mfacts, sfacts; + int mrest, srest; + Client *c; + + + int oh, ov, ih, iv; + getgaps(m, &oh, &ov, &ih, &iv, &n); + + if (n == 0) + return; + + sx = mx = m->wx + ov; + sy = my = m->wy + oh; + mh = m->wh - 2*oh - ih * (MIN(n, m->nmaster) - 1); + sh = m->wh - 2*oh - ih * (n - m->nmaster - 1); + sw = mw = m->ww - 2*ov; + + if (m->nmaster && n > m->nmaster) { + sw = (mw - iv) * (1 - m->mfact); + mw = (mw - iv) * m->mfact; + sx = mx + mw + iv; + } + + getfacts(m, mh, sh, &mfacts, &sfacts, &mrest, &srest); + + for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) + if (i < m->nmaster) { + resize(c, mx, my, mw - (2*c->bw), (mh / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), 0); + my += HEIGHT(c) + ih; + } else { + resize(c, sx, sy, sw - (2*c->bw), (sh / sfacts) + ((i - m->nmaster) < srest ? 1 : 0) - (2*c->bw), 0); + sy += HEIGHT(c) + ih; + } +} -- 2.39.5