diff --git a/.forgejo/workflows/build.yml b/.forgejo/workflows/build.yml deleted file mode 100644 index 3866e31..0000000 --- a/.forgejo/workflows/build.yml +++ /dev/null @@ -1,63 +0,0 @@ -# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json -name: Build/Publish XBPS - -on: - push: - paths: - - "config**" - -jobs: - build-and-publish: - runs-on: docker - container: git.snaile.de/snailed/xbps-builder:2024.0502.1733@sha256:0ca263f8a97fbd29f88349e731153f60569d19ddd7320f2c7c80047a35260c9b - env: - LICENSE: "GPL-2.0" - SHORT_DESCRIPTION: "Customized DWM" - DEPENDENCIES: "pango>=1.44,glibc>=2.32,libX11>=1.2,libXft>=2.3.8,libXinerama>=1.0.3,fontconfig>=2.6.0" - MAINTAINER: "Luca Bilke " - NAME: "dwm-custom" - ARCH: "x86_64" - steps: - - name: Checkout - uses: https://code.forgejo.org/actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4 - with: - submodules: true - - - name: Generate tag and package name - run: | - set -xeu - MAJOR=$(date +%Y) - MINOR=$(date +%m%d) - PATCH=$(date +%H%M) - echo "PACKAGE_NAME=${{ env.NAME }}-${MAJOR}.${MINOR}.${PATCH}_1" >> $GITHUB_ENV - - - name: Build - run: | - set -xeu - make clean install DESTDIR="${GITHUB_WORKSPACE}/pkg" PREFIX="/usr" - - - name: Create package - run: | - set -xeu - export XBPS_TARGET_ARCH=${{ env.ARCH }} - mkdir /target - cd /target || exit 1 - xbps-create -A ${{ env.ARCH }} \ - -H "${{ github.server_url }}/${{ github.repository }}" \ - -l "${{ env.LICENSE }}" \ - -n "${{ env.PACKAGE_NAME }}" \ - -m "${{ env.MAINTAINER }}" \ - -s "${{ env.SHORT_DESCRIPTION }}" \ - -D "${{ env.DEPENDENCIES }}" \ - "${GITHUB_WORKSPACE}/pkg" - - - name: Push Package - run: | - set -xeu - curl -so "/target/${{ env.ARCH }}-repodata" "https://xbps.snaile.de/${{ env.ARCH }}-repodata" - xbps-rindex --add "/target/${{ env.PACKAGE_NAME }}.${{ env.ARCH }}.xbps" - echo '${{ secrets.XBPS_SIGNING_KEY }}' >/tmp/privkey.pem - XBPS_PASSPHRASE=${{ secrets.XBPS_SIGNING_PASSPHRASE }} xbps-rindex --privkey /tmp/privkey.pem --sign-pkg --signedby "${{ env.MAINTAINER }}" "/target/${{ env.PACKAGE_NAME }}.${{ env.ARCH }}.xbps" - rm /tmp/privkey.pem - ls -lAH /target - find /target -type f -exec sh -c 'curl -X PUT --digest -u "${{ vars.XBPS_WEBDAV_USER }}:${{ secrets.XBPS_WEBDAV_KEY }}" -T "${1}" "https://xbps.snaile.de/$(basename $1)"' find-shell {} \; diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 648f5aa..0000000 --- a/.gitignore +++ /dev/null @@ -1,23 +0,0 @@ -dwm-final -dwm-dev-1.0.0_1.x86_64.xbps -pkg/usr -patch -LICENSE -README -config.def.h -config.h -config.mk -drw.c -drw.h -dwm.1 -dwm.c -dwm.c.orig -dwm.desktop -dwm.png -transient.c -util.c -util.h -util.o -drw.o -dwm.o -dwm diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 1b17b6d..0000000 --- a/.gitmodules +++ /dev/null @@ -1,6 +0,0 @@ -[submodule "dwm-flexipatch"] - path = dwm-flexipatch - url = https://github.com/bakkeby/dwm-flexipatch -[submodule "flexipatch-finalizer"] - path = flexipatch-finalizer - url = https://github.com/bakkeby/flexipatch-finalizer diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..995172f --- /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 +© 2020-2022 Chris Down + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/Makefile b/Makefile index 10de8bd..a166452 100644 --- a/Makefile +++ b/Makefile @@ -1,50 +1,57 @@ # dwm - dynamic window manager # See LICENSE file for copyright and license details. -.PHONY: all submodule clean dist install uninstall include config.mk SRC = drw.c dwm.c util.c OBJ = ${SRC:.c=.o} +# FreeBSD users, prefix all ifdef, else and endif statements with a . for this to work (e.g. .ifdef) + +ifdef YAJLLIBS +all: dwm dwm-msg +else all: dwm +endif .c.o: ${CC} -c ${CFLAGS} $< -config.mk: - cp config/config.mk config.mk +${OBJ}: config.h config.mk config.h: - cp config/config.h config.h - -${SRC}: buildroot - -${OBJ}: config.h config.mk + cp config.def.h $@ dwm: ${OBJ} ${CC} -o $@ ${OBJ} ${LDFLAGS} -submodule: - git submodule update --init +ifdef YAJLLIBS +dwm-msg: + ${CC} -o $@ patch/ipc/dwm-msg.c ${LDFLAGS} +endif clean: - find . -maxdepth 1 -type f | grep -Pv "^\./\.|Makefile$$" | xargs -r rm - rm -r tmp patch 2>/dev/null || true - git -C dwm-flexipatch reset --hard HEAD - git -C dwm-flexipatch clean -ffdx + rm -f dwm ${OBJ} dwm-${VERSION}.tar.gz + rm -f dwm-msg -buildroot: submodule - cp config/patches.h dwm-flexipatch/patches.h - flexipatch-finalizer/flexipatch-finalizer.sh -r -d dwm-flexipatch -o tmp - rm -r tmp/Makefile tmp/config.mk patch 2>/dev/null || true - mv tmp/* ./ - for patch in config/patches/*.diff; do patch <"$$patch"; done +dist: clean + mkdir -p dwm-${VERSION} + cp -R LICENSE Makefile README config.def.h config.mk\ + dwm.1 drw.h util.h ${SRC} dwm.png transient.c dwm-${VERSION} + tar -cf dwm-${VERSION}.tar dwm-${VERSION} + gzip dwm-${VERSION}.tar + rm -rf dwm-${VERSION} install: all mkdir -p ${DESTDIR}${PREFIX}/bin cp -f dwm ${DESTDIR}${PREFIX}/bin +ifdef YAJLLIBS + cp -f dwm-msg ${DESTDIR}${PREFIX}/bin +endif chmod 755 ${DESTDIR}${PREFIX}/bin/dwm +ifdef YAJLLIBS + chmod 755 ${DESTDIR}${PREFIX}/bin/dwm-msg +endif mkdir -p ${DESTDIR}${MANPREFIX}/man1 sed "s/VERSION/${VERSION}/g" < dwm.1 > ${DESTDIR}${MANPREFIX}/man1/dwm.1 chmod 644 ${DESTDIR}${MANPREFIX}/man1/dwm.1 @@ -56,3 +63,5 @@ uninstall: rm -f ${DESTDIR}${PREFIX}/bin/dwm\ ${DESTDIR}${MANPREFIX}/man1/dwm.1\ ${DESTDIR}${PREFIX}/share/xsessions/dwm.desktop + +.PHONY: all clean dist install uninstall diff --git a/README b/README new file mode 100644 index 0000000..95d4fd0 --- /dev/null +++ b/README @@ -0,0 +1,48 @@ +dwm - dynamic window manager +============================ +dwm is an extremely fast, small, and dynamic window manager for X. + + +Requirements +------------ +In order to build dwm you need the Xlib header files. + + +Installation +------------ +Edit config.mk to match your local setup (dwm is installed into +the /usr/local namespace by default). + +Afterwards enter the following command to build and install dwm (if +necessary as root): + + make clean install + + +Running dwm +----------- +Add the following line to your .xinitrc to start dwm using startx: + + exec dwm + +In order to connect dwm to a specific display, make sure that +the DISPLAY environment variable is set correctly, e.g.: + + DISPLAY=foo.bar:1 exec dwm + +(This will start dwm on display :1 of the host foo.bar.) + +In order to display status info in the bar, you can do something +like this in your .xinitrc: + + while xsetroot -name "`date` `uptime | sed 's/.*,//'`" + do + sleep 1 + done & + exec dwm + + +Configuration +------------- +The configuration of dwm is done by creating a custom config.h +and (re)compiling the source code. diff --git a/config.def.h b/config.def.h new file mode 100644 index 0000000..e592d6d --- /dev/null +++ b/config.def.h @@ -0,0 +1,341 @@ +/* See LICENSE file for copyright and license details. */ + +/* appearance */ +static const unsigned int borderpx = 1; /* border pixel of windows */ +static const unsigned int snap = 32; /* snap pixel */ +static const int showbar = 1; /* 0 means no bar */ +static const int topbar = 1; /* 0 means bottom bar */ +static const int focusonwheel = 0; +static int floatposgrid_x = 5; /* float grid columns */ +static int floatposgrid_y = 5; /* float grid rows */ +/* Status is to be shown on: -1 (all monitors), 0 (a specific monitor by index), 'A' (active monitor) */ +static const int statusmon = 'A'; + +/* Indicators: see patch/bar_indicators.h for options */ +static int tagindicatortype = INDICATOR_TOP_LEFT_SQUARE; +static int tiledindicatortype = INDICATOR_NONE; +static int floatindicatortype = INDICATOR_TOP_LEFT_SQUARE; +static const char font[] = "monospace 10"; +static const char dmenufont[] = "monospace:size=10"; + +static char c000000[] = "#000000"; // placeholder value + +static char normfgcolor[] = "#bbbbbb"; +static char normbgcolor[] = "#222222"; +static char normbordercolor[] = "#444444"; +static char normfloatcolor[] = "#db8fd9"; + +static char selfgcolor[] = "#eeeeee"; +static char selbgcolor[] = "#005577"; +static char selbordercolor[] = "#005577"; +static char selfloatcolor[] = "#005577"; + +static char titlenormfgcolor[] = "#bbbbbb"; +static char titlenormbgcolor[] = "#222222"; +static char titlenormbordercolor[] = "#444444"; +static char titlenormfloatcolor[] = "#db8fd9"; + +static char titleselfgcolor[] = "#eeeeee"; +static char titleselbgcolor[] = "#005577"; +static char titleselbordercolor[] = "#005577"; +static char titleselfloatcolor[] = "#005577"; + +static char tagsnormfgcolor[] = "#bbbbbb"; +static char tagsnormbgcolor[] = "#222222"; +static char tagsnormbordercolor[] = "#444444"; +static char tagsnormfloatcolor[] = "#db8fd9"; + +static char tagsselfgcolor[] = "#eeeeee"; +static char tagsselbgcolor[] = "#005577"; +static char tagsselbordercolor[] = "#005577"; +static char tagsselfloatcolor[] = "#005577"; + +static char hidnormfgcolor[] = "#005577"; +static char hidselfgcolor[] = "#227799"; +static char hidnormbgcolor[] = "#222222"; +static char hidselbgcolor[] = "#222222"; + +static char urgfgcolor[] = "#bbbbbb"; +static char urgbgcolor[] = "#222222"; +static char urgbordercolor[] = "#ff0000"; +static char urgfloatcolor[] = "#db8fd9"; + +static char scratchselfgcolor[] = "#FFF7D4"; +static char scratchselbgcolor[] = "#77547E"; +static char scratchselbordercolor[] = "#894B9F"; +static char scratchselfloatcolor[] = "#894B9F"; + +static char scratchnormfgcolor[] = "#FFF7D4"; +static char scratchnormbgcolor[] = "#664C67"; +static char scratchnormbordercolor[] = "#77547E"; +static char scratchnormfloatcolor[] = "#77547E"; + +static char *colors[][ColCount] = { + /* fg bg border float */ + [SchemeNorm] = { normfgcolor, normbgcolor, normbordercolor, normfloatcolor }, + [SchemeSel] = { selfgcolor, selbgcolor, selbordercolor, selfloatcolor }, + [SchemeTitleNorm] = { titlenormfgcolor, titlenormbgcolor, titlenormbordercolor, titlenormfloatcolor }, + [SchemeTitleSel] = { titleselfgcolor, titleselbgcolor, titleselbordercolor, titleselfloatcolor }, + [SchemeTagsNorm] = { tagsnormfgcolor, tagsnormbgcolor, tagsnormbordercolor, tagsnormfloatcolor }, + [SchemeTagsSel] = { tagsselfgcolor, tagsselbgcolor, tagsselbordercolor, tagsselfloatcolor }, + [SchemeHidNorm] = { hidnormfgcolor, hidnormbgcolor, c000000, c000000 }, + [SchemeHidSel] = { hidselfgcolor, hidselbgcolor, c000000, c000000 }, + [SchemeUrg] = { urgfgcolor, urgbgcolor, urgbordercolor, urgfloatcolor }, + [SchemeScratchSel] = { scratchselfgcolor, scratchselbgcolor, scratchselbordercolor, scratchselfloatcolor }, + [SchemeScratchNorm] = { scratchnormfgcolor, scratchnormbgcolor, scratchnormbordercolor, scratchnormfloatcolor }, +}; + +static const char *const autostart[] = { + "st", NULL, + NULL /* terminate */ +}; + +static const char *scratchpadcmd[] = {"s", "st", "-n", "spterm", NULL}; + +/* Tags + * In a traditional dwm the number of tags in use can be changed simply by changing the number + * of strings in the tags array. This build does things a bit different which has some added + * benefits. If you need to change the number of tags here then change the NUMTAGS macro in dwm.c. + * + * Examples: + * + * 1) static char *tagicons[][NUMTAGS*2] = { + * [DEFAULT_TAGS] = { "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I" }, + * } + * + * 2) static char *tagicons[][1] = { + * [DEFAULT_TAGS] = { "•" }, + * } + * + * The first example would result in the tags on the first monitor to be 1 through 9, while the + * tags for the second monitor would be named A through I. A third monitor would start again at + * 1 through 9 while the tags on a fourth monitor would also be named A through I. Note the tags + * count of NUMTAGS*2 in the array initialiser which defines how many tag text / icon exists in + * the array. This can be changed to *3 to add separate icons for a third monitor. + * + * For the second example each tag would be represented as a bullet point. Both cases work the + * same from a technical standpoint - the icon index is derived from the tag index and the monitor + * index. If the icon index is is greater than the number of tag icons then it will wrap around + * until it an icon matches. Similarly if there are two tag icons then it would alternate between + * them. This works seamlessly with alternative tags and alttagsdecoration patches. + */ +static char *tagicons[][NUMTAGS] = +{ + [DEFAULT_TAGS] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }, + [ALTERNATIVE_TAGS] = { "A", "B", "C", "D", "E", "F", "G", "H", "I" }, + [ALT_TAGS_DECORATION] = { "<1>", "<2>", "<3>", "<4>", "<5>", "<6>", "<7>", "<8>", "<9>" }, +}; + +/* There are two options when it comes to per-client rules: + * - a typical struct table or + * - using the RULE macro + * + * A traditional struct table looks like this: + * // class instance title wintype tags mask isfloating monitor + * { "Gimp", NULL, NULL, NULL, 1 << 4, 0, -1 }, + * { "Firefox", NULL, NULL, NULL, 1 << 7, 0, -1 }, + * + * The RULE macro has the default values set for each field allowing you to only + * specify the values that are relevant for your rule, e.g. + * + * RULE(.class = "Gimp", .tags = 1 << 4) + * RULE(.class = "Firefox", .tags = 1 << 7) + * + * Refer to the Rule struct definition for the list of available fields depending on + * the patches you enable. + */ +static const Rule rules[] = { + /* xprop(1): + * WM_CLASS(STRING) = instance, class + * WM_NAME(STRING) = title + * WM_WINDOW_ROLE(STRING) = role + * _NET_WM_WINDOW_TYPE(ATOM) = wintype + */ + RULE(.wintype = WTYPE "DIALOG", .isfloating = 1) + RULE(.wintype = WTYPE "UTILITY", .isfloating = 1) + RULE(.wintype = WTYPE "TOOLBAR", .isfloating = 1) + RULE(.wintype = WTYPE "SPLASH", .isfloating = 1) + RULE(.class = "Gimp", .tags = 1 << 4) + RULE(.class = "Firefox", .tags = 1 << 7) + RULE(.instance = "spterm", .scratchkey = 's', .isfloating = 1) +}; + +/* Bar rules allow you to configure what is shown where on the bar, as well as + * introducing your own bar modules. + * + * monitor: + * -1 show on all monitors + * 0 show on monitor 0 + * 'A' show on active monitor (i.e. focused / selected) (or just -1 for active?) + * bar - bar index, 0 is default, 1 is extrabar + * alignment - how the module is aligned compared to other modules + * widthfunc, drawfunc, clickfunc - providing bar module width, draw and click functions + * name - does nothing, intended for visual clue and for logging / debugging + */ +static const BarRule barrules[] = { + /* monitor bar alignment widthfunc drawfunc clickfunc hoverfunc name */ + { -1, 0, BAR_ALIGN_LEFT, width_tags, draw_tags, click_tags, hover_tags, "tags" }, + { -1, 0, BAR_ALIGN_LEFT, width_ltsymbol, draw_ltsymbol, click_ltsymbol, NULL, "layout" }, + { statusmon, 0, BAR_ALIGN_RIGHT, width_status, draw_status, click_statuscmd, NULL, "status" }, + { -1, 0, BAR_ALIGN_NONE, width_wintitle, draw_wintitle, click_wintitle, NULL, "wintitle" }, +}; + +/* layout(s) */ +static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */ +static const int nmaster = 1; /* number of clients in master area */ +static const int resizehints = 0; /* 1 means respect size hints in tiled resizals */ +static const int lockfullscreen = 1; /* 1 will force focus on the fullscreen window */ + +static const Layout layouts[] = { + /* symbol arrange function */ + { "[]=", tile }, /* first entry is default */ + { "><>", NULL }, /* no layout function means floating behavior */ + { "[M]", monocle }, + { "TTT", bstack }, + { "|M|", centeredmaster }, + { "[D]", deck }, + { ":::", gaplessgrid }, +}; + +/* key definitions */ +#define MODKEY Mod1Mask +#define TAGKEYS(KEY,TAG) \ + { MODKEY, KEY, view, {.ui = 1 << TAG} }, \ + { MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \ + { MODKEY|ShiftMask, KEY, tag, {.ui = 1 << TAG} }, \ + { MODKEY|ControlMask|ShiftMask, KEY, toggletag, {.ui = 1 << TAG} }, + +#define STACKKEYS(MOD,ACTION) \ + { MOD, XK_j, ACTION##stack, {.i = INC(+1) } }, \ + { MOD, XK_k, ACTION##stack, {.i = INC(-1) } }, \ + { MOD, XK_s, ACTION##stack, {.i = PREVSEL } }, \ + { MOD, XK_w, ACTION##stack, {.i = 0 } }, \ + { MOD, XK_e, ACTION##stack, {.i = 1 } }, \ + { MOD, XK_a, ACTION##stack, {.i = 2 } }, \ + { MOD, XK_z, 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 *dmenucmd[] = { + "dmenu_run", + "-fn", dmenufont, + "-nb", normbgcolor, + "-nf", normfgcolor, + "-sb", selbgcolor, + "-sf", selfgcolor, + NULL +}; +static const char *termcmd[] = { "st", NULL }; + +/* This defines the name of the executable that handles the bar (used for signalling purposes) */ +#define STATUSBAR "dwmblocks" + +static const Key keys[] = { + /* modifier key function argument */ + { MODKEY, XK_p, spawn, {.v = dmenucmd } }, + { MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } }, + { MODKEY, XK_b, togglebar, {0} }, + STACKKEYS(MODKEY, focus) + STACKKEYS(MODKEY|ShiftMask, push) + { MODKEY, XK_Left, focusdir, {.i = 0 } }, // left + { MODKEY, XK_Right, focusdir, {.i = 1 } }, // right + { MODKEY, XK_Up, focusdir, {.i = 2 } }, // up + { MODKEY, XK_Down, focusdir, {.i = 3 } }, // down + { MODKEY, XK_i, incnmaster, {.i = +1 } }, + { MODKEY, XK_d, incnmaster, {.i = -1 } }, + { MODKEY, XK_h, setmfact, {.f = -0.05} }, + { MODKEY, XK_l, setmfact, {.f = +0.05} }, + { MODKEY, XK_Return, zoom, {0} }, + { MODKEY, XK_Tab, view, {0} }, + { MODKEY|ShiftMask, XK_c, killclient, {0} }, + { MODKEY|ShiftMask, XK_q, quit, {0} }, + { MODKEY|ControlMask|ShiftMask, XK_q, quit, {1} }, + { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, + { MODKEY, XK_f, setlayout, {.v = &layouts[1]} }, + { MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, + { MODKEY, XK_space, setlayout, {0} }, + { MODKEY|ShiftMask, XK_space, togglefloating, {0} }, + { MODKEY, XK_grave, togglescratch, {.v = scratchpadcmd } }, + { MODKEY|ControlMask, XK_grave, setscratch, {.v = scratchpadcmd } }, + { MODKEY|ShiftMask, XK_grave, removescratch, {.v = scratchpadcmd } }, + { MODKEY|Mod4Mask, XK_space, unfloatvisible, {0} }, + { MODKEY|ShiftMask, XK_t, unfloatvisible, {.v = &layouts[0]} }, + { MODKEY, XK_y, togglefullscreen, {0} }, + { MODKEY, XK_0, view, {.ui = ~0 } }, + { MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } }, + { MODKEY, XK_comma, focusmon, {.i = -1 } }, + { MODKEY, XK_period, focusmon, {.i = +1 } }, + { MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } }, + { MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } }, + /* Note that due to key limitations the below example kybindings are defined with a Mod3Mask, + * which is not always readily available. Refer to the patch wiki for more details. */ + /* Client position is limited to monitor window area */ + { Mod3Mask, XK_u, floatpos, {.v = "-26x -26y" } }, // ↖ + { Mod3Mask, XK_i, floatpos, {.v = " 0x -26y" } }, // ↑ + { Mod3Mask, XK_o, floatpos, {.v = " 26x -26y" } }, // ↗ + { Mod3Mask, XK_j, floatpos, {.v = "-26x 0y" } }, // ← + { Mod3Mask, XK_l, floatpos, {.v = " 26x 0y" } }, // → + { Mod3Mask, XK_m, floatpos, {.v = "-26x 26y" } }, // ↙ + { Mod3Mask, XK_comma, floatpos, {.v = " 0x 26y" } }, // ↓ + { Mod3Mask, XK_period, floatpos, {.v = " 26x 26y" } }, // ↘ + /* Absolute positioning (allows moving windows between monitors) */ + { Mod3Mask|ControlMask, XK_u, floatpos, {.v = "-26a -26a" } }, // ↖ + { Mod3Mask|ControlMask, XK_i, floatpos, {.v = " 0a -26a" } }, // ↑ + { Mod3Mask|ControlMask, XK_o, floatpos, {.v = " 26a -26a" } }, // ↗ + { Mod3Mask|ControlMask, XK_j, floatpos, {.v = "-26a 0a" } }, // ← + { Mod3Mask|ControlMask, XK_l, floatpos, {.v = " 26a 0a" } }, // → + { Mod3Mask|ControlMask, XK_m, floatpos, {.v = "-26a 26a" } }, // ↙ + { Mod3Mask|ControlMask, XK_comma, floatpos, {.v = " 0a 26a" } }, // ↓ + { Mod3Mask|ControlMask, XK_period, floatpos, {.v = " 26a 26a" } }, // ↘ + /* Resize client, client center position is fixed which means that client expands in all directions */ + { Mod3Mask|ShiftMask, XK_u, floatpos, {.v = "-26w -26h" } }, // ↖ + { Mod3Mask|ShiftMask, XK_i, floatpos, {.v = " 0w -26h" } }, // ↑ + { Mod3Mask|ShiftMask, XK_o, floatpos, {.v = " 26w -26h" } }, // ↗ + { Mod3Mask|ShiftMask, XK_j, floatpos, {.v = "-26w 0h" } }, // ← + { Mod3Mask|ShiftMask, XK_k, floatpos, {.v = "800W 800H" } }, // · + { Mod3Mask|ShiftMask, XK_l, floatpos, {.v = " 26w 0h" } }, // → + { Mod3Mask|ShiftMask, XK_m, floatpos, {.v = "-26w 26h" } }, // ↙ + { Mod3Mask|ShiftMask, XK_comma, floatpos, {.v = " 0w 26h" } }, // ↓ + { Mod3Mask|ShiftMask, XK_period, floatpos, {.v = " 26w 26h" } }, // ↘ + /* Client is positioned in a floating grid, movement is relative to client's current position */ + { Mod3Mask|Mod1Mask, XK_u, floatpos, {.v = "-1p -1p" } }, // ↖ + { Mod3Mask|Mod1Mask, XK_i, floatpos, {.v = " 0p -1p" } }, // ↑ + { Mod3Mask|Mod1Mask, XK_o, floatpos, {.v = " 1p -1p" } }, // ↗ + { Mod3Mask|Mod1Mask, XK_j, floatpos, {.v = "-1p 0p" } }, // ← + { Mod3Mask|Mod1Mask, XK_k, floatpos, {.v = " 0p 0p" } }, // · + { Mod3Mask|Mod1Mask, XK_l, floatpos, {.v = " 1p 0p" } }, // → + { Mod3Mask|Mod1Mask, XK_m, floatpos, {.v = "-1p 1p" } }, // ↙ + { Mod3Mask|Mod1Mask, XK_comma, floatpos, {.v = " 0p 1p" } }, // ↓ + { Mod3Mask|Mod1Mask, XK_period, floatpos, {.v = " 1p 1p" } }, // ↘ + 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) +}; + +/* button definitions */ +/* click can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */ +static const Button buttons[] = { + /* click event mask button function argument */ + { ClkLtSymbol, 0, Button1, setlayout, {0} }, + { ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} }, + { ClkWinTitle, 0, Button2, zoom, {0} }, + { ClkStatusText, 0, Button1, sigstatusbar, {.i = 1 } }, + { ClkStatusText, 0, Button2, sigstatusbar, {.i = 2 } }, + { ClkStatusText, 0, Button3, sigstatusbar, {.i = 3 } }, + { ClkClientWin, MODKEY, Button1, movemouse, {0} }, + { ClkClientWin, MODKEY, Button2, togglefloating, {0} }, + { ClkClientWin, MODKEY, Button3, resizemouse, {0} }, + { ClkTagBar, 0, Button1, view, {0} }, + { ClkTagBar, 0, Button3, toggleview, {0} }, + { ClkTagBar, MODKEY, Button1, tag, {0} }, + { ClkTagBar, MODKEY, Button3, toggletag, {0} }, +}; + diff --git a/config/config.h b/config.h similarity index 100% rename from config/config.h rename to config.h diff --git a/config/config.mk b/config.mk similarity index 100% rename from config/config.mk rename to config.mk diff --git a/config/patches.h b/config/patches.h deleted file mode 100644 index 4b63e4d..0000000 --- a/config/patches.h +++ /dev/null @@ -1,1499 +0,0 @@ -/* - * This file contains patch control flags. - * - * In principle you should be able to mix and match any patches - * you may want. In cases where patches are logically incompatible - * one patch may take precedence over the other as noted in the - * relevant descriptions. - * - * Although layouts typically come as patches they are differentiated - * here for grouping purposes. - */ - -/** - * Bar modules - */ - -/* Enhanced taskbar that shows the titles of all visible windows in the status bar - * and allows focus / hiding / unhiding of windows by clicking on the status bar. - * Awesomebar takes precedence over fancybar. - * https://dwm.suckless.org/patches/awesomebar/ - */ -#define BAR_AWESOMEBAR_PATCH 0 - -/* This patch depends on statuscmd patch and adds integration with a (patched) - * dwmblocks instance to give a clickable status bar. One must not necessarily - * have to use dwmblocks for this feature, any status updater that has support - * for real-time signals (SIGRTMIN) can be used. - * - * dwmblocks: https://github.com/torrinfail/dwmblocks - * https://dwm.suckless.org/patches/statuscmd/ - */ -#define BAR_DWMBLOCKS_PATCH 1 - -/* Originally the dwmblocks + statuscmd patch used a user defined signal (SIGUSR1) - * for communicating with dwmblocks to indicate update signal and what button was - * pressed. The signalling was later changed to SIGRTMIN instead. - * - * Ultimately this makes dwmblocks instances that were patched with the old patch - * are incompatible with the new dwm patch and vice versa. - * - * This is a compatibility patch that makes dwm use SIGUSR1 instead of SIGRTMIN so - * if button clicks are not working then you may want to try enabling this. - * - * If dwmblocks happen to die like this when clicking on a status - * - * [1] 54355 user-defined signal 1 dwmblocks - * - * then it suggests that dwmblocks does not support user defined signals and this - * patch should be left disabled. - * - * Patch: https://gist.github.com/danbyl/54f7c1d57fc6507242a95b71c3d8fdea - * https://dwm.suckless.org/patches/statuscmd/ - */ -#define BAR_DWMBLOCKS_SIGUSR1_PATCH 0 - -/* This patch shows the titles of all visible windows in the status bar - * (as opposed to showing only the selected one). - * Awesomebar takes precedence over fancybar. Fancybar takes precedence over - * the centeredwindowname patch. - * https://dwm.suckless.org/patches/fancybar/ - */ -#define BAR_FANCYBAR_PATCH 0 - -/* Being an evolution of the bartabgroups patch the flexwintitle patch specifically - * taps into the many layout options that flextile-deluxe offers to produce a window - * title section in the bar that is representative of what is shown on screen. - */ -#define BAR_FLEXWINTITLE_PATCH 0 - -/* This patch adds a context menu for layout switching. - * - xmenu needs to be installed. - * - Edit layoutmenu.sh with the installed layouts and with correct indexes. - * - Place layoutmenu.sh in PATH. - * - The text of the menu items is for display only. Name them however you want. - * https://dwm.suckless.org/patches/layoutmenu/ - */ -#define BAR_LAYOUTMENU_PATCH 0 - -/* Show layout symbol in bar */ -#define BAR_LTSYMBOL_PATCH 1 - -/* Adds powerline arrows for the status. - * This uses statuscolors logic for choosing colors for the powerline. As these markers - * are also control characters there is no explicit statuscmd support for this patch. - * - * Powerline separators are defined as: - * |\xXX (creates a hard edge) - * <\xXX (creates a less than arrow) - * /\xXX (creates a diagonal line) - * - * Examples: - * xsetroot -name "$(echo -e '<\x01a<\x02b<\x03c')" - * xsetroot -name "$(echo -e '/\x01d/\x02e/\x03f')" - * - * https://gitlab.com/udiboy1209-suckless/dwm/-/commit/071f5063e8ac4280666828179f92788d893eea40#4b1a539194be7467cefbda22f675a3b7c19ceca7 - * https://dwm.suckless.org/patches/statuscolors/ - */ -#define BAR_POWERLINE_STATUS_PATCH 0 - -/* Adds powerline arrows for the tags. - * https://gitlab.com/udiboy1209-suckless/dwm/-/commit/071f5063e8ac4280666828179f92788d893eea40#4b1a539194be7467cefbda22f675a3b7c19ceca7 - */ -#define BAR_POWERLINE_TAGS_PATCH 0 - -/* Alters the tags powerline to use forward slash instead of arrows */ -#define BAR_POWERLINE_TAGS_SLASH_PATCH 0 - -/* This patch turns the titlebar area into a mfact-respecting tabbar showing each client's title. - * https://dwm.suckless.org/patches/bartabgroups/ - */ -#define BAR_TABGROUPS_PATCH 0 - -/* This patch adds an option to place tags in rows like in many other window managers. - * https://dwm.suckless.org/patches/taggrid/ - */ -#define BAR_TAGGRID_PATCH 0 - -/* Hover tag icons to see a preview of the windows on that tag. - * - * The patch depends on Imlib2 for icon scaling. - * You need to uncomment the corresponding line in config.mk to use the -lImlib2 library - * - * Arch Linux: - * sudo pacman -S imlib2 - * Debian: - * sudo apt install libimlib2-dev - * - * As with the winicon patch you may want to consider adding the compiler flags of -O3 and - * -march=native to enable auto loop vectorize for better performance. - * - * https://dwm.suckless.org/patches/tag-previews/ - */ -#define BAR_TAGPREVIEW_PATCH 0 - -/* Show status in bar */ -#define BAR_STATUS_PATCH 1 - -/* This patch adds a clickable button to the left hand side of the statusbar. - * https://dwm.suckless.org/patches/statusbutton/ - */ -#define BAR_STATUSBUTTON_PATCH 0 - -/* This patch adds the ability to execute shell commands based on the mouse button and position - * when clicking the status bar. Refer to the website for usage. - * https://dwm.suckless.org/patches/statuscmd/ - */ -#define BAR_STATUSCMD_PATCH 1 - -/* Status2d allows colors and rectangle drawing in your dwm status bar. - * This patch is incompatible with the statuscolors patch which takes precedence. - * This patch is incompatible with the extrabar patch. - * https://dwm.suckless.org/patches/status2d/ - */ -#define BAR_STATUS2D_PATCH 0 - -/* Supplementary patch should you want to disable alpha for the status2d section */ -#define BAR_STATUS2D_NO_ALPHA_PATCH 0 - -/* Addition to the status2d patch that allows the use of terminal colors (color0 through color15) - * from xrdb in the status, allowing programs like pywal to change statusbar colors. - * This adds the C and B codes to use terminal foreground and background colors respectively. - * E.g. ^B5^ would use color5 as the background color. - * https://dwm.suckless.org/patches/status2d/ - */ -#define BAR_STATUS2D_XRDB_TERMCOLORS_PATCH 0 - -/* The systray patch adds systray for the status bar. - * https://dwm.suckless.org/patches/systray/ - */ -#define BAR_SYSTRAY_PATCH 0 - -/* Show tag symbols in the bar. */ -#define BAR_TAGS_PATCH 1 - -/* Show tag symbols + class of master window in the bar. - * https://dwm.suckless.org/patches/taglabels/ - */ -#define BAR_TAGLABELS_PATCH 0 - -/* This patch underlines the selected tag, or optionally all tags. - * https://dwm.suckless.org/patches/underlinetags/ - */ -#define BAR_UNDERLINETAGS_PATCH 0 - -/* This patch adds the window icon next to the window title in the bar. - * - * The patch depends on Imlib2 for icon scaling. - * You need to uncomment the corresponding line in config.mk to use the -lImlib2 library - * - * Arch Linux: - * sudo pacman -S imlib2 - * Debian: - * sudo apt install libimlib2-dev - * - * The author recommends adding the compiler flags of -O3 and -march=native to enable auto loop - * vectorize for better performance. - * - * https://github.com/AdamYuan/dwm-winicon - * https://dwm.suckless.org/patches/winicon - */ -#define BAR_WINICON_PATCH 0 - -/* Show window title in bar */ -#define BAR_WINTITLE_PATCH 1 - -/* Shows window titles in the bar, but only for floating clients. - * This depends on code from the flexwintitle patch. - * Note that the configuration in config.def.h for this is merely an example. If combined - * with the corresponding hidden patch then these two will overlap unless the width of the - * modules are controlled. - */ -#define BAR_WINTITLE_FLOATING_PATCH 0 - -/* Shows window titles in the bar, but only for floating clients. - * This depends on code from the flexwintitle patch. - * Note that the configuration in config.def.h for this is merely an example. If combined - * with the corresponding floating patch then these two will overlap unless the width of the - * modules are controlled. - */ -#define BAR_WINTITLE_HIDDEN_PATCH 0 - -/* Title bar modules such as wintitle (default), fancybar and awesomebar - * do not by default add left and/or right padding as they take up the - * remaining space. These options allow you explicitly add padding should - * you need it. - */ -#define BAR_TITLE_RIGHT_PAD_PATCH 0 -#define BAR_TITLE_LEFT_PAD_PATCH 0 - -/** - * Bar options - */ - -/* This patch changes the rectangle indicating if a tag is used by a client into a bar - * above the tag name for better visibility. - * Set the tagindicatortype variable in config.h to INDICATOR_TOP_BAR to enable this. - * https://dwm.suckless.org/patches/activetagindicatorbar/ - */ -#define BAR_ACTIVETAGINDICATORBAR_PATCH N/A - -/* Alternative patch to the activetagindicatorbar patch, adds the bar below the tag - * icon rather than above. - * Set the tagindicatortype variable in config.h to INDICATOR_BOTTOM_BAR to enable this. - */ -#define BAR_ACTIVETAGINDICATORBAR_ALT1_PATCH N/A - -/* The alpha patch adds transparency for the status bar. - * You need to uncomment the corresponding line in config.mk to use the -lXrender library - * when including this patch. - * https://dwm.suckless.org/patches/alpha/ - */ -#define BAR_ALPHA_PATCH 0 - -/* This patch introduces alternative tags which can be switched on the fly for the - * sole purpose of providing visual aid. - * https://dwm.suckless.org/patches/alternativetags/ - */ -#define BAR_ALTERNATIVE_TAGS_PATCH 0 - -/* This patches provides the ability to use alternative text for tags which contain at - * least one window. - * https://dwm.suckless.org/patches/alttagsdecoration/ - */ -#define BAR_ALTTAGSDECORATION_PATCH 0 - -/* This patch enables dwm to manage external status bars such as lemonbar and polybar. - * dwm treats the external bar as it would its own, so all regular dwm commands such as - * togglebar affect the external bar in the same way. - * - * NB: Unless you want both anybar + dwm bar(s) then the recommendation is to disable all - * bar modules and have { -2 } in the barrules. - * - * https://dwm.suckless.org/patches/anybar/ - */ -#define BAR_ANYBAR_PATCH 0 - -/* Anybar option to place the next bar depending on previous bar's position (top or bottom) */ -#define BAR_ANYBAR_TOP_AND_BOTTOM_BARS_PATCH 0 - -/* Anybar option to let dwm manage the width of the bar */ -#define BAR_ANYBAR_MANAGE_WIDTH_PATCH 0 - -/* This patch adds a border around the status bar(s) just like the border of client windows. - * https://codemadness.org/paste/dwm-border-bar.patch - */ -#define BAR_BORDER_PATCH 0 - -/* This patch centers the WM_NAME of the currently selected window on the status bar. - * This is compatible with the wintitle, bartabgroups, flexwintitle and awesomebar bar - * modules. - * https://dwm.suckless.org/patches/centeredwindowname/ - */ -#define BAR_CENTEREDWINDOWNAME_PATCH 0 - -/* Draws a dot indicator overlayed on each tag icon for each client. The selected client - * is drawn as a larger horizontal line. - * Set the tagindicatortype variable in config.h to INDICATOR_CLIENT_DOTS to enable this. - * https://dwm.suckless.org/patches/clientindicators/ - */ -#define BAR_CLIENTINDICATOR_PATCH N/A - -/* Updates the position of dmenu to match that of the bar. I.e. if topbar is 0 then dmenu - * will appear at the bottom and if 1 then dmenu will appear at the top. - * https://dwm.suckless.org/patches/dmenumatchtop - */ -#define BAR_DMENUMATCHTOP_PATCH 0 - -/* Originally this was the extrabar patch, but as the handling of extra bars is now built-in - * only the splitting of the status by a designated separator remains. As such this has been - * renamed to more accurately reflect what it does - creating an extra status. - * https://dwm.suckless.org/patches/extrabar/ - */ -#define BAR_EXTRASTATUS_PATCH 0 - -/* Adds EWMH support for _NET_NUMBER_OF_DESKTOPS, _NET_CURRENT_DESKTOP, _NET_DESKTOP_NAMES - * and _NET_DESKTOP_VIEWPORT, which allows for compatibility with other bars and programs - * that request workspace information. For example polybar's xworkspaces module. - * - * This patch also includes support for adding the _IS_FLOATING property for floating windows - * allowing for compositors to treat floating windows differently to tiled windows. - * - * E.g. this setting makes picom only render shadows for floating windows: - * - * shadow-exclude = [ "! _IS_FLOATING@:32c = 1" ]; - * - * https://github.com/bakkeby/dwm-flexipatch/issues/50 (_IS_FLOATING patch) - * https://dwm.suckless.org/patches/ewmhtags/ - */ -#define BAR_EWMHTAGS_PATCH 1 - -/* Allows the bar height to be explicitly set rather than being derived from font. - * https://dwm.suckless.org/patches/bar_height/ - */ -#define BAR_HEIGHT_PATCH 0 - -/* This patch prevents dwm from drawing tags with no clients (i.e. vacant) on the bar. - * https://dwm.suckless.org/patches/hide_vacant_tags/ - */ -#define BAR_HIDEVACANTTAGS_PATCH 1 - -/* With this patch dwm's built-in status bar is only shown when HOLDKEY is pressed - * and the bar will now overlay the display. - * http://dwm.suckless.org/patches/holdbar/ - */ -#define BAR_HOLDBAR_PATCH 0 - -/* Sometimes dwm crashes when it cannot render some glyphs in window titles (usually emoji). - * This patch is essentially a hack to ignore any errors when drawing text on the status bar. - * https://groups.google.com/forum/m/#!topic/wmii/7bncCahYIww - * https://docs.google.com/viewer?a=v&pid=forums&srcid=MDAwODA2MTg0MDQyMjE0OTgzMzMBMDQ3ODQzODkyMTU3NTAyMTMxNTYBX2RUMVNtOUtDQUFKATAuMQEBdjI&authuser=0 - */ -#define BAR_IGNORE_XFT_ERRORS_WHEN_DRAWING_TEXT_PATCH 1 - -/* This patch adds back in the workaround for a BadLength error in the Xft library when color - * glyphs are used. This is for systems that do not have an updated version of the Xft library - * (or generally prefer monochrome fonts). - */ -#define BAR_NO_COLOR_EMOJI_PATCH 0 - -/* This patch adds vertical and horizontal space between the statusbar and the edge of the screen. - * https://dwm.suckless.org/patches/barpadding/ - */ -#define BAR_PADDING_PATCH 0 - -/* Same as barpadding patch but specifically tailored for the vanitygaps patch in that the outer - * bar padding is derived from the vanitygaps settings. In addition to this the bar padding is - * toggled in unison when vanitygaps are toggled. Increasing or decreasing gaps during runtime - * will not affect the bar padding. - */ -#define BAR_PADDING_VANITYGAPS_PATCH 0 - -/* This patch adds simple markup for status messages using pango markup. - * This depends on the pango library v1.44 or greater. - * You need to uncomment the corresponding lines in config.mk to use the pango libraries - * when including this patch. - * - * Note that the pango patch does not protect against the BadLength error from Xft - * when color glyphs are used, which means that dwm will crash if color emoji is used. - * - * If you need color emoji then you may want to install this patched library from the AUR: - * https://aur.archlinux.org/packages/libxft-bgra/ - * - * A long term fix for the libXft library is pending approval of this pull request: - * https://gitlab.freedesktop.org/xorg/lib/libxft/-/merge_requests/1 - * - * Also see: - * https://developer.gnome.org/pygtk/stable/pango-markup-language.html - * https://lists.suckless.org/hackers/2004/17285.html - * https://dwm.suckless.org/patches/pango/ - */ -#define BAR_PANGO_PATCH 1 - -/* This patch allows the status text to be fixed to the bar on a specific - * monitor rather than being drawn on the focused monitor. - * The statusallmons patch takes precedence over this patch. - * https://dwm.suckless.org/patches/staticstatus/ - */ -#define BAR_STATICSTATUS_PATCH 0 - -/* This patch draws and updates the statusbar on all monitors. - * https://dwm.suckless.org/patches/statusallmons/ - */ -#define BAR_STATUSALLMONS_PATCH 0 - -/* This patch enables colored text in the status bar. It changes the way colors are defined - * in config.h allowing multiple color combinations for use in the status script. - * This patch is incompatible with and takes precedence over the status2d patch. - * - * This patch is compatible with the statuscmd patch with the caveat that the first 16 markers - * are reserved for status colors restricting block signals to 17 through 31. - * - * https://dwm.suckless.org/patches/statuscolors/ - */ -#define BAR_STATUSCOLORS_PATCH 0 - -/* This patch adds configuration options for horizontal and vertical padding in the status bar. - * https://dwm.suckless.org/patches/statuspadding/ - */ -#define BAR_STATUSPADDING_PATCH 0 - -/* This patch adds the ability for dwm to read colors from the linux virtual console. - * /sys/module/vt/parameters/default_{red,grn,blu} - * Essentially this way the colors you use in your regular tty is "mirrored" to dwm. - * https://dwm.suckless.org/patches/vtcolors/ - */ -#define BAR_VTCOLORS_PATCH 0 - -/* This patch allows client windows to be hidden. This code was originally part of awesomebar, - * but has been separated out so that other bar modules can take advantage of it. - * Both awesomebar and bartabgroups patches depend on this patch and it will be auto-enabled - * during compile time if it is needed. Note that if using flexipatch-finalizer this must be - * explicitly enabled. - * https://github.com/bakkeby/patches/blob/master/dwm/dwm-barmodules-wintitleactions-6.2.diff - */ -#define BAR_WINTITLEACTIONS_PATCH BAR_AWESOMEBAR_PATCH || BAR_TABGROUPS_PATCH || BAR_FLEXWINTITLE_PATCH - -/*** - * Other patches - */ - -/* Adds a window task switcher toggled using alt-tab. - * https://dwm.suckless.org/patches/alt-tab/ - */ -#define ALT_TAB_PATCH 0 - -/* All floating windows are centered, like the center patch, but without a rule. - * The center patch takes precedence over this patch. - * This patch interferes with the center transient windows patches. - * https://dwm.suckless.org/patches/alwayscenter/ - */ -#define ALWAYSCENTER_PATCH 0 - -/* This patch allows windows to be resized with its aspect ratio remaining constant. - * https://dwm.suckless.org/patches/aspectresize/ - */ -#define ASPECTRESIZE_PATCH 0 - -/* This patch adds new clients above the selected client, instead of always - * becoming the new master. This behaviour is known from Xmonad. - * This patch takes precedence over ATTACHASIDE_PATCH. - * https://dwm.suckless.org/patches/attachabove/ - */ -#define ATTACHABOVE_PATCH 0 - -/* This patch adds new clients on top of the stack. - * This patch takes precedence over ATTACHBELOW_PATCH. - * https://dwm.suckless.org/patches/attachaside/ - */ -#define ATTACHASIDE_PATCH 0 - -/* This patch adds new clients below the selected client. - * This patch takes precedence over ATTACHBOTTOM_PATCH. - * https://dwm.suckless.org/patches/attachbelow/ - */ -#define ATTACHBELOW_PATCH 0 - -/* This patch adds new clients at the bottom of the stack. - * https://dwm.suckless.org/patches/attachbottom/ - */ -#define ATTACHBOTTOM_PATCH 1 - -/* This patch will make dwm run "~/.local/share/dwm/autostart_blocking.sh" and - * "~/.local/share/dwm/autostart.sh &" before entering the handler loop. One or - * both of these files can be ommited. Note the path inside .local/share rather - * than the original ~/.dwm folder. - * https://dwm.suckless.org/patches/autostart/ - */ -#define AUTOSTART_PATCH 0 - -/* By default, windows that are not visible when requesting a resize/move will not - * get resized/moved. With this patch, they will. - * https://dwm.suckless.org/patches/autoresize/ - */ -#define AUTORESIZE_PATCH 0 - -/* This patch adds proper support for Right-To-Left languages. (such as Farsi, Arabic or Hebrew). - * - * You need to uncomment the corresponding lines in config.mk to use the -lfribidi library - * when including this patch. - * - * This patch depends on the following additional library: - * - fribidi - * - * https://dwm.suckless.org/patches/bidi/ - */ -#define BIDI_PATCH 0 - -/* This patch adds an iscentered rule to automatically center clients on the current monitor. - * This patch takes precedence over centeredwindowname, alwayscenter and fancybar patches. - * https://dwm.suckless.org/patches/center/ - */ -#define CENTER_PATCH 1 - -/* A transient window is one that is meant to be short lived and is usually raised by a - * parent window. Such windows are typically dialog boxes and the like. - * It should be noted that in dwm transient windows are not subject to normal client rules - * and they are always floating by default. - * This patch centers transient windows on the screen like the center patch does. Note that - * the 6.2 center patch piggy-backed on the updatewindowtype function to ensure that all - * dialog boxes were centered, transient or not. This function was removed in relation to - * adding wintype as a client rule filter, hence this no longer works out of the box. This - * patch restores previous behaviour with the center patch. - */ -#define CENTER_TRANSIENT_WINDOWS_PATCH 0 - -/* As above, except that the transient window is centered within the position of the parent - * window, rather than at the center of the screen. This takes precedence over the above patch. - */ -#define CENTER_TRANSIENT_WINDOWS_BY_PARENT_PATCH 1 - -/* This patch provides the ability to assign different weights to clients in their - * respective stack in tiled layout. - * https://dwm.suckless.org/patches/cfacts/ - */ -#define CFACTS_PATCH 0 - -/* This patch allows color attributes to be set through the command line. - * https://dwm.suckless.org/patches/cmdcustomize/ - */ -#define CMDCUSTOMIZE_PATCH 0 - -/* This patch tweaks the tagging interface so that you can select multiple tags for tag - * or view by pressing all the right keys as a combo. For example to view tags 1 and 3, - * hold MOD and then press and hold 1 and 3 together. - * https://dwm.suckless.org/patches/combo/ - */ -#define COMBO_PATCH 0 - -/* Allow dwm to execute commands from autostart array in your config.h file. When dwm exits - * then all processes from autostart array will be killed. - * https://dwm.suckless.org/patches/cool_autostart/ - */ -#define COOL_AUTOSTART_PATCH 1 - -/* The cyclelayouts patch lets you cycle through all your layouts. - * https://dwm.suckless.org/patches/cyclelayouts/ - */ -#define CYCLELAYOUTS_PATCH 0 - -/* Make dwm respect _MOTIF_WM_HINTS property, and not draw borders around windows requesting - * for it. Some applications use this property to notify window managers to not draw window - * decorations. - * Not respecting this property leads to issues with applications that draw their own borders, - * like chromium (with "Use system title bar and borders" turned off) or vlc in fullscreen mode. - * https://dwm.suckless.org/patches/decoration_hints/ - */ -#define DECORATION_HINTS_PATCH 0 - -/* This feature distributes all clients on the current monitor evenly across all tags. - * It is a variant of the reorganizetags patch. - * https://dwm.suckless.org/patches/reorganizetags/ - */ -#define DISTRIBUTETAGS_PATCH 0 - -/* By default dwm will terminate on color allocation failure and the behaviour is intended to - * catch and inform the user of color configuration issues. - * - * Some patches like status2d and xresources / xrdb can change colours during runtime, which - * means that if a color can't be allocated at this time then the window manager will abruptly - * terminate. - * - * This patch will ignore color allocation failures and continue on as normal. The effect of - * this is that the existing color, that was supposed to be replaced, will remain as-is. - */ -#define DO_NOT_DIE_ON_COLOR_ALLOCATION_FAILURE_PATCH 0 - -/* Similarly to the dragmfact patch this allows you to click and drag clients to change the - * cfact to adjust the client's size in the stack. This patch depends on the cfacts patch. - */ -#define DRAGCFACT_PATCH 0 - -/* This patch lets you resize the split in the tile layout (i.e. modify mfact) by holding - * the modkey and dragging the mouse. - * This patch can be a bit wonky with other layouts, but generally works. - * https://dwm.suckless.org/patches/dragmfact/ - */ -#define DRAGMFACT_PATCH 0 - -/* Simple dwmc client using a fork of fsignal to communicate with dwm. - * To use this either copy the patch/dwmc shell script to somewhere in your path or - * uncomment the following line in Makefile: - * #cp -f patch/dwmc ${DESTDIR}${PREFIX}/bin - * http://dwm.suckless.org/patches/dwmc/ - */ -#define DWMC_PATCH 0 - -/* This patch allows no tag at all to be selected. The result is that dwm will start with - * no tag selected and when you start a client with no tag rule and no tag selected then - * it will be opened on the first tag. - * https://dwm.suckless.org/patches/emptyview/ - */ -#define EMPTYVIEW_PATCH 0 - -/* This patch allows the user to change size and placement of floating windows using only the - * keyboard. It also allows for temporary vertical and horizontal extension of windows similar - * to other WMs fill command. - * https://dwm.suckless.org/patches/exresize/ - */ -#define EXRESIZE_PATCH 0 - -/* Only allow clients to "fullscreen" into the space currently given to them. - * As an example, this will allow you to view a fullscreen video in your browser on - * one half of the screen, while having the other half available for other tasks. - * This patch takes precedence over the fakefullscreen client patch below. - * https://dwm.suckless.org/patches/fakefullscreen/ - */ -#define FAKEFULLSCREEN_PATCH 0 - -/* Similarly to the fakefullscreen patch this patch only allows clients to "fullscreen" into - * the space currently given to them. - * The "twist" with this patch is that fake fullscreen can be toggled on a per client basis - * rather than applying to all clients globally. - * Also see the selectivefakefullscreen option that adds a rule option to enabled this on client - * startup. - */ -#define FAKEFULLSCREEN_CLIENT_PATCH 0 - -/* This patch adds a float rule allowing the size and position of floating windows to be specified - * It also allows the size and position of floating windows to be controlled similar to the - * exresize, moveresize, and moveplace patches. - * The size and position can be specified using absolute, relative or fixed co-ordinates and - * https://github.com/bakkeby/patches/wiki/floatpos/ - */ -#define FLOATPOS_PATCH 1 - -/* Add-on functionality for the above: make the float positions respect outer (vanity)gaps. */ -#define FLOATPOS_RESPECT_GAPS_PATCH 0 - -/* This patch provides the ability to focus the tag on the immediate left or right of the - * currently focused tag. It also allows to send the focused window either on the left or - * the right tag. - * http://dwm.suckless.org/patches/focusadjacenttag/ - */ -#define FOCUSADJACENTTAG_PATCH 0 - -/* Allows focusing on clients based on direction (up, down, left, right) instead of client order. - * https://github.com/bakkeby/patches/wiki/focusdir/ - */ -#define FOCUSDIR_PATCH 1 - -/* When changing tags, closing windows or moving clients out of view then focus will revert to the - * client window that remains under the mouse cursor rather than the most recently focused window. - * https://github.com/bakkeby/patches/wiki/focusfollowmouse - */ -#define FOCUSFOLLOWMOUSE_PATCH 0 - -/* A simple patch that just puts focus back to the master client. - * https://dwm.suckless.org/patches/focusmaster/ - */ -#define FOCUSMASTER_PATCH 0 - -/* A variant of the focusmaster patch that additionally allows the focus to be returned to the - * previously focused client - * https://dwm.suckless.org/patches/focusmaster/ - */ -#define FOCUSMASTER_RETURN_PATCH 0 - -/* Switch focus only by mouse click and not sloppy (focus follows mouse pointer). - * https://dwm.suckless.org/patches/focusonclick/ - */ -#define FOCUSONCLICK_PATCH 1 - -/* Selects the next window having the urgent flag regardless of the tag it is on. - * The urgent flag can be artificially set with the following xdotool command on any window: - * xdotool selectwindow -- set_window --urgency 1 - * https://dwm.suckless.org/patches/focusurgent/ - */ -#define FOCUSURGENT_PATCH 0 - -/* By default, dwm responds to _NET_ACTIVE_WINDOW client messages by setting - * the urgency bit on the named window. This patch activates the window instead. - * https://dwm.suckless.org/patches/focusonnetactive/ - */ -#define FOCUSONNETACTIVE_PATCH 0 - -/* Send "fake signals" to dwm for handling, using xsetroot. This will not conflict with the - * status bar, which also is managed using xsetroot. - * Also see the dwmc patch, which takes precedence over this patch. - * https://dwm.suckless.org/patches/fsignal/ - */ -#define FSIGNAL_PATCH 0 - -/* Applies the monocle layout with the focused client on top and hides the bar. When pressed - * again it shows the bar and restores the layout that was active before going fullscreen. - * https://dwm.suckless.org/patches/fullscreen/ - */ -#define FULLSCREEN_PATCH 0 - -/* This patch provides a keybinding to rotate all clients in the currently selected - * area (master or stack) without affecting the other area. - * https://dwm.suckless.org/patches/inplacerotate/ - */ -#define INPLACEROTATE_PATCH 0 - -/* This patch lets you define custom insets from each edge of the screen. One use case would be - * to arrange space for an external bar. - * https://dwm.suckless.org/patches/insets/ - */ -#define INSETS_PATCH 0 - -/* This patch (v1.5.7) implements inter-process communication through a UNIX socket for dwm. This - * allows for the window manager to be queried for information, e.g. listen for events such as tag - * or layout changes, as well as send commands to control the window manager via other programs. - * - * You need to uncomment the corresponding lines in config.mk to use the -lyajl library - * when including this patch. - * This patch depends on the following additional library: - * - yajl - * - * https://github.com/mihirlad55/dwm-ipc - * https://dwm.suckless.org/patches/ipc/ - */ -#define IPC_PATCH 0 - -/* Adds rule option for clients to avoid accidental termination by killclient for sticky windows. - * https://dwm.suckless.org/patches/ispermanent/ - */ -#define ISPERMANENT_PATCH 0 - -/* This patch adds key modes (like in vim or emacs) where chains of keyboard shortcuts - * can be performed. - * https://dwm.suckless.org/patches/keymodes/ - */ -#define KEYMODES_PATCH 0 - -/* This patch adds a keybinding to kills all visible clients that are not selected. - * https://dwm.suckless.org/patches/killunsel/ - */ -#define KILLUNSEL_PATCH 0 - -/* This changes the window manager name to LG3d instead of dwm as a workaround for Java - * applications that assume that the window manager is using window reparenting. - * Refer to the ISSUES secton of the dwm man page for more details. - */ -#define LG3D_PATCH 0 - -/* By default in dwm it is possible to make an application fullscreen, then use - * the focusstack keybindings to focus on other windows beneath the current window. - * It is also possible to spawn new windows (e.g. a terminal) that end up getting - * focus while the previous window remains in fullscreen. This patch ensures that - * in such scenarios the previous window loses fullscreen. - * https://github.com/bakkeby/patches/blob/master/dwm/dwm-losefullscreen-6.2.diff - */ -#define LOSEFULLSCREEN_PATCH 1 - -/* This patch adds helper functions for maximizing, horizontally and vertically, floating - * windows using keybindings. - * https://dwm.suckless.org/patches/maximize/ - */ -#define MAXIMIZE_PATCH 0 - -/* Control Music Player Daemon via keybinds. - * You need to uncomment the corresponding line in config.mk to use the -lmpdclient library - * when including this patch. - * This patch depends on the following additional library: - * - libmpdclient - * https://dwm.suckless.org/patches/mpdcontrol/ - */ -#define MPDCONTROL_PATCH 0 - -/* Adds rules per monitor, e.g. have default layouts per monitor. - * The use case for this is if the second monitor is vertical (i.e. rotated) then - * you may want to use a different default layout for this monitor than what is - * used for the main monitor. E.g. normal vertical split for main monitor and - * horizontal split for the second. - */ -#define MONITOR_RULES_PATCH 0 - -/* Always display the the monocle-symbol as defined in config.h if the monocle-layout - * is activated. Do not display the number of open clients in the current tag. - * https://dwm.suckless.org/patches/monoclesymbol/ - */ -#define MONOCLESYMBOL_PATCH 0 - -/* Makes a window floating and 1/3rd the height and 1/3rd the width of the screen and is - * positioned in either the center or one of the 8 cardinal directions depending on which - * key is pressed. - * https://dwm.suckless.org/patches/moveplace/ - */ -#define MOVEPLACE_PATCH 0 - -/* This patch allows you to move and resize dwm's clients using keyboard bindings. - * https://dwm.suckless.org/patches/moveresize/ - */ -#define MOVERESIZE_PATCH 0 - -/* This patch allows you to move clients around in the stack and swap them with the master. - * https://dwm.suckless.org/patches/movestack/ - */ -#define MOVESTACK_PATCH 0 - -/* This patch allows you to change the names of tags during runtime. - * - * This is a bespoke version implemented specifically in relation to tagicons, which is integrated - * into dwm-flexipatch. By default it uses dmenu to retrieve the new name, but this can be - * customised via config along with the maximum text length and the format string. - * - * Special behaviour: - * - if more than one tag is selected then the name change applies to all selected tags - * - if tagicons is configured to have unique tags per monitor then the change only applies - * for the current monitor - * - the name change applies to the tag set that is active for the current tag: - * * if used in combination with BAR_ALTTAGSDECORATION_PATCH and there are clients on the - * given tag then the name change only applies to the ALT_TAGS_DECORATION tag set - * * if used in combination with the BAR_ALTERNATIVE_TAGS_PATCH and alternative tags are - * shown then the name change only applies to the ALTERNATIVE_TAGS tag set - * * if used in combination with both then BAR_ALTTAGSDECORATION_PATCH takes precedence - * * otherwise the name change applies to the DEFAULT_TAGS tag set - * - * https://dwm.suckless.org/patches/nametag/ - */ -#define NAMETAG_PATCH 0 - -/* Variant of the above which prepends the tag number to the given string. - * The toggle does nothing on its own and need to be enabled in combination with the above. */ -#define NAMETAG_PREPEND_PATCH 0 - -/* Adds support for the _NET_CLIENT_LIST_STACKING atom, needed by certain applications like the - * Zoom video conferencing application. - * https://github.com/bakkeby/patches/wiki/netclientliststacking/ - */ -#define NET_CLIENT_LIST_STACKING_PATCH 1 - -/* Removes the border when there is only one window visible. - * https://dwm.suckless.org/patches/noborder/ - */ -#define NOBORDER_PATCH 0 - -/* Enable modifying or removing dmenu in config.def.h which resulted previously in a - * compilation error because two lines of code hardcode dmenu into dwm. - * https://dwm.suckless.org/patches/nodmenu/ - */ -#define NODMENU_PATCH 1 - -/* This patch allows for toggleable client button bindings that have no modifiers. - * This can, for example, allow you to move or resize using the mouse alone without holding - * down a modifier key. This can be practical if you have extra buttons on your mouse. - * While you can use button bindings with no modifiers without this patch in a bare dwm, - * those buttons are then unavailable for use within the application itself so being able to - * toggle these on and off can be necessary in certain situations (e.g. being able to use - * back and forward buttons in a browser). - - * Example bindings: - * { ClkClientWin, 0, Button8, movemouse, {0} }, - * { ClkClientWin, 0, Button9, resizemouse, {0} }, - */ -#define NO_MOD_BUTTONS_PATCH 0 - -/* When terminals have transparency then their borders also become transparent. - * This patch ensures that borders have no transparency. Note that this patch is - * only relevant if you are not using the alpha patch. - * https://github.com/szatanjl/dwm/commit/1529909466206016f2101457bbf37c67195714c8 - * https://dwm.suckless.org/patches/alpha/dwm-fixborders-6.2.diff - */ -#define NO_TRANSPARENT_BORDERS_PATCH 1 - -/* Port of InstantWM's on_empty_keys functionality allowing keybindings that apply only when - * a tag is empty. An example use case is being able to launch applications with first hand - * keys like "f" to launch firefox. - * - * https://github.com/instantOS/instantWM/ - * https://github.com/bakkeby/dwm-flexipatch/issues/51 - */ -#define ON_EMPTY_KEYS_PATCH 0 - -/* Minor patch that prevents more than one rule being matched for a given client. */ -#define ONLY_ONE_RULE_MATCH_PATCH 0 - -/* This patch makes it so dwm will only exit via quit() if no windows are open. - * This is to prevent you accidentally losing all your work. - * https://dwm.suckless.org/patches/onlyquitonempty/ - */ -#define ONLYQUITONEMPTY_PATCH 0 - -/* The pertag patch adds nmaster, mfacts and layouts per tag rather than per - * monitor (default). - * https://dwm.suckless.org/patches/pertag/ - */ -#define PERTAG_PATCH 1 - -/* Option to enable gaps on a per tag basis rather than globally. - * Depends on both pertag and vanitygaps patches being enabled. - */ -#define PERTAG_VANITYGAPS_PATCH 0 - -/* This patch allows configuring vanity gaps on a per-monitor basis rather than - * all monitors (default). - */ -#define PERMON_VANITYGAPS_PATCH 0 - -/* This controls whether or not to also store bar position on a per - * tag basis, or leave it as one bar per monitor. - */ -#define PERTAGBAR_PATCH 0 - -/* This patch lets you change the position of a client in the stack using the mouse. - * https://github.com/bakkeby/patches/wiki/placemouse - */ -#define PLACEMOUSE_PATCH 0 - -/* This patch provides a way to move clients up and down inside the client list. - * https://dwm.suckless.org/patches/push/ - */ -#define PUSH_PATCH 0 - -/* This patch provides a way to move clients up and down inside the client list, - * but does not push up or down into the master area (except that it does not take - * nmaster into account). - * This takes precedence over the push patch above. - * https://dwm.suckless.org/patches/push/ - */ -#define PUSH_NO_MASTER_PATCH 0 - -/* Variant of the named scratchpads patch allowing scratch keys to be added or removed - * on demand, allowing multiple scratchpad windows to be toggled into and out of view - * in unison, as well as including multi-monitor support. - * - * https://github.com/bakkeby/patches/wiki/renamedscratchpads - */ -#define RENAMED_SCRATCHPADS_PATCH 1 - -/* Renamed scratchpads option to auto-hide scratchpads when moving to a different tag. - * This behaviour is similar to that of the (multiple) scratchpads patch. */ -#define RENAMED_SCRATCHPADS_AUTO_HIDE_PATCH 0 - -/* Shifts all clients per tag to leftmost unoccupied tags. - * - * For example, if clients A, B, C are tagged on tags 1, 5, 9 respectively, when - * this function is called, they will now be on 1, 2, and 3. The focused client - * will also remain focused. - * - * Clients on multiple tags will be treated as if they only were only on their - * leftmost tag, and will be reduced to one tag after the operation is complete. - * https://dwm.suckless.org/patches/reorganizetags/ - */ -#define REORGANIZETAGS_PATCH 0 - -/* By default, windows only resize from the bottom right corner. With this - * patch the mouse is warped to the nearest corner and you resize from there. - * https://dwm.suckless.org/patches/resizecorners/ - */ -#define RESIZECORNERS_PATCH 0 - -/* Practically the same as resizecorners, but the cursor does not warp to corners. - * This takes precedence over the resizecorners patch. - * https://github.com/bakkeby/patches/blob/master/dwm/dwm-resizepoint-6.2.diff - */ -#define RESIZEPOINT_PATCH 1 - -/* Adds a keyboard shortcut to restart dwm or alternatively by using kill -HUP dwmpid. - * Additionally dwm can quit cleanly by using kill -TERM dwmpid. - * https://dwm.suckless.org/patches/restartsig/ - */ -#define RESTARTSIG_PATCH 1 - -/* Adds rio-like drawing to resize the selected client. - * This depends on an external tool slop being installed. - * This patch was backported from instantWM. - * https://github.com/bakkeby/patches/blob/master/dwm/dwm-riodraw-6.2.diff - */ -#define RIODRAW_PATCH 0 - -/* This patch let's you rotate through the stack using keyboard shortcuts. - * https://dwm.suckless.org/patches/rotatestack/ - */ -#define ROTATESTACK_PATCH 0 - -/* This patch adds rounded corners to client windows in dwm. - * You need to uncomment the corresponding line in config.mk to use the -lXext library - * when including this patch. You will also want to set "borderpx = 0;" in your config.h. - * https://github.com/mitchweaver/suckless/blob/master/dwm/patches/mitch-06-rounded_corners-f04cac6d6e39cd9e3fc4fae526e3d1e8df5e34b2.patch - */ -#define ROUNDED_CORNERS_PATCH 0 - -/* This patch saves size and position of every floating window before it is forced - * into tiled mode. If the window is made floating again then the old dimensions - * will be restored. - * https://dwm.suckless.org/patches/save_floats/ - */ -#define SAVEFLOATS_PATCH 1 - -/* The scratchpad patch allows you to spawn or restore floating terminal windows. - * It is typically useful when one need to do some short typing. - * - * Note that this patch changes TAGMASK to make room for special scratchpad tags, - * so ~0 does more than select all tags with this patch. Code that relies on ~0 to - * represent all tags should use ~SPTAGMASK instead. - * - * Upgraded to Christian Tenllado's multiple scratchpad version. - * https://lists.suckless.org/hackers/2004/17205.html - * https://dwm.suckless.org/patches/scratchpads/ - */ -#define SCRATCHPADS_PATCH 0 - -/* Minor alteration of the above allowing clients to keep their size and position when shown */ -#define SCRATCHPADS_KEEP_POSITION_AND_SIZE_PATCH 0 - -/* This alternative patch enables a scratchpad feature in dwm similar to the scratchpad - * feature in i3wm. - * https://github.com/GasparVardanyan/dwm-scratchpad - */ -#define SCRATCHPAD_ALT_1_PATCH 0 - -/* This patch persists some settings across window manager restarts. These include but are not - * limited to: - * - client's assigned tag(s) on which monitor - * - the order of clients - * - nmaster - * - selected layout - * - plus various additions depending on what other patches are used - * - * The above is not persisted across reboots, however. - */ -#define SEAMLESS_RESTART_PATCH 1 - -/* As opposed to the original patch this only adds a rule option allowing fake fullscreen - * to be enabled for applications when they start. This is intended to be used in combination - * with the fakefullscreenclient patch and offers no practical functionality without it. - * https://dwm.suckless.org/patches/selectivefakefullscreen/ - */ -#define SELECTIVEFAKEFULLSCREEN_PATCH 0 - -/* Allows restarting dwm without the dependency of an external script. - * https://dwm.suckless.org/patches/selfrestart/ - */ -#define SELFRESTART_PATCH 0 - -/* Floating windows being sent to another monitor will be centered. - * https://dwm.suckless.org/patches/sendmoncenter/ - */ -#define SENDMON_CENTER_PATCH 0 - -/* This patch allow clients to keep focus when being sent to another monitor. - * https://github.com/bakkeby/patches/blob/master/dwm/dwm-sendmon_keepfocus-6.2.diff - */ -#define SENDMON_KEEPFOCUS_PATCH 1 - -/* This patch allows border pixels to be changed during runtime. - * https://dwm.suckless.org/patches/setborderpx/ - */ -#define SETBORDERPX_PATCH 0 - -/* Combines shifttag and shiftview. Basically moves the window to the next/prev tag and follows it. - * Also see the focusadjacenttag patch. - * https://dwm.suckless.org/patches/shift-tools/ - */ -#define SHIFTBOTH_PATCH 0 - -/* Swaps all the clients on the current tag with all the client on the next/prev tag. - * Depends on the swaptags patch. - * https://dwm.suckless.org/patches/shift-tools/ - */ -#define SHIFTSWAPTAGS_PATCH 0 - -/* Moves the current selected client to the adjacent tag. - * Also see the focusadjacenttag patch. - * https://dwm.suckless.org/patches/shift-tools/ - */ -#define SHIFTTAG_PATCH 0 - -/* Moves the current selected client to the adjacent tag that has at least one client, if none - * then it acts as shifttag. - * https://dwm.suckless.org/patches/shift-tools/ - */ -#define SHIFTTAGCLIENTS_PATCH 0 - -/* This patch adds keybindings for left and right circular shift through tags. - * https://github.com/chau-bao-long/dotfiles/blob/master/suckless/dwm/shiftview.diff - */ -#define SHIFTVIEW_PATCH 0 - -/* This variant of the shiftview patch adds left and right circular shift through tags, - * but skips tags where there are no clients. - */ -#define SHIFTVIEW_CLIENTS_PATCH 0 - -/* This patch makes dwm obey even "soft" sizehints for new clients. Any window - * that requests a specific initial size will be floated and set to that size. - * Unlike with "fixed size" windows, you are able to resize and/or unfloat these - * windows freely - only the initial state is affected. - * This version of the patch is honestly of limited utility since there are many - * clients that will abuse it. - * https://dwm.suckless.org/patches/sizehints/ - */ -#define SIZEHINTS_PATCH 0 - -/* This patch makes dwm obey even "soft" sizehints for new clients. This ruled - * version is essentially the same patch except it obeys the "isfloating" rule - * if it is available in config.h for the given client. - * https://dwm.suckless.org/patches/sizehints/ - */ -#define SIZEHINTS_RULED_PATCH 1 - -/* This patch makes dwm obey even "soft" sizehints for new clients. The isfreesize - * version is similar to the sizehints ruled patch except it allows you to specify - * via client rules which clients this should apply to. Soft sizehints applies by - * default to clients that are not ruled, and will be disabled by default for clients - * that are. - * - * Example client rule enabling soft sizehints: - * - RULE(.wintype = WTYPE "DIALOG", .isfloating = 1, .isfreesize = 1) - * - * https://dwm.suckless.org/patches/sizehints/ - */ -#define SIZEHINTS_ISFREESIZE_PATCH 0 - -/* In a multi-head setup monitor 0 is by default the primary screen, with the left and right - * screen being monitor 1 and 2 respectively. This patch sorts screens left to right (or - * top to bottom in a vertical layout) which aims to address some inconsistencies when it - * comes to focusmon, tagmon and similar functionality. - * https://www.mail-archive.com/hackers@suckless.org/msg09400.html - */ -#define SORTSCREENS_PATCH 0 - -/* Spawns programs from currently focused client's working directory. - * https://dwm.suckless.org/patches/spawn_cwd/ - */ -#define SPAWNCMD_PATCH 0 - -/* This patch provides comprehensive utilities for managing the client stack, providing - * keyboard shortcuts for focusing or placing a client at specific positions in the stack. - * Note that the default keybindings for this patch have been changed in dwm-flexipatch - * due to the many conflicts with other patches. As it provides similar functionality to the - * swapfocus patch it also uses the MOD+s shortcut to focus the previously selected client, - * thus note a conflict between these two patches. - * https://dwm.suckless.org/patches/stacker/ - */ -#define STACKER_PATCH 1 - -/* Steam, and steam windows (games), trigger a ConfigureNotify request every time the window - * gets focus. More so, the configure event passed along from Steam tends to have the wrong - * x and y co-ordinates which can make the window, if floating, jump around the screen. - * - * This patch works around this age-old issue by ignoring the x and y co-ordinates for - * ConfigureNotify requests relating to Steam windows. - * - * https://github.com/bakkeby/patches/wiki/steam - */ -#define STEAM_PATCH 0 - -/* Adds toggleable keyboard shortcut to make a client 'sticky', i.e. visible on all tags. - * https://dwm.suckless.org/patches/sticky/ - */ -#define STICKY_PATCH 0 - -/* This patch adds "window swallowing" to dwm as known from Plan 9's windowing system rio. - * Clients marked with isterminal in config.h swallow a window opened by any child process, - * e.g. running xclock in a terminal. Closing the xclock window restores the terminal window - * in the current position. - * - * This patch depends on the following additional libraries: - * - libxcb - * - Xlib-libxcb - * - xcb-res - * - * You need to uncomment the corresponding line in config.mk to use the above libraries when - * including this patch. - * - * https://dwm.suckless.org/patches/swallow/ - */ -#define SWALLOW_PATCH 0 - -/* This patch depends on the pertag patch and makes it possible to switch focus with a single - * shortcut (MOD+s) instead of having to think if you should use mod-j or mod-k for reaching - * the previously used window. - * https://dwm.suckless.org/patches/swapfocus/ - */ -#define SWAPFOCUS_PATCH 0 - -/* This patch allows swapping the contents of the currently selected tag with another tag using - * keyboard shortcuts. - * https://dwm.suckless.org/patches/swaptags/ - */ -#define SWAPTAGS_PATCH 0 - -/* Switch focus between the master and stack columns using a single keybinding. - * https://dwm.suckless.org/patches/switchcol/ - */ -#define SWITCHCOL_PATCH 0 - -/* By default dwm allow you to set application specific rules so that you can have your browser, - * for example, start up on tag 9 optionally on a given monitor when you open your browser it is - * then automatically moved to the configured tag, but you have to manually enable the tag to see - * the newly opened application. - * This patch adds an extra configuration option for individual rules where: - * 0 is default behaviour - * 1 automatically moves you to the tag of the newly opened application and - * 2 enables the tag of the newly opened application in addition to your existing enabled tags - * 3 as 1, but closing that window reverts the view back to what it was previously (*) - * 4 as 2, but closing that window reverts the view back to what it was previously (*) - * - * (*) except if the client has been moved between tags or to another monitor - * - * https://github.com/bakkeby/patches/blob/master/dwm/dwm-switchtag-6.2.diff - * Also see https://dwm.suckless.org/patches/switchtotag - */ -#define SWITCHTAG_PATCH 0 - -/* This patch transforms the monocle layout into a "tabbed" layout if more than one window is - * present on the monocle view. This patch has been added for demonstration purposes only and has - * limited compatibility with other patches. It will conflict space-wise with a second bar. - * Note that fancybar, awesomebar, bartabgroups and similar patches make the tab patch redundant. - * https://dwm.suckless.org/patches/tab/ - */ -#define TAB_PATCH 0 - -/* Adds keyboard shortcuts to move all (or only floating) windows from one tag to another. - * https://dwm.suckless.org/patches/tagall/ - */ -#define TAGALL_PATCH 0 - -/* This patch allows you to move all visible windows on a monitor to an adjacent monitor. - * https://github.com/bakkeby/patches/blob/master/dwm/dwm-tagallmon-6.2.diff - */ -#define TAGALLMON_PATCH 0 - -/* This patch makes new clients attach into the stack area when you toggle a new tag into - * view. This means your master area will remain unchanged when toggling views. - * The allmaster patch will cause all clients in the master area to be left alone. This patch - * takes precedence over the onemaster tagintostack patch. - * https://dwm.suckless.org/patches/tagintostack/ - */ -#define TAGINTOSTACK_ALLMASTER_PATCH 0 - -/* This patch makes new clients attach into the stack area when you toggle a new tag into - * view. This means your master area will remain unchanged when toggling views. - * The onemaster patch will cause the first client in the master area to be left alone. - * https://dwm.suckless.org/patches/tagintostack/ - */ -#define TAGINTOSTACK_ONEMASTER_PATCH 0 - -/* If you try to send a fullscreen window to an adjacent monitor using tagmon then - * the window is moved behind the scenes, but it remains in fullscreen on the original - * monitor until you exit fullscreen view (at which point it will appear on the adjacent - * monitor). This patch allows a fullscreen window to be moved to an adjacent monitor - * while remaining in fullscreen. - * https://github.com/bakkeby/patches/blob/master/dwm/dwm-tagmonfixfs-6.2.diff - */ -#define TAGMONFIXFS_PATCH 1 - -/* Add functions and keybindings to tag a window to a desired tag on the next (right) - * or previous (left) monitor from the currently selected monitor. - * https://dwm.suckless.org/patches/tagothermonitor/ - */ -#define TAGOTHERMONITOR_PATCH 0 - -/* This patch allows you to swap all visible windows on one monitor with those of an - * adjacent monitor. - * https://github.com/bakkeby/patches/blob/master/dwm/dwm-tagswapmon-6.2.diff - */ -#define TAGSWAPMON_PATCH 0 - -/* Sync tag actions across all monitors. - * This is comparable to a sort of pseudo-desktop environment. - * Also refer to the desktop patch: - * https://github.com/bakkeby/patches/blob/master/dwm/dwm-desktop-6.3.diff - */ -#define TAGSYNC_PATCH 0 - -/* This patch can be useful to the touchpad users because it allows to - * resize windows using Mod + two-finger scroll. It is useful when - * two-finger scrolling is configured in libinput. - * https://dwm.suckless.org/patches/tapresize/ - */ -#define TAPRESIZE_PATCH 0 - -/* This patch allows you to toggle fullscreen on and off using a single shortcut key. - * https://github.com/bakkeby/patches/blob/master/dwm/dwm-togglefullscreen-6.2.diff - */ -#define TOGGLEFULLSCREEN_PATCH 1 - -/* This patch allows for the bar position (top or bottom) to be toggled during runtime. - * https://dwm.suckless.org/patches/toggletopbar/ - */ -#define TOGGLETOPBAR_PATCH 0 - -/* Minor patch that lets you use the same keyboard shortcut to toggle to the previous layout if the - * designated layout is already active. - * - * This allows you to use e.g. MOD+m to change to the monocle layout and use the same keybinding to - * toggle back to what it was previously. The default behaviour in dwm forces you to use either - * MOD+space or MOD+t to change back to tiled layout. - * - * https://github.com/bakkeby/patches/wiki/togglelayout - */ - -#define TOGGLELAYOUT_PATCH 0 - -/* Minor patch that lets you use the same keyboard shortcut to toggle to the previous tag if the - * designated tag is already active. - * - * This allows you to use e.g. MOD+4 to quickly view the 4th tag and use the same keybinding to - * toggle back to what it was previously. The default behaviour in dwm forces you to use either - * MOD+tab or MOD+1 to change back to the previous tag. - * - * Idea ref. - * https://www.reddit.com/r/suckless/comments/ik27vd/key_toggle_between_next_and_previous_tag_dwm/ - * https://github.com/bakkeby/patches/wiki/toggletag - */ -#define TOGGLETAG_PATCH 0 - -/* Lets you transfer the currently focused client between the master and stack area - * while increasing or decreasing the master area (nmaster) accordingly. - * https://dwm.suckless.org/patches/transfer/ - */ -#define TRANSFER_PATCH 0 - -/* Lets you transfer all clients between the master and stack area - * while increasing or decreasing the master area (nmaster) accordingly. - * https://dwm.suckless.org/patches/transfer/ - */ -#define TRANSFER_ALL_PATCH 0 - -/* This patch resets isfloating on any visible windows that have it set. - * Optionally also applies a layout. - * https://dwm.suckless.org/patches/unfloatvisible/ - */ -#define UNFLOATVISIBLE_PATCH 1 - -/* This patch adds a client rule that allows for windows that do not specify the override-redirect - * to not be managed by the window manager. This can be useful for external bars, widgets, - * launchers, docks, desktop icons and more. - * https://github.com/bakkeby/patches/wiki/unmanaged - */ -#define UNMANAGED_PATCH 0 - -/* This patch adds configurable gaps between windows differentiating between outer, inner, - * horizontal and vertical gaps. - * https://github.com/bakkeby/patches/blob/master/dwm/dwm-vanitygaps-6.2.diff - * https://github.com/bakkeby/patches/blob/master/dwm/dwm-cfacts-vanitygaps-6.2.diff - */ -#define VANITYGAPS_PATCH 0 - -/* This patch adds outer gaps for the monocle layout. - * Most gaps patches tries to avoid gaps on the monocle layout, as it is often used as a - * fullscreen mode, hence this is enabled separately from the main vanitygaps patch. - */ -#define VANITYGAPS_MONOCLE_PATCH 0 - -/* By default MOD+Tab will take the user back to the previous tag only. If the user keeps - * using MOD+Tab then the view will switch back and forth between the current and previous tag. - * This patch allows dwm to keep a longer history of previous tag changes such that MOD+Tab can - * be pressed multiple times to go further back to earlier tag selections. - * - * The number of history elements is defined by the NUMVIEWHIST macro in dwm.c and defaults to - * the number of tags in the system. - */ -#define VIEW_HISTORY_PATCH 0 - -/* Follow a window to the tag it is being moved to. - * https://dwm.suckless.org/patches/viewontag/ - */ -#define VIEWONTAG_PATCH 0 - -/* This patch warps the mouse cursor to the center of the currently focused window or screen - * when the mouse cursor is (a) on a different screen or (b) on top of a different window. - * https://dwm.suckless.org/patches/warp/ - */ -#define WARP_PATCH 0 - -/* Sometimes a single application opens different windows depending on the task - * at hand and this is often reflected in the WM_WINDOW_ROLE(STRING) x property. - * This patch adds the role field to the rule configuration so that one can - * differentiate between, say, Firefox "browser" vs "Preferences" vs "Manager" - * or Google-chrome "browser" vs "pop-up". - * https://github.com/bakkeby/patches/blob/master/dwm/dwm-windowrolerule-6.2.diff - */ -#define WINDOWROLERULE_PATCH 1 - -/* The winview patch allows switching the view to that of a given client from the all-window - * view (Mod-0) using a keyboard shortcut. - * http://dwm.suckless.org/patches/winview/ - */ -#define WINVIEW_PATCH 0 - -/* Remember keyboard layout per client. - * It is recommended that you configure xkb before using this patch as described in - * https://www.x.org/archive/X11R7.5/doc/input/XKB-Config.html - * https://dwm.suckless.org/patches/xkb/ - */ -#define XKB_PATCH 0 - -/* Allows dwm to read colors from xrdb (.Xresources) during runtime. Compatible with - * the float border color, awesomebar, urgentborder and titlecolor patches. - * https://dwm.suckless.org/patches/xrdb/ - */ -#define XRDB_PATCH 0 - -/* Simple patch that allows floating windows to be zoomed into the master stack position. - * https://www.reddit.com/r/suckless/comments/ie5fe3/zoomfloating_my_own_simple_original_patch/ - */ -#define ZOOMFLOATING_PATCH 0 - -/* The zoomswap patch allows a master and a stack window to swap places - * rather than every window on the screen changing position. - * https://dwm.suckless.org/patches/zoomswap/ - */ -#define ZOOMSWAP_PATCH 0 - -/** - * Layouts - */ - -/* Bottomstack layout. - * https://dwm.suckless.org/patches/bottomstack/ - */ -#define BSTACK_LAYOUT 1 - -/* Bottomstack horizontal layout. - * https://dwm.suckless.org/patches/bottomstack/ - */ -#define BSTACKHORIZ_LAYOUT 0 - -/* Centered master layout. - * https://dwm.suckless.org/patches/centeredmaster/ - */ -#define CENTEREDMASTER_LAYOUT 1 - -/* Centered floating master layout. - * https://dwm.suckless.org/patches/centeredmaster/ - */ -#define CENTEREDFLOATINGMASTER_LAYOUT 0 - -/* Same as the default tile layout except clients in the master area are arranged in - * columns (i.e. left to right). - * https://dwm.suckless.org/patches/columns/ - */ -#define COLUMNS_LAYOUT 0 - -/* Deck layout. - * https://dwm.suckless.org/patches/deck/ - */ -#define DECK_LAYOUT 1 - -/* Fibonacci dwindle layout. - * https://dwm.suckless.org/patches/fibonacci/ - */ -#define FIBONACCI_DWINDLE_LAYOUT 0 - -/* Fibonacci spiral layout. - * https://dwm.suckless.org/patches/fibonacci/ - */ -#define FIBONACCI_SPIRAL_LAYOUT 0 - -/* Flextile deluxe layout. - * A revamped, more flexible, and over-the-top version of the original flextile layout. - * https://dwm.suckless.org/patches/flextile/ (original) - */ -#define FLEXTILE_DELUXE_LAYOUT 0 - -/* Gappless grid layout. - * https://dwm.suckless.org/patches/gaplessgrid/ - */ -#define GAPPLESSGRID_LAYOUT 1 - -/* Gridmode (grid) layout. - * https://dwm.suckless.org/patches/gridmode/ - */ -#define GRIDMODE_LAYOUT 0 - -/* Horizontal grid (horizgrid) layout. - * https://dwm.suckless.org/patches/horizgrid/ - */ -#define HORIZGRID_LAYOUT 0 - -/* Grid layout where nmaster controls the number of rows. - * https://dwm.suckless.org/patches/nrowgrid/ - */ -#define NROWGRID_LAYOUT 0 - -/* The default tile layout. - * This can be optionally disabled in favour of other layouts. - */ -#define TILE_LAYOUT 1 - -/* Monocle layout (default). - * This can be optionally disabled in favour of other layouts. - */ -#define MONOCLE_LAYOUT 1 diff --git a/config/patches/autostart_signal.diff b/config/patches/autostart_signal.diff deleted file mode 100644 index 70b6119..0000000 --- a/config/patches/autostart_signal.diff +++ /dev/null @@ -1,11 +0,0 @@ ---- a/dwm.c -+++ b/dwm.c -@@ -693,7 +693,7 @@ cleanup(void) - /* kill child processes */ - for (i = 0; i < autostart_len; i++) { - if (0 < autostart_pids[i]) { -- kill(autostart_pids[i], SIGTERM); -+ kill(autostart_pids[i], autostart_kill_signal); - waitpid(autostart_pids[i], NULL, 0); - } - } diff --git a/config/patches/stext_buffer_size.diff b/config/patches/stext_buffer_size.diff deleted file mode 100644 index 723a8c4..0000000 --- a/config/patches/stext_buffer_size.diff +++ /dev/null @@ -1,13 +0,0 @@ ---- a/dwm.c -+++ b/dwm.c -@@ -393,8 +393,8 @@ static void zoom(const Arg *arg); - - /* variables */ - static const char broken[] = "broken"; --static char stext[1024]; --static char rawstext[512]; -+static char stext[2048]; -+static char rawstext[1024]; - - static int screen; - static int sw, sh; /* X display screen geometry width, height */ diff --git a/config/patches/tagmon_scratchpad_floatrules.diff b/config/patches/tagmon_scratchpad_floatrules.diff deleted file mode 100644 index 1678eb2..0000000 --- a/config/patches/tagmon_scratchpad_floatrules.diff +++ /dev/null @@ -1,13 +0,0 @@ ---- a/dwm.c -+++ b/dwm.c -@@ -2179,6 +2179,10 @@ tagmon(const Arg *arg) - c->isfullscreen = 1; - resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); - XRaiseWindow(dpy, c->win); -+ } else if (c->isfloating && c->scratchkey != 0) { -+ sendmon(c, dest); -+ applyrules(c); -+ XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); - } else - sendmon(c, dest); - } diff --git a/drw.c b/drw.c new file mode 100644 index 0000000..92f082e --- /dev/null +++ b/drw.c @@ -0,0 +1,297 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include +#include +#include +#include + +#include "drw.h" +#include "util.h" + +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_font_create instead. + */ +static Fnt * +xfont_create(Drw *drw, const char *fontname) +{ + Fnt *font; + PangoFontMap *fontmap; + PangoContext *context; + PangoFontDescription *desc; + PangoFontMetrics *metrics; + + if (!fontname) { + die("no font specified."); + } + + font = ecalloc(1, sizeof(Fnt)); + font->dpy = drw->dpy; + + fontmap = pango_xft_get_font_map(drw->dpy, drw->screen); + context = pango_font_map_create_context(fontmap); + desc = pango_font_description_from_string(fontname); + font->layout = pango_layout_new(context); + pango_layout_set_font_description(font->layout, desc); + + metrics = pango_context_get_metrics(context, desc, pango_language_from_string ("en-us")); + font->h = pango_font_metrics_get_height(metrics) / PANGO_SCALE; + + pango_font_metrics_unref(metrics); + g_object_unref(context); + + return font; +} + +static void +xfont_free(Fnt *font) +{ + if (!font) + return; + if (font->layout) + g_object_unref(font->layout); + free(font); +} + +Fnt* +drw_font_create(Drw* drw, const char font[]) +{ + Fnt *fnt = NULL; + + if (!drw || !font) + return NULL; + + fnt = xfont_create(drw, font); + + return (drw->fonts = fnt); +} + +void +drw_fontset_free(Fnt *font) +{ + if (font) { + 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_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, Bool markup) +{ + char buf[1024]; + int i, ty, th; + unsigned int ew, eh; + XftDraw *d = NULL; + size_t len; + int render = x || y || w || h; + + if (!drw || (render && !drw->scheme) || !text || !drw->fonts) + return 0; + + if (!render) { + w = ~w; + } 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; + } + + len = strlen(text); + + if (len) { + drw_font_getexts(drw->fonts, text, len, &ew, &eh, markup); + th = eh; + /* shorten text if necessary */ + for (len = MIN(len, sizeof(buf) - 1); len && ew > w; len--) { + drw_font_getexts(drw->fonts, text, len, &ew, &eh, markup); + if (eh > th) + th = eh; + } + + if (len) { + memcpy(buf, text, len); + buf[len] = '\0'; + if (len < strlen(text)) + for (i = len; i && i > len - 3; buf[--i] = '.') + ; /* NOP */ + + if (render) { + ty = y + (h - th) / 2; + if (markup) + pango_layout_set_markup(drw->fonts->layout, buf, len); + else + pango_layout_set_text(drw->fonts->layout, buf, len); + pango_xft_render_layout(d, &drw->scheme[invert ? ColBg : ColFg], + drw->fonts->layout, x * PANGO_SCALE, ty * PANGO_SCALE); + if (markup) /* clear markup attributes */ + pango_layout_set_attributes(drw->fonts->layout, NULL); + } + x += ew; + w -= ew; + } + } + 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, Bool markup) +{ + if (!drw || !drw->fonts || !text) + return 0; + return drw_text(drw, 0, 0, 0, 0, 0, text, 0, markup); +} + +void +drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h, Bool markup) +{ + if (!font || !text) + return; + + PangoRectangle r; + if (markup) + pango_layout_set_markup(font->layout, text, len); + else + pango_layout_set_text(font->layout, text, len); + pango_layout_get_extents(font->layout, 0, &r); + if (markup) /* clear markup attributes */ + pango_layout_set_attributes(font->layout, NULL); + if (w) + *w = r.width / PANGO_SCALE; + if (h) + *h = r.height / PANGO_SCALE; +} + +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..701bed3 --- /dev/null +++ b/drw.h @@ -0,0 +1,66 @@ +/* See LICENSE file for copyright and license details. */ + +#include +#include + +typedef struct { + Cursor cursor; +} Cur; + +typedef struct Fnt { + Display *dpy; + unsigned int h; + PangoLayout *layout; +} Fnt; + +enum { ColFg, ColBg, ColBorder, ColFloat, ColCount }; /* 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_font_create(Drw* drw, const char font[]); +void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h, Bool markup); +void drw_fontset_free(Fnt* set); +unsigned int drw_fontset_getwidth(Drw *drw, const char *text, Bool markup); + +/* 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_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, Bool markup); + +/* Map functions */ +void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h); + diff --git a/dwm-flexipatch b/dwm-flexipatch deleted file mode 160000 index 66770cf..0000000 --- a/dwm-flexipatch +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 66770cfbccceb562279ad7f8c700622b42776281 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..d734593 --- /dev/null +++ b/dwm.c @@ -0,0 +1,2702 @@ +/* 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 +#ifdef XINERAMA +#include +#endif /* XINERAMA */ +#include + +#include "drw.h" +#include "util.h" + +#include + +#include + +/* macros */ +#define Button6 6 +#define Button7 7 +#define Button8 8 +#define Button9 9 +#define NUMTAGS 9 +#define NUMVIEWHIST NUMTAGS +#define BARRULES 20 +#define BUTTONMASK (ButtonPressMask|ButtonReleaseMask) +#define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)) +#define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \ + * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy))) +#define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags])) +#define LENGTH(X) (sizeof X / sizeof X[0]) +#define MOUSEMASK (BUTTONMASK|PointerMotionMask) +#define WIDTH(X) ((X)->w + 2 * (X)->bw) +#define HEIGHT(X) ((X)->h + 2 * (X)->bw) +#define WTYPE "_NET_WM_WINDOW_TYPE_" +#define TAGMASK ((1 << NUMTAGS) - 1) +#define TEXTWM(X) (drw_fontset_getwidth(drw, (X), True) + lrpad) +#define TEXTW(X) (drw_fontset_getwidth(drw, (X), False) + lrpad) +#define HIDDEN(C) ((getstate(C->win) == IconicState)) + +/* enums */ +enum { + CurResizeBR, + CurResizeBL, + CurResizeTR, + CurResizeTL, + CurNormal, + CurResize, + CurMove, + CurLast +}; /* cursor */ + +enum { + SchemeNorm, + SchemeSel, + SchemeTitleNorm, + SchemeTitleSel, + SchemeTagsNorm, + SchemeTagsSel, + SchemeHidNorm, + SchemeHidSel, + SchemeUrg, + SchemeScratchSel, + SchemeScratchNorm, +}; /* color schemes */ + +enum { + NetSupported, NetWMName, NetWMState, NetWMCheck, + NetWMFullscreen, NetActiveWindow, NetWMWindowType, + NetDesktopNames, NetDesktopViewport, NetNumberOfDesktops, NetCurrentDesktop, + NetClientList, + NetClientListStacking, + NetLast +}; /* EWMH atoms */ + +enum { + WMProtocols, + WMDelete, + WMState, + WMTakeFocus, + WMWindowRole, + WMLast +}; /* default atoms */ + +enum { + ClientFields, + ClientTags, + ClientLast +}; /* dwm client atoms */ + +enum { + ClkTagBar, + ClkLtSymbol, + ClkStatusText, + ClkWinTitle, + ClkClientWin, + ClkRootWin, + ClkLast +}; /* clicks */ + +enum { + BAR_ALIGN_LEFT, + BAR_ALIGN_CENTER, + BAR_ALIGN_RIGHT, + BAR_ALIGN_LEFT_LEFT, + BAR_ALIGN_LEFT_RIGHT, + BAR_ALIGN_LEFT_CENTER, + BAR_ALIGN_NONE, + BAR_ALIGN_RIGHT_LEFT, + BAR_ALIGN_RIGHT_RIGHT, + BAR_ALIGN_RIGHT_CENTER, + BAR_ALIGN_LAST +}; /* bar alignment */ + +typedef union { + int i; + unsigned int ui; + float f; + const void *v; +} Arg; + +typedef struct Monitor Monitor; +typedef struct Bar Bar; +struct Bar { + Window win; + Monitor *mon; + Bar *next; + int idx; + int showbar; + int topbar; + int external; + int borderpx; + int borderscheme; + int bx, by, bw, bh; /* bar geometry */ + int w[BARRULES]; // width, array length == barrules, then use r index for lookup purposes + int x[BARRULES]; // x position, array length == ^ +}; + +typedef struct { + int x; + int y; + int h; + int w; +} BarArg; + +typedef struct { + int monitor; + int bar; + int alignment; // see bar alignment enum + int (*widthfunc)(Bar *bar, BarArg *a); + int (*drawfunc)(Bar *bar, BarArg *a); + int (*clickfunc)(Bar *bar, Arg *arg, BarArg *a); + int (*hoverfunc)(Bar *bar, BarArg *a, XMotionEvent *ev); + char *name; // for debugging + int x, w; // position, width for internal use +} BarRule; + +typedef struct { + unsigned int click; + unsigned int mask; + unsigned int button; + void (*func)(const Arg *arg); + const Arg arg; +} Button; + +typedef struct Client Client; +struct Client { + char name[256]; + float mina, maxa; + int x, y, w, h; + int sfx, sfy, sfw, sfh; /* stored float geometry, used on mode revert */ + unsigned int idx; + 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; + int iscentered; + Client *next; + Client *snext; + Monitor *mon; + Window win; + char scratchkey; +}; + +typedef struct { + unsigned int mod; + KeySym keysym; + void (*func)(const Arg *); + const Arg arg; +} Key; + +typedef struct { + const char *symbol; + void (*arrange)(Monitor *); +} Layout; + +typedef struct Pertag Pertag; +struct Monitor { + char ltsymbol[16]; + float mfact; + int nmaster; + int num; + int mx, my, mw, mh; /* screen size */ + int wx, wy, ww, wh; /* window area */ + unsigned int seltags; + unsigned int sellt; + unsigned int tagset[2]; + int showbar; + Client *clients; + Client *sel; + Client *stack; + Monitor *next; + Bar *bar; + const Layout *lt[2]; + Pertag *pertag; +}; + +typedef struct { + const char *class; + const char *role; + const char *instance; + const char *title; + const char *wintype; + unsigned int tags; + int iscentered; + int isfloating; + const char *floatpos; + int monitor; + const char scratchkey; +} Rule; + +#define RULE(...) { .monitor = -1, __VA_ARGS__ }, + +/* Cross patch compatibility rule macro helper macros */ +#define FLOATING , .isfloating = 1 +#define CENTERED , .iscentered = 1 +#define PERMANENT +#define FAKEFULLSCREEN +#define NOSWALLOW +#define TERMINAL +#define SWITCHTAG + +/* function declarations */ +static void applyrules(Client *c); +static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact); +static void arrange(Monitor *m); +static void arrangemon(Monitor *m); +static void attach(Client *c); +static void attachstack(Client *c); +static void buttonpress(XEvent *e); +static void checkotherwm(void); +static void cleanup(void); +static void cleanupmon(Monitor *mon); +static void clientmessage(XEvent *e); +static void configure(Client *c); +static void configurenotify(XEvent *e); +static void configurerequest(XEvent *e); +static Monitor *createmon(void); +static void destroynotify(XEvent *e); +static void detach(Client *c); +static void detachstack(Client *c); +static Monitor *dirtomon(int dir); +static void drawbar(Monitor *m); +static void drawbars(void); +static void drawbarwin(Bar *bar); +static void expose(XEvent *e); +static void focus(Client *c); +static void focusin(XEvent *e); +static void focusmon(const Arg *arg); +static Atom getatomprop(Client *c, Atom prop, Atom req); +static int getrootptr(int *x, int *y); +static long getstate(Window w); +static int gettextprop(Window w, Atom atom, char *text, unsigned int size); +static void grabbuttons(Client *c, int focused); +static void grabkeys(void); +static void incnmaster(const Arg *arg); +static void keypress(XEvent *e); +static void killclient(const Arg *arg); +static void manage(Window w, XWindowAttributes *wa); +static void mappingnotify(XEvent *e); +static void maprequest(XEvent *e); +static void motionnotify(XEvent *e); +static void movemouse(const Arg *arg); +static Client *nexttiled(Client *c); +static void pop(Client *c); +static void propertynotify(XEvent *e); +static void quit(const Arg *arg); +static Monitor *recttomon(int x, int y, int w, int h); +static void resize(Client *c, int x, int y, int w, int h, int interact); +static void resizeclient(Client *c, int x, int y, int w, int h); +static void resizemouse(const Arg *arg); +static void restack(Monitor *m); +static void run(void); +static void scan(void); +static int sendevent(Client *c, Atom proto); +static void sendmon(Client *c, Monitor *m); +static void setclientstate(Client *c, long state); +static void setfocus(Client *c); +static void setfullscreen(Client *c, int fullscreen); +static void setlayout(const Arg *arg); +static void setmfact(const Arg *arg); +static void setup(void); +static void seturgent(Client *c, int urg); +static void sigchld(int unused); +static void showhide(Client *c); +static void spawn(const Arg *arg); +static void tag(const Arg *arg); +static void tagmon(const Arg *arg); +static void togglebar(const Arg *arg); +static void togglefloating(const Arg *arg); +static void toggletag(const Arg *arg); +static void toggleview(const Arg *arg); +static void unfocus(Client *c, int setfocus, Client *nextfocus); +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 updatewmhints(Client *c); +static void view(const Arg *arg); +static Client *wintoclient(Window w); +static Monitor *wintomon(Window w); +static int xerror(Display *dpy, XErrorEvent *ee); +static int xerrordummy(Display *dpy, XErrorEvent *ee); +static int xerrorstart(Display *dpy, XErrorEvent *ee); +static void zoom(const Arg *arg); + +/* bar functions */ + +#include "patch/include.h" + +/* variables */ +static const char broken[] = "broken"; +static char stext[2048]; +static char rawstext[1024]; + +static int screen; +static int sw, sh; /* X display screen geometry width, height */ +static int bh; /* bar geometry */ +static int lrpad; /* sum of left and right padding for text */ +/* Some clients (e.g. alacritty) helpfully send configure requests with a new size or position + * when they detect that they have been moved to another monitor. This can cause visual glitches + * when moving (or resizing) client windows from one monitor to another. This variable is used + * internally to ignore such configure requests while movemouse or resizemouse are being used. */ +static int ignoreconfigurerequests = 0; +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, + [Expose] = expose, + [FocusIn] = focusin, + [KeyPress] = keypress, + [MappingNotify] = mappingnotify, + [MapRequest] = maprequest, + [MotionNotify] = motionnotify, + [PropertyNotify] = propertynotify, + [UnmapNotify] = unmapnotify +}; +static Atom wmatom[WMLast], netatom[NetLast]; +static Atom clientatom[ClientLast]; +static volatile sig_atomic_t running = 1; +static Cur *cursor[CurLast]; +static Clr **scheme; +static Display *dpy; +static Drw *drw; +static Monitor *mons, *selmon; +static Window root, wmcheckwin; + +/* configuration, allows nested code to access above variables */ +#include "config.h" + +#include "patch/include.c" + +/* compile-time check if all tags fit into an unsigned int bit array. */ +struct NumTags { char limitexceeded[NUMTAGS > 31 ? -1 : 1]; }; + +/* function implementations */ +void +applyrules(Client *c) +{ + const char *class, *instance; + Atom wintype; + char role[64]; + unsigned int i; + const Rule *r; + Monitor *m; + XClassHint ch = { NULL, NULL }; + + /* rule matching */ + c->isfloating = 0; + c->tags = 0; + c->scratchkey = 0; + XGetClassHint(dpy, c->win, &ch); + class = ch.res_class ? ch.res_class : broken; + instance = ch.res_name ? ch.res_name : broken; + wintype = getatomprop(c, netatom[NetWMWindowType], XA_ATOM); + gettextprop(c->win, wmatom[WMWindowRole], role, sizeof(role)); + + 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->role || strstr(role, r->role)) + && (!r->instance || strstr(instance, r->instance)) + && (!r->wintype || wintype == XInternAtom(dpy, r->wintype, False))) + { + c->iscentered = r->iscentered; + c->isfloating = r->isfloating; + c->tags |= r->tags; + c->scratchkey = r->scratchkey; + for (m = mons; m && m->num != r->monitor; m = m->next); + if (m) + c->mon = m; + if (c->isfloating && r->floatpos) { + c->iscentered = 0; + setfloatpos(c, r->floatpos); + } + + } + } + if (ch.res_class) + XFree(ch.res_class); + if (ch.res_name) + XFree(ch.res_name); + c->tags = c->tags & TAGMASK ? c->tags & TAGMASK : c->mon->tagset[c->mon->seltags]; +} + +int +applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact) +{ + int baseismin; + Monitor *m = c->mon; + + /* set minimum possible */ + *w = MAX(1, *w); + *h = MAX(1, *h); + if (interact) { + if (*x > sw) + *x = sw - WIDTH(c); + if (*y > sh) + *y = sh - HEIGHT(c); + if (*x + *w + 2 * c->bw < 0) + *x = 0; + if (*y + *h + 2 * c->bw < 0) + *y = 0; + } else { + if (*x >= m->wx + m->ww) + *x = m->wx + m->ww - WIDTH(c); + if (*y >= m->wy + m->wh) + *y = m->wy + m->wh - HEIGHT(c); + if (*x + *w + 2 * c->bw <= m->wx) + *x = m->wx; + if (*y + *h + 2 * c->bw <= m->wy) + *y = m->wy; + } + if (*h < bh) + *h = bh; + if (*w < bh) + *w = bh; + if (resizehints || c->isfloating || !c->mon->lt[c->mon->sellt]->arrange) { + if (!c->hintsvalid) + updatesizehints(c); + /* see last two sentences in ICCCM 4.1.2.3 */ + baseismin = c->basew == c->minw && c->baseh == c->minh; + if (!baseismin) { /* temporarily remove base dimensions */ + *w -= c->basew; + *h -= c->baseh; + } + /* adjust for aspect limits */ + if (c->mina > 0 && c->maxa > 0) { + if (c->maxa < (float)*w / *h) + *w = *h * c->maxa + 0.5; + else if (c->mina < (float)*h / *w) + *h = *w * c->mina + 0.5; + } + if (baseismin) { /* increment calculation requires this */ + *w -= c->basew; + *h -= c->baseh; + } + /* adjust for increment value */ + if (c->incw) + *w -= *w % c->incw; + if (c->inch) + *h -= *h % c->inch; + /* restore base dimensions */ + *w = MAX(*w + c->basew, c->minw); + *h = MAX(*h + c->baseh, c->minh); + if (c->maxw) + *w = MIN(*w, c->maxw); + if (c->maxh) + *h = MIN(*h, c->maxh); + } + return *x != c->x || *y != c->y || *w != c->w || *h != c->h; +} + +void +arrange(Monitor *m) +{ + if (m) + showhide(m->stack); + else for (m = mons; m; m = m->next) + showhide(m->stack); + if (m) { + arrangemon(m); + restack(m); + } else for (m = mons; m; m = m->next) + arrangemon(m); +} + +void +arrangemon(Monitor *m) +{ + strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol); + if (m->lt[m->sellt]->arrange) + m->lt[m->sellt]->arrange(m); +} + +void +attach(Client *c) +{ + c->next = c->mon->clients; + c->mon->clients = c; +} + +void +attachstack(Client *c) +{ + c->snext = c->mon->stack; + c->mon->stack = c; +} + +void +buttonpress(XEvent *e) +{ + int click, i, r; + Arg arg = {0}; + Client *c; + Monitor *m; + Bar *bar; + XButtonPressedEvent *ev = &e->xbutton; + const BarRule *br; + BarArg carg = { 0, 0, 0, 0 }; + click = ClkRootWin; + + /* focus monitor if necessary */ + if ((m = wintomon(ev->window)) && m != selmon + && (focusonwheel || (ev->button != Button4 && ev->button != Button5)) + ) { + unfocus(selmon->sel, 1, NULL); + selmon = m; + focus(NULL); + } + + for (bar = selmon->bar; bar; bar = bar->next) { + if (ev->window == bar->win) { + for (r = 0; r < LENGTH(barrules); r++) { + br = &barrules[r]; + if (br->bar != bar->idx || (br->monitor == 'A' && m != selmon) || br->clickfunc == NULL) + continue; + if (br->monitor != 'A' && br->monitor != -1 && br->monitor != bar->mon->num) + continue; + if (bar->x[r] <= ev->x && ev->x <= bar->x[r] + bar->w[r]) { + carg.x = ev->x - bar->x[r]; + carg.y = ev->y - bar->borderpx; + carg.w = bar->w[r]; + carg.h = bar->bh - 2 * bar->borderpx; + click = br->clickfunc(bar, &arg, &carg); + if (click < 0) + return; + break; + } + } + break; + } + } + + if (click == ClkRootWin && (c = wintoclient(ev->window))) { + if (focusonwheel || (ev->button != Button4 && ev->button != Button5)) + focus(c); + 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) +{ + Monitor *m; + Layout foo = { "", NULL }; + size_t i; + + for (m = mons; m; m = m->next) + persistmonitorstate(m); + + /* kill child processes */ + for (i = 0; i < autostart_len; i++) { + if (0 < autostart_pids[i]) { + kill(autostart_pids[i], autostart_kill_signal); + waitpid(autostart_pids[i], NULL, 0); + } + } + + 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; + Bar *bar; + + if (mon == mons) + mons = mons->next; + else { + for (m = mons; m && m->next != mon; m = m->next); + m->next = mon->next; + } + for (bar = mon->bar; bar; bar = mon->bar) { + if (!bar->external) { + XUnmapWindow(dpy, bar->win); + XDestroyWindow(dpy, bar->win); + } + mon->bar = bar->next; + free(bar); + } + free(mon->pertag); + 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; + Bar *bar; + 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, sh); + 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); + for (bar = m->bar; bar; bar = bar->next) + XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh); + } + arrange(NULL); + focus(NULL); + } + } +} + +void +configurerequest(XEvent *e) +{ + Client *c; + Monitor *m; + XConfigureRequestEvent *ev = &e->xconfigurerequest; + XWindowChanges wc; + + if (ignoreconfigurerequests) + return; + + if ((c = wintoclient(ev->window))) { + if (ev->value_mask & CWBorderWidth) + c->bw = ev->border_width; + else if (c->isfloating || !selmon->lt[selmon->sellt]->arrange) { + m = c->mon; + if (ev->value_mask & CWX) { + c->oldx = c->x; + c->x = m->mx + ev->x; + } + if (ev->value_mask & CWY) { + c->oldy = c->y; + c->y = m->my + ev->y; + } + if (ev->value_mask & CWWidth) { + c->oldw = c->w; + c->w = ev->width; + } + if (ev->value_mask & CWHeight) { + c->oldh = c->h; + c->h = ev->height; + } + if ((c->x + c->w) > m->mx + m->mw && c->isfloating) + c->x = m->mx + (m->mw / 2 - WIDTH(c) / 2); /* center in x direction */ + if ((c->y + c->h) > m->my + m->mh && c->isfloating) + c->y = m->my + (m->mh / 2 - HEIGHT(c) / 2); /* center in y direction */ + if ((ev->value_mask & (CWX|CWY)) && !(ev->value_mask & (CWWidth|CWHeight))) + configure(c); + if (ISVISIBLE(c)) + XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); + } else + configure(c); + } else { + wc.x = ev->x; + wc.y = ev->y; + wc.width = ev->width; + wc.height = ev->height; + wc.border_width = ev->border_width; + wc.sibling = ev->above; + wc.stack_mode = ev->detail; + XConfigureWindow(dpy, ev->window, ev->value_mask, &wc); + } + XSync(dpy, False); +} + +Monitor * +createmon(void) +{ + Monitor *m, *mon; + int i, n, mi, max_bars = 2, istopbar = topbar; + + const BarRule *br; + Bar *bar; + + m = ecalloc(1, sizeof(Monitor)); + m->tagset[0] = m->tagset[1] = 1; + m->mfact = mfact; + m->nmaster = nmaster; + m->showbar = showbar; + for (mi = 0, mon = mons; mon; mon = mon->next, mi++); // monitor index + m->num = mi; + m->lt[0] = &layouts[0]; + m->lt[1] = &layouts[1 % LENGTH(layouts)]; + strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); + + /* Derive the number of bars for this monitor based on bar rules */ + for (n = -1, i = 0; i < LENGTH(barrules); i++) { + br = &barrules[i]; + if (br->monitor == 'A' || br->monitor == -1 || br->monitor == m->num) + n = MAX(br->bar, n); + } + + m->bar = NULL; + for (i = 0; i <= n && i < max_bars; i++) { + bar = ecalloc(1, sizeof(Bar)); + bar->mon = m; + bar->idx = i; + bar->next = m->bar; + bar->topbar = istopbar; + m->bar = bar; + istopbar = !istopbar; + bar->showbar = 1; + bar->external = 0; + bar->borderpx = 0; + bar->bh = bh + bar->borderpx * 2; + bar->borderscheme = SchemeNorm; + } + + if (!(m->pertag = (Pertag *)calloc(1, sizeof(Pertag)))) + die("fatal: could not malloc() %u bytes\n", sizeof(Pertag)); + m->pertag->curtag = 1; + for (i = 0; i <= NUMTAGS; i++) { + + /* init nmaster */ + m->pertag->nmasters[i] = m->nmaster; + + /* init mfacts */ + m->pertag->mfacts[i] = m->mfact; + + /* init layouts */ + m->pertag->ltidxs[i][0] = m->lt[0]; + m->pertag->ltidxs[i][1] = m->lt[1]; + m->pertag->sellts[i] = m->sellt; + + } + + restoremonitorstate(m); + + return m; +} + +void +destroynotify(XEvent *e) +{ + Client *c; + XDestroyWindowEvent *ev = &e->xdestroywindow; + + if ((c = wintoclient(ev->window))) + unmanage(c, 1); +} + +void +detach(Client *c) +{ + Client **tc; + c->idx = 0; + + for (tc = &c->mon->clients; *tc && *tc != c; tc = &(*tc)->next); + *tc = c->next; + c->next = NULL; +} + +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; + } + c->snext = NULL; +} + +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) +{ + Bar *bar; + + if (m->showbar) + for (bar = m->bar; bar; bar = bar->next) + drawbarwin(bar); +} + +void +drawbars(void) +{ + Monitor *m; + for (m = mons; m; m = m->next) + drawbar(m); +} + +void +drawbarwin(Bar *bar) +{ + if (!bar || !bar->win || bar->external) + return; + int r, w, total_drawn = 0; + int rx, lx, rw, lw; // bar size, split between left and right if a center module is added + const BarRule *br; + + if (bar->borderpx) { + XSetForeground(drw->dpy, drw->gc, scheme[bar->borderscheme][ColBorder].pixel); + XFillRectangle(drw->dpy, drw->drawable, drw->gc, 0, 0, bar->bw, bar->bh); + } + + BarArg warg = { 0 }; + BarArg darg = { 0 }; + warg.h = bar->bh - 2 * bar->borderpx; + + rw = lw = bar->bw - 2 * bar->borderpx; + rx = lx = bar->borderpx; + + drw_setscheme(drw, scheme[SchemeNorm]); + drw_rect(drw, lx, bar->borderpx, lw, bar->bh - 2 * bar->borderpx, 1, 1); + for (r = 0; r < LENGTH(barrules); r++) { + br = &barrules[r]; + if (br->bar != bar->idx || !br->widthfunc || (br->monitor == 'A' && bar->mon != selmon)) + continue; + if (br->monitor != 'A' && br->monitor != -1 && br->monitor != bar->mon->num) + continue; + drw_setscheme(drw, scheme[SchemeNorm]); + warg.w = (br->alignment < BAR_ALIGN_RIGHT_LEFT ? lw : rw); + + w = br->widthfunc(bar, &warg); + w = MIN(warg.w, w); + + if (lw <= 0) { // if left is exhausted then switch to right side, and vice versa + lw = rw; + lx = rx; + } else if (rw <= 0) { + rw = lw; + rx = lx; + } + + switch(br->alignment) { + default: + case BAR_ALIGN_NONE: + case BAR_ALIGN_LEFT_LEFT: + case BAR_ALIGN_LEFT: + bar->x[r] = lx; + if (lx == rx) { + rx += w; + rw -= w; + } + lx += w; + lw -= w; + break; + case BAR_ALIGN_LEFT_RIGHT: + case BAR_ALIGN_RIGHT: + bar->x[r] = lx + lw - w; + if (lx == rx) + rw -= w; + lw -= w; + break; + case BAR_ALIGN_LEFT_CENTER: + case BAR_ALIGN_CENTER: + bar->x[r] = lx + lw / 2 - w / 2; + if (lx == rx) { + rw = rx + rw - bar->x[r] - w; + rx = bar->x[r] + w; + } + lw = bar->x[r] - lx; + break; + case BAR_ALIGN_RIGHT_LEFT: + bar->x[r] = rx; + if (lx == rx) { + lx += w; + lw -= w; + } + rx += w; + rw -= w; + break; + case BAR_ALIGN_RIGHT_RIGHT: + bar->x[r] = rx + rw - w; + if (lx == rx) + lw -= w; + rw -= w; + break; + case BAR_ALIGN_RIGHT_CENTER: + bar->x[r] = rx + rw / 2 - w / 2; + if (lx == rx) { + lw = lx + lw - bar->x[r] + w; + lx = bar->x[r] + w; + } + rw = bar->x[r] - rx; + break; + } + bar->w[r] = w; + darg.x = bar->x[r]; + darg.y = bar->borderpx; + darg.h = bar->bh - 2 * bar->borderpx; + darg.w = bar->w[r]; + if (br->drawfunc) + total_drawn += br->drawfunc(bar, &darg); + } + + if (total_drawn == 0 && bar->showbar) { + bar->showbar = 0; + updatebarpos(bar->mon); + XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh); + arrange(bar->mon); + } + else if (total_drawn > 0 && !bar->showbar) { + bar->showbar = 1; + updatebarpos(bar->mon); + XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh); + drw_map(drw, bar->win, 0, 0, bar->bw, bar->bh); + arrange(bar->mon); + } else + drw_map(drw, bar->win, 0, 0, bar->bw, bar->bh); +} + +void +expose(XEvent *e) +{ + Monitor *m; + XExposeEvent *ev = &e->xexpose; + + if (ev->count == 0 && (m = wintomon(ev->window))) { + drawbar(m); + } +} + +void +focus(Client *c) +{ + if (!c || !ISVISIBLE(c)) + for (c = selmon->stack; c && !ISVISIBLE(c); c = c->snext); + if (selmon->sel && selmon->sel != c) + unfocus(selmon->sel, 0, c); + if (c) { + if (c->mon != selmon) + selmon = c->mon; + if (c->isurgent) + seturgent(c, 0); + detachstack(c); + attachstack(c); + grabbuttons(c, 1); + if (c->scratchkey != 0 && c->isfloating) + XSetWindowBorder(dpy, c->win, scheme[SchemeScratchSel][ColFloat].pixel); + else if (c->scratchkey != 0) + XSetWindowBorder(dpy, c->win, scheme[SchemeScratchSel][ColBorder].pixel); + else if (c->isfloating) + XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColFloat].pixel); + else + XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel); + setfocus(c); + } else { + XSetInputFocus(dpy, selmon->bar && selmon->bar->win ? selmon->bar->win : 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; + Client *sel; + + if (!mons->next) + return; + if ((m = dirtomon(arg->i)) == selmon) + return; + sel = selmon->sel; + selmon = m; + unfocus(sel, 0, NULL); + focus(NULL); +} + +Atom +getatomprop(Client *c, Atom prop, Atom req) +{ + int di; + unsigned long dl; + unsigned char *p = NULL; + Atom da, atom = None; + + /* FIXME getatomprop should return the number of items and a pointer to + * the stored data instead of this workaround */ + if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, req, + &da, &di, &dl, &dl, &p) == Success && p) { + atom = *(Atom *)p; + XFree(p); + } + return atom; +} + +int +getrootptr(int *x, int *y) +{ + int di; + unsigned int dui; + Window dummy; + + return XQueryPointer(dpy, root, &dummy, &dummy, x, y, &di, &di, &dui); +} + +long +getstate(Window w) +{ + int format; + long result = -1; + unsigned char *p = NULL; + unsigned long n, extra; + Atom real; + + if (XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState], + &real, &format, &n, &extra, (unsigned char **)&p) != Success) + return -1; + if (n != 0) + result = *p; + XFree(p); + return result; +} + +int +gettextprop(Window w, Atom atom, char *text, unsigned int size) +{ + char **list = NULL; + int n; + XTextProperty name; + + if (!text || size == 0) + return 0; + text[0] = '\0'; + if (!XGetTextProperty(dpy, w, &name, atom) || !name.nitems) + return 0; + if (name.encoding == XA_STRING) { + strncpy(text, (char *)name.value, size - 1); + } else if (XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success && n > 0 && *list) { + strncpy(text, *list, size - 1); + XFreeStringList(list); + } + text[size - 1] = '\0'; + XFree(name.value); + return 1; +} + +void +grabbuttons(Client *c, int focused) +{ + updatenumlockmask(); + { + unsigned int i, j; + unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; + XUngrabButton(dpy, AnyButton, AnyModifier, c->win); + if (!focused) + XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, + BUTTONMASK, GrabModeSync, GrabModeSync, None, None); + for (i = 0; i < LENGTH(buttons); i++) + if (buttons[i].click == ClkClientWin + ) + for (j = 0; j < LENGTH(modifiers); j++) + XGrabButton(dpy, buttons[i].button, + buttons[i].mask | modifiers[j], + c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + } +} + +void +grabkeys(void) +{ + updatenumlockmask(); + { + unsigned int i, j, k; + unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; + int start, end, skip; + KeySym *syms; + + XUngrabKey(dpy, AnyKey, AnyModifier, root); + XDisplayKeycodes(dpy, &start, &end); + syms = XGetKeyboardMapping(dpy, start, end - start + 1, &skip); + if (!syms) + return; + for (k = start; k <= end; k++) + for (i = 0; i < LENGTH(keys); i++) + /* skip modifier codes, we do that ourselves */ + if (keys[i].keysym == syms[(k - start) * skip]) + for (j = 0; j < LENGTH(modifiers); j++) + XGrabKey(dpy, k, + keys[i].mod | modifiers[j], + root, True, + GrabModeAsync, GrabModeAsync); + XFree(syms); + } +} + +void +incnmaster(const Arg *arg) +{ + selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag] = 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; + int keysyms_return; + KeySym* keysym; + XKeyEvent *ev; + + ev = &e->xkey; + keysym = XGetKeyboardMapping(dpy, (KeyCode)ev->keycode, 1, &keysyms_return); + 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)); + XFree(keysym); +} + +void +killclient(const Arg *arg) +{ + if (!selmon->sel) + return; + if (!sendevent(selmon->sel, wmatom[WMDelete])) + { + XGrabServer(dpy); + XSetErrorHandler(xerrordummy); + XSetCloseDownMode(dpy, DestroyAll); + XKillClient(dpy, selmon->sel->win); + XSync(dpy, False); + XSetErrorHandler(xerror); + XUngrabServer(dpy); + } +} + +void +manage(Window w, XWindowAttributes *wa) +{ + Client *c, *t = NULL; + int settings_restored; + Window trans = None; + XWindowChanges wc; + + c = ecalloc(1, sizeof(Client)); + c->win = w; + /* geometry */ + c->sfx = c->sfy = c->sfw = c->sfh = -9999; + 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; + settings_restored = restoreclientstate(c); + updatetitle(c); + + if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) { + c->mon = t->mon; + c->tags = t->tags; + c->bw = borderpx; + c->x = t->x + WIDTH(t) / 2 - WIDTH(c) / 2; + c->y = t->y + HEIGHT(t) / 2 - HEIGHT(c) / 2; + } else { + if (!settings_restored || c->mon == NULL) { + c->mon = selmon; + settings_restored = 0; + } + if (c->x == c->mon->wx && c->y == c->mon->wy) + c->iscentered = 1; + c->bw = borderpx; + if (!settings_restored) + applyrules(c); + } + + if (c->x + WIDTH(c) > c->mon->wx + c->mon->ww) + c->x = c->mon->wx + c->mon->ww - WIDTH(c); + if (c->y + HEIGHT(c) > c->mon->wy + c->mon->wh) + c->y = c->mon->wy + c->mon->wh - HEIGHT(c); + c->x = MAX(c->x, c->mon->wx); + c->y = MAX(c->y, c->mon->wy); + + wc.border_width = c->bw; + XConfigureWindow(dpy, w, CWBorderWidth, &wc); + if (c->isfloating) + XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColFloat].pixel); + else + XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel); + configure(c); /* propagates border_width, if size doesn't change */ + updatesizehints(c); + updatewmhints(c); + + if (c->iscentered) { + c->sfx = c->x = c->mon->wx + (c->mon->ww - WIDTH(c)) / 2; + c->sfy = c->y = c->mon->wy + (c->mon->wh - HEIGHT(c)) / 2; + } + if (c->sfw == -9999) { + c->sfw = c->w; + c->sfh = c->h; + } + + if (getatomprop(c, netatom[NetWMState], XA_ATOM) == netatom[NetWMFullscreen]) + setfullscreen(c, 1); + + 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); + XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColFloat].pixel); + } + attachx(c); + attachstack(c); + XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, + (unsigned char *) &(c->win), 1); + XChangeProperty(dpy, root, netatom[NetClientListStacking], XA_WINDOW, 32, PropModePrepend, + (unsigned char *) &(c->win), 1); + XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */ + + setclientstate(c, NormalState); + if (c->mon == selmon) + unfocus(selmon->sel, 0, c); + c->mon->sel = c; + arrange(c->mon); + XMapWindow(dpy, c->win); + focus(NULL); + + setfloatinghint(c); +} + +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 +motionnotify(XEvent *e) +{ + Bar *bar; + XMotionEvent *ev = &e->xmotion; + + if ((bar = wintobar(ev->window))) { + barhover(e, bar); + return; + } + +} + +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); + nx = ocx = c->x; + ny = ocy = c->y; + if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, + None, cursor[CurMove]->cursor, CurrentTime) != GrabSuccess) + return; + if (!getrootptr(&x, &y)) + return; + ignoreconfigurerequests = 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; + + 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)) { + c->sfx = -9999; // disable savefloats when using movemouse + 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); + } + /* save last known float coordinates */ + if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) { + c->sfx = nx; + c->sfy = ny; + } + ignoreconfigurerequests = 0; +} + +Client * +nexttiled(Client *c) +{ + for (; c && (c->isfloating || !ISVISIBLE(c)); c = c->next); + return c; +} + +void +pop(Client *c) +{ + detach(c); + attach(c); + focus(c); + arrange(c->mon); +} + +void +propertynotify(XEvent *e) +{ + Client *c; + Window trans; + XPropertyEvent *ev = &e->xproperty; + + if ((ev->window == root) && (ev->atom == XA_WM_NAME)) { + updatestatus(); + } else if (ev->state == PropertyDelete) { + return; /* ignore */ + } else if ((c = wintoclient(ev->window))) { + switch(ev->atom) { + default: break; + case XA_WM_TRANSIENT_FOR: + if (!c->isfloating && (XGetTransientForHint(dpy, c->win, &trans)) && + (c->isfloating = (wintoclient(trans)) != NULL)) + arrange(c->mon); + break; + case XA_WM_NORMAL_HINTS: + c->hintsvalid = 0; + break; + case XA_WM_HINTS: + updatewmhints(c); + if (c->isurgent) + drawbars(); + break; + } + if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { + updatetitle(c); + if (c == c->mon->sel) + drawbar(c->mon); + } + } +} + +void +quit(const Arg *arg) +{ + restart = arg->i; + 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, nx, ny; + int opx, opy, och, ocw; + int horizcorner, vertcorner; + unsigned int dui; + Window dummy; + 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); + nx = ocx = c->x; + ny = ocy = c->y; + nh = c->h; + nw = c->w; + och = c->h; + ocw = c->w; + if (!XQueryPointer(dpy, c->win, &dummy, &dummy, &opx, &opy, &nx, &ny, &dui)) + return; + horizcorner = nx < c->w / 2; + vertcorner = ny < c->h / 2; + if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, + None, cursor[horizcorner | (vertcorner << 1)]->cursor, CurrentTime) != GrabSuccess) + return; + ignoreconfigurerequests = 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; + + nx = horizcorner ? (ocx + ev.xmotion.x - opx) : c->x; + ny = vertcorner ? (ocy + ev.xmotion.y - opy) : c->y; + nw = MAX(horizcorner ? (ocx + ocw - nx) : (ocw + (ev.xmotion.x - opx)), 1); + nh = MAX(vertcorner ? (ocy + och - ny) : (och + (ev.xmotion.y - opy)), 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)) { + c->sfx = -9999; // disable savefloats when using resizemouse + togglefloating(NULL); + } + } + if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) { + resize(c, nx, ny, nw, nh, 1); + } + break; + } + } while (ev.type != ButtonRelease); + + 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); + } + /* save last known float dimensions */ + if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) { + c->sfx = nx; + c->sfy = ny; + c->sfw = nw; + c->sfh = nh; + } + ignoreconfigurerequests = 0; +} + +void +restack(Monitor *m) +{ + Client *c, *f = NULL; + 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; + if (m->bar) { + wc.sibling = m->bar->win; + } else { + for (f = m->stack; f && (f->isfloating || !ISVISIBLE(f)); f = f->snext); // find first tiled stack client + if (f) + wc.sibling = f->win; + } + for (c = m->stack; c; c = c->snext) + if (!c->isfloating && ISVISIBLE(c) && c != f) { + XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc); + wc.sibling = c->win; + } + } + XSync(dpy, False); + while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); +} + +void +run(void) +{ + XEvent ev; + XSync(dpy, False); + /* main event loop */ + while (running) { + struct pollfd pfd = { + .fd = ConnectionNumber(dpy), + .events = POLLIN, + }; + int pending = XPending(dpy) > 0 || poll(&pfd, 1, -1) > 0; + + if (!running) + break; + if (!pending) + continue; + + XNextEvent(dpy, &ev); + if (handler[ev.type]) + handler[ev.type](&ev); /* call handler */ + } +} + +void +scan(void) +{ + unsigned int i, num; + Window d1, d2, *wins = NULL; + XWindowAttributes wa; + + if (XQueryTree(dpy, root, &d1, &d2, &wins, &num)) { + for (i = 0; i < num; i++) { + if (!XGetWindowAttributes(dpy, wins[i], &wa) + || wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1)) + continue; + if (wa.map_state == IsViewable || getstate(wins[i]) == IconicState) + manage(wins[i], &wa); + } + for (i = 0; i < num; i++) { /* now the transients */ + if (!XGetWindowAttributes(dpy, wins[i], &wa)) + continue; + if (XGetTransientForHint(dpy, wins[i], &d1) + && (wa.map_state == IsViewable || getstate(wins[i]) == IconicState)) + manage(wins[i], &wa); + } + XFree(wins); + } +} + +void +sendmon(Client *c, Monitor *m) +{ + if (c->mon == m) + return; + int hadfocus = (c == selmon->sel); + unfocus(c, 1, NULL); + detach(c); + detachstack(c); + arrange(c->mon); + c->mon = m; + c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ + attachx(c); + attachstack(c); + arrange(m); + if (hadfocus) { + focus(c); + restack(m); + } else { + focus(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->oldbw = c->bw; + c->oldstate = c->isfloating; + 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->bw = c->oldbw; + c->isfloating = c->oldstate; + c->x = c->oldx; + c->y = c->oldy; + c->w = c->oldw; + c->h = c->oldh; + resizeclient(c, c->x, c->y, c->w, c->h); + arrange(c->mon); + } +} + +void +setlayout(const Arg *arg) +{ + if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) { + selmon->pertag->sellts[selmon->pertag->curtag] ^= 1; + selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; + } + if (arg && arg->v) + selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt] = (Layout *)arg->v; + selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; + + 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 = selmon->pertag->mfacts[selmon->pertag->curtag] = 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); + + /* the one line of bloat that would have saved a lot of time for a lot of people */ + putenv("_JAVA_AWT_WM_NONREPARENTING=1"); + + /* 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_font_create(drw, font)) + 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); + wmatom[WMWindowRole] = XInternAtom(dpy, "WM_WINDOW_ROLE", False); + clientatom[ClientFields] = XInternAtom(dpy, "_DWM_CLIENT_FIELDS", False); + clientatom[ClientTags] = XInternAtom(dpy, "_DWM_CLIENT_TAGS", False); + netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); + netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); + netatom[NetDesktopViewport] = XInternAtom(dpy, "_NET_DESKTOP_VIEWPORT", False); + netatom[NetNumberOfDesktops] = XInternAtom(dpy, "_NET_NUMBER_OF_DESKTOPS", False); + netatom[NetCurrentDesktop] = XInternAtom(dpy, "_NET_CURRENT_DESKTOP", False); + netatom[NetDesktopNames] = XInternAtom(dpy, "_NET_DESKTOP_NAMES", 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[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False); + netatom[NetClientListStacking] = XInternAtom(dpy, "_NET_CLIENT_LIST_STACKING", False); + /* init cursors */ + cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr); + cursor[CurResize] = drw_cur_create(drw, XC_sizing); + cursor[CurResizeBR] = drw_cur_create(drw, XC_bottom_right_corner); + cursor[CurResizeBL] = drw_cur_create(drw, XC_bottom_left_corner); + cursor[CurResizeTR] = drw_cur_create(drw, XC_top_right_corner); + cursor[CurResizeTL] = drw_cur_create(drw, XC_top_left_corner); + 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], ColCount); + + 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); + setnumdesktops(); + setcurrentdesktop(); + setdesktopnames(); + setviewport(); + XDeleteProperty(dpy, root, netatom[NetClientList]); + XDeleteProperty(dpy, root, netatom[NetClientListStacking]); + /* select events */ + wa.cursor = cursor[CurNormal]->cursor; + wa.event_mask = SubstructureRedirectMask|SubstructureNotifyMask + |ButtonPressMask|PointerMotionMask|EnterWindowMask + |LeaveWindowMask|StructureNotifyMask|PropertyChangeMask; + XChangeWindowAttributes(dpy, root, CWEventMask|CWCursor, &wa); + XSelectInput(dpy, root, wa.event_mask); + + grabkeys(); + focus(NULL); +} + +void +seturgent(Client *c, int urg) +{ + XWMHints *wmh; + + c->isurgent = urg; + if (!(wmh = XGetWMHints(dpy, c->win))) + return; + wmh->flags = urg ? (wmh->flags | XUrgencyHint) : (wmh->flags & ~XUrgencyHint); + XSetWMHints(dpy, c->win, wmh); + XFree(wmh); +} + +void +showhide(Client *c) +{ + if (!c) + return; + if (ISVISIBLE(c)) { + /* show clients top down */ + if (!c->mon->lt[c->mon->sellt]->arrange && c->sfx != -9999 && !c->isfullscreen) { + XMoveResizeWindow(dpy, c->win, c->sfx, c->sfy, c->sfw, c->sfh); + resize(c, c->sfx, c->sfy, c->sfw, c->sfh, 0); + showhide(c->snext); + return; + } + 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 +sigchld(int unused) +{ + pid_t pid; + + if (signal(SIGCHLD, sigchld) == SIG_ERR) + die("can't install SIGCHLD handler:"); + + while (0 < (pid = waitpid(-1, NULL, WNOHANG))) { + pid_t *p, *lim; + + if (!(p = autostart_pids)) + continue; + lim = &p[autostart_len]; + + for (; p < lim; p++) { + if (*p == pid) { + *p = -1; + break; + } + } + } +} + +void +spawn(const Arg *arg) +{ + struct sigaction sa; + + if (fork() == 0) + { + if (dpy) + close(ConnectionNumber(dpy)); + + setsid(); + + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sa.sa_handler = SIG_DFL; + sigaction(SIGCHLD, &sa, NULL); + + execvp(((char **)arg->v)[0], (char **)arg->v); + die("dwm: execvp '%s' failed:", ((char **)arg->v)[0]); + } +} + +void +tag(const Arg *arg) +{ + + if (selmon->sel && arg->ui & TAGMASK) { + selmon->sel->tags = arg->ui & TAGMASK; + arrange(selmon); + focus(NULL); + } +} + +void +tagmon(const Arg *arg) +{ + Client *c = selmon->sel; + Monitor *dest; + int restored; + if (!c || !mons->next) + return; + dest = dirtomon(arg->i); + savewindowfloatposition(c, c->mon); + restored = restorewindowfloatposition(c, dest); + if (restored && (!dest->lt[dest->sellt]->arrange || c->isfloating)) { + XMoveResizeWindow(dpy, c->win, c->sfx, c->sfy, c->sfw, c->sfh); + resize(c, c->sfx, c->sfy, c->sfw, c->sfh, 1); + } + if (c->isfullscreen) { + c->isfullscreen = 0; + sendmon(c, dest); + c->isfullscreen = 1; + resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); + XRaiseWindow(dpy, c->win); + } else if (c->isfloating && c->scratchkey != 0) { + sendmon(c, dest); + applyrules(c); + XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); + } else + sendmon(c, dest); +} + +void +togglebar(const Arg *arg) +{ + Bar *bar; + selmon->showbar = !selmon->showbar; + updatebarpos(selmon); + for (bar = selmon->bar; bar; bar = bar->next) + XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh); + arrange(selmon); +} + +void +togglefloating(const Arg *arg) +{ + Client *c = selmon->sel; + if (arg && arg->v) + c = (Client*)arg->v; + if (!c) + return; + if (c->isfullscreen) /* no support for fullscreen windows */ + return; + c->isfloating = !c->isfloating || c->isfixed; + if (c->isfloating) + XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColFloat].pixel); + else + XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel); + if (c->isfloating) { + if (c->sfx != -9999) { + /* restore last known float dimensions */ + resize(c, c->sfx, c->sfy, c->sfw, c->sfh, 0); + } else + resize(c, c->x, c->y, c->w, c->h, 0); + } else { + /* save last known float dimensions */ + c->sfx = c->x; + c->sfy = c->y; + c->sfw = c->w; + c->sfh = c->h; + } + arrange(c->mon); + + setfloatinghint(c); +} + +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; + arrange(selmon); + focus(NULL); + } + updatecurrentdesktop(); +} + +void +toggleview(const Arg *arg) +{ + unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK);; + int i; + + if (newtagset) { + selmon->tagset[selmon->seltags] = newtagset; + + if (newtagset == ~0) + { + selmon->pertag->curtag = 0; + } + /* test if the user did not select the same tag */ + if (!(newtagset & 1 << (selmon->pertag->curtag - 1))) { + for (i = 0; !(newtagset & 1 << i); i++) ; + selmon->pertag->curtag = i + 1; + } + + /* apply settings for this view */ + selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag]; + selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag]; + selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; + selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; + selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1]; + arrange(selmon); + focus(NULL); + } + updatecurrentdesktop(); +} + +void +unfocus(Client *c, int setfocus, Client *nextfocus) +{ + if (!c) + return; + if (c->isfullscreen && ISVISIBLE(c) && c->mon == selmon && nextfocus && !nextfocus->isfloating) { + setfullscreen(c, 0); + } + grabbuttons(c, 0); + if (c->scratchkey != 0 && c->isfloating) + XSetWindowBorder(dpy, c->win, scheme[SchemeScratchNorm][ColFloat].pixel); + else if (c->scratchkey != 0) + XSetWindowBorder(dpy, c->win, scheme[SchemeScratchNorm][ColBorder].pixel); + else if (c->isfloating) + XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColFloat].pixel); + else + 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; + XWindowChanges wc; + + m = c->mon; + + 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); + 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) +{ + Bar *bar; + 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) { + for (bar = m->bar; bar; bar = bar->next) { + if (bar->external) + continue; + if (!bar->win) { + bar->win = XCreateWindow(dpy, root, bar->bx, bar->by, bar->bw, bar->bh, 0, DefaultDepth(dpy, screen), + CopyFromParent, DefaultVisual(dpy, screen), + CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); + XDefineCursor(dpy, bar->win, cursor[CurNormal]->cursor); + XMapRaised(dpy, bar->win); + XSetClassHint(dpy, bar->win, &ch); + } + } + } +} + +void +updatebarpos(Monitor *m) +{ + + m->wx = m->mx; + m->wy = m->my; + m->ww = m->mw; + m->wh = m->mh; + Bar *bar; + int y_pad = 0; + int x_pad = 0; + + for (bar = m->bar; bar; bar = bar->next) { + bar->bx = m->wx + x_pad; + bar->bw = m->ww - 2 * x_pad; + } + + for (bar = m->bar; bar; bar = bar->next) + if (!m->showbar || !bar->showbar) + bar->by = -bar->bh - y_pad; + + if (!m->showbar) + return; + for (bar = m->bar; bar; bar = bar->next) { + if (!bar->showbar) + continue; + if (bar->topbar) + m->wy = m->wy + bar->bh + y_pad; + m->wh -= y_pad + bar->bh; + bar->by = (bar->topbar ? m->wy - bar->bh : m->wy + m->wh); + } +} + +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); + + XDeleteProperty(dpy, root, netatom[NetClientListStacking]); + for (m = mons; m; m = m->next) + for (c = m->stack; c; c = c->snext) + XChangeProperty(dpy, root, netatom[NetClientListStacking], + 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 = 0; + 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; + if (size.flags & PSize) + { + c->basew = size.base_width; + c->baseh = size.base_height; + c->isfloating = 1; + } + checkfloatingrules(c); + c->isfixed = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh); + c->hintsvalid = 1; +} + +void +updatestatus(void) +{ + Monitor *m; + if (!gettextprop(root, XA_WM_NAME, rawstext, sizeof(rawstext))) + strcpy(stext, "dwm-"VERSION); + else + copyvalidchars(stext, rawstext); + for (m = mons; m; m = m->next) + drawbar(m); +} + +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 +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 (c->isurgent) { + if (c->isfloating) + XSetWindowBorder(dpy, c->win, scheme[SchemeUrg][ColFloat].pixel); + else + XSetWindowBorder(dpy, c->win, scheme[SchemeUrg][ColBorder].pixel); + } + 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; + pertagview(arg); + arrange(selmon); + focus(NULL); + updatecurrentdesktop(); +} + +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; + Bar *bar; + + if (w == root && getrootptr(&x, &y)) + return recttomon(x, y, 1, 1); + for (m = mons; m; m = m->next) + for (bar = m->bar; bar; bar = bar->next) + if (w == bar->win) + return m; + if ((c = wintoclient(w))) + return c->mon; + return selmon; +} + +/* There's no way to check accesses to destroyed windows, thus those cases are + * ignored (especially on UnmapNotify's). Other types of errors call Xlibs + * default error handler, which may call exit. */ +int +xerror(Display *dpy, XErrorEvent *ee) +{ + if (ee->error_code == BadWindow + || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch) + || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable) + || (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable) + || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable) + || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch) + || (ee->request_code == X_GrabButton && ee->error_code == BadAccess) + || (ee->request_code == X_GrabKey && ee->error_code == BadAccess) + || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable)) + return 0; + fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n", + ee->request_code, ee->error_code); + return xerrorxlib(dpy, ee); /* may call exit */ +} + +int +xerrordummy(Display *dpy, XErrorEvent *ee) +{ + return 0; +} + +/* Startup Error handler to check if another window manager + * is already running. */ +int +xerrorstart(Display *dpy, XErrorEvent *ee) +{ + die("dwm: another window manager is already running"); + return -1; +} + +void +zoom(const Arg *arg) +{ + Client *c = selmon->sel; + if (arg && arg->v) + c = (Client*)arg->v; + if (!c) + return; + + if (!c->mon->lt[c->mon->sellt]->arrange || !c || c->isfloating) + return; + + if (c == nexttiled(selmon->clients) && !(c = nexttiled(c->next))) + return; + pop(c); +} + +int +main(int argc, char *argv[]) +{ + if (argc == 2 && !strcmp("-v", argv[1])) + die("dwm-"VERSION); + else if (argc != 1) + die("usage: dwm [-v]"); + if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) + fputs("warning: no locale support\n", stderr); + if (!(dpy = XOpenDisplay(NULL))) + die("dwm: cannot open display"); + checkotherwm(); + autostart_exec(); + setup(); +#ifdef __OpenBSD__ + if (pledge("stdio rpath proc exec", NULL) == -1) + die("pledge"); +#endif /* __OpenBSD__ */ + scan(); + run(); + cleanup(); + XCloseDisplay(dpy); + if (restart) + execvp(argv[0], argv); + return EXIT_SUCCESS; +} + diff --git a/dwm.desktop b/dwm.desktop new file mode 100644 index 0000000..b0c3354 --- /dev/null +++ b/dwm.desktop @@ -0,0 +1,7 @@ +[Desktop Entry] +Encoding=UTF-8 +Name=Dwm +Comment=Dynamic window manager +Exec=dwm +Icon=dwm +Type=XSession diff --git a/dwm.png b/dwm.png new file mode 100644 index 0000000..b1f9ba7 Binary files /dev/null and b/dwm.png differ diff --git a/flexipatch-finalizer b/flexipatch-finalizer deleted file mode 160000 index 37e8cb6..0000000 --- a/flexipatch-finalizer +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 37e8cb6145f189b805aa58c7d560b1adf51377a7 diff --git a/patch/attachx.c b/patch/attachx.c new file mode 100644 index 0000000..bdd99aa --- /dev/null +++ b/patch/attachx.c @@ -0,0 +1,28 @@ +void +attachx(Client *c) +{ + Client *at; + + if (c->idx > 0) { /* then the client has a designated position in the client list */ + for (at = c->mon->clients; at; at = at->next) { + if (c->idx < at->idx) { + c->next = at; + c->mon->clients = c; + return; + } else if (at->idx <= c->idx && (!at->next || c->idx <= at->next->idx)) { + c->next = at->next; + at->next = c; + return; + } + } + } + + for (at = c->mon->clients; at && at->next; at = at->next); + if (at) { + at->next = c; + c->next = NULL; + return; + } + attach(c); // master (default) +} + diff --git a/patch/attachx.h b/patch/attachx.h new file mode 100644 index 0000000..e522d27 --- /dev/null +++ b/patch/attachx.h @@ -0,0 +1,2 @@ +static void attachx(Client *c); + diff --git a/patch/bar.c b/patch/bar.c new file mode 100644 index 0000000..65e1a69 --- /dev/null +++ b/patch/bar.c @@ -0,0 +1,39 @@ +void +barhover(XEvent *e, Bar *bar) +{ + const BarRule *br; + Monitor *m = bar->mon; + XMotionEvent *ev = &e->xmotion; + BarArg barg = { 0, 0, 0, 0 }; + int r; + + for (r = 0; r < LENGTH(barrules); r++) { + br = &barrules[r]; + if (br->bar != bar->idx || (br->monitor == 'A' && m != selmon) || br->hoverfunc == NULL) + continue; + if (br->monitor != 'A' && br->monitor != -1 && br->monitor != bar->mon->num) + continue; + if (bar->x[r] > ev->x || ev->x > bar->x[r] + bar->w[r]) + continue; + + barg.x = ev->x - bar->x[r]; + barg.y = ev->y - bar->borderpx; + barg.w = bar->w[r]; + barg.h = bar->bh - 2 * bar->borderpx; + + br->hoverfunc(bar, &barg, ev); + break; + } +} + +Bar * +wintobar(Window win) +{ + Monitor *m; + Bar *bar; + for (m = mons; m; m = m->next) + for (bar = m->bar; bar; bar = bar->next) + if (bar->win == win) + return bar; + return NULL; +} diff --git a/patch/bar.h b/patch/bar.h new file mode 100644 index 0000000..3e006dc --- /dev/null +++ b/patch/bar.h @@ -0,0 +1,2 @@ +static void barhover(XEvent *e, Bar *bar); +static Bar *wintobar(Window win); diff --git a/patch/bar_dwmblocks.c b/patch/bar_dwmblocks.c new file mode 100644 index 0000000..0a4b097 --- /dev/null +++ b/patch/bar_dwmblocks.c @@ -0,0 +1,41 @@ +static int statussig; +pid_t statuspid = -1; + +pid_t +getstatusbarpid() +{ + char buf[32], *str = buf, *c; + FILE *fp; + + if (statuspid > 0) { + snprintf(buf, sizeof(buf), "/proc/%u/cmdline", statuspid); + if ((fp = fopen(buf, "r"))) { + fgets(buf, sizeof(buf), fp); + while ((c = strchr(str, '/'))) + str = c + 1; + fclose(fp); + if (!strcmp(str, STATUSBAR)) + return statuspid; + } + } + if (!(fp = popen("pgrep -o "STATUSBAR, "r"))) + return -1; + fgets(buf, sizeof(buf), fp); + pclose(fp); + return strtol(buf, NULL, 10); +} + +void +sigstatusbar(const Arg *arg) +{ + union sigval sv; + + if (!statussig) + return; + if ((statuspid = getstatusbarpid()) <= 0) + return; + + sv.sival_int = arg->i; + sigqueue(statuspid, SIGRTMIN+statussig, sv); +} + diff --git a/patch/bar_dwmblocks.h b/patch/bar_dwmblocks.h new file mode 100644 index 0000000..4db9467 --- /dev/null +++ b/patch/bar_dwmblocks.h @@ -0,0 +1,3 @@ +static int getstatusbarpid(); +static void sigstatusbar(const Arg *arg); + diff --git a/patch/bar_ewmhtags.c b/patch/bar_ewmhtags.c new file mode 100644 index 0000000..46ee5e4 --- /dev/null +++ b/patch/bar_ewmhtags.c @@ -0,0 +1,52 @@ +void +setcurrentdesktop(void) +{ + long data[] = { 0 }; + XChangeProperty(dpy, root, netatom[NetCurrentDesktop], XA_CARDINAL, 32, PropModeReplace, (unsigned char *)data, 1); +} + +void +setdesktopnames(void) +{ + int i; + XTextProperty text; + char *tags[NUMTAGS]; + for (i = 0; i < NUMTAGS; i++) + tags[i] = tagicon(selmon, i); + Xutf8TextListToTextProperty(dpy, tags, NUMTAGS, XUTF8StringStyle, &text); + XSetTextProperty(dpy, root, &text, netatom[NetDesktopNames]); +} + +void +setfloatinghint(Client *c) +{ + Atom target = XInternAtom(dpy, "_IS_FLOATING", 0); + unsigned int floating[1] = {c->isfloating}; + XChangeProperty(dpy, c->win, target, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)floating, 1); +} + +void +setnumdesktops(void) +{ + long data[] = { NUMTAGS }; + XChangeProperty(dpy, root, netatom[NetNumberOfDesktops], XA_CARDINAL, 32, PropModeReplace, (unsigned char *)data, 1); +} + +void +setviewport(void) +{ + long data[] = { 0, 0 }; + XChangeProperty(dpy, root, netatom[NetDesktopViewport], XA_CARDINAL, 32, PropModeReplace, (unsigned char *)data, 2); +} + +void +updatecurrentdesktop(void) +{ + long rawdata[] = { selmon->tagset[selmon->seltags] }; + int i = 0; + while (*rawdata >> (i + 1)) { + i++; + } + long data[] = { i }; + XChangeProperty(dpy, root, netatom[NetCurrentDesktop], XA_CARDINAL, 32, PropModeReplace, (unsigned char *)data, 1); +} diff --git a/patch/bar_ewmhtags.h b/patch/bar_ewmhtags.h new file mode 100644 index 0000000..4d6a74b --- /dev/null +++ b/patch/bar_ewmhtags.h @@ -0,0 +1,7 @@ +static void setcurrentdesktop(void); +static void setdesktopnames(void); +static void setfloatinghint(Client *c); +static void setnumdesktops(void); +static void setviewport(void); +static void updatecurrentdesktop(void); + diff --git a/patch/bar_indicators.c b/patch/bar_indicators.c new file mode 100644 index 0000000..b003fc8 --- /dev/null +++ b/patch/bar_indicators.c @@ -0,0 +1,104 @@ +/* Indicator properties, you can override these in your config.h if you want. */ +#ifndef TAGSINDICATOR +#define TAGSINDICATOR 1 // 0 = off, 1 = on if >1 client/view tag, 2 = always on +#endif +#ifndef TAGSPX +#define TAGSPX 5 // # pixels for tag grid boxes +#endif +#ifndef TAGSROWS +#define TAGSROWS 3 // # rows in tag grid (9 tags, e.g. 3x3) +#endif + +void +drawindicator(Monitor *m, Client *c, unsigned int occ, int x, int y, int w, int h, unsigned int tag, int filled, int invert, int type) +{ + int i, boxw, boxs, indn = 0; + if (!(occ & 1 << tag) || type == INDICATOR_NONE) + return; + + boxs = drw->fonts->h / 9; + boxw = drw->fonts->h / 6 + 2; + if (filled == -1) + filled = m == selmon && m->sel && m->sel->tags & 1 << tag; + + switch (type) { + default: + case INDICATOR_TOP_LEFT_SQUARE: + drw_rect(drw, x + boxs, y + boxs, boxw, boxw, filled, invert); + break; + case INDICATOR_TOP_LEFT_LARGER_SQUARE: + drw_rect(drw, x + boxs + 2, y + boxs+1, boxw+1, boxw+1, filled, invert); + break; + case INDICATOR_TOP_BAR: + drw_rect(drw, x + boxw, y, w - ( 2 * boxw + 1), boxw/2, filled, invert); + break; + case INDICATOR_TOP_BAR_SLIM: + drw_rect(drw, x + boxw, y, w - ( 2 * boxw + 1), 1, 0, invert); + break; + case INDICATOR_BOTTOM_BAR: + drw_rect(drw, x + boxw, y + h - boxw/2, w - ( 2 * boxw + 1), boxw/2, filled, invert); + break; + case INDICATOR_BOTTOM_BAR_SLIM: + drw_rect(drw, x + boxw, y + h - 1, w - ( 2 * boxw + 1), 1, 0, invert); + break; + case INDICATOR_BOX: + drw_rect(drw, x + boxw, y, w - 2 * boxw, h, 0, invert); + break; + case INDICATOR_BOX_WIDER: + drw_rect(drw, x + boxw/2, y, w - boxw, h, 0, invert); + break; + case INDICATOR_BOX_FULL: + drw_rect(drw, x, y, w - 2, h, 0, invert); + break; + case INDICATOR_CLIENT_DOTS: + for (c = m->clients; c; c = c->next) { + if (c->tags & (1 << tag)) { + drw_rect(drw, x, 1 + (indn * 2), m->sel == c ? 6 : 1, 1, 1, invert); + indn++; + } + if (h <= 1 + (indn * 2)) { + indn = 0; + x += 2; + } + } + break; + case INDICATOR_RIGHT_TAGS: + if (!c) + break; + for (i = 0; i < NUMTAGS; i++) { + drw_rect(drw, + ( x + w - 2 - ((NUMTAGS / TAGSROWS) * TAGSPX) + - (i % (NUMTAGS/TAGSROWS)) + ((i % (NUMTAGS / TAGSROWS)) * TAGSPX) + ), + ( y + 2 + ((i / (NUMTAGS/TAGSROWS)) * TAGSPX) + - ((i / (NUMTAGS/TAGSROWS))) + ), + TAGSPX, TAGSPX, (c->tags >> i) & 1, 0 + ); + } + break; + case INDICATOR_PLUS_AND_LARGER_SQUARE: + boxs += 2; + boxw += 2; + /* falls through */ + case INDICATOR_PLUS_AND_SQUARE: + drw_rect(drw, x + boxs, y + boxs, boxw % 2 ? boxw : boxw + 1, boxw % 2 ? boxw : boxw + 1, filled, invert); + /* falls through */ + case INDICATOR_PLUS: + if (!(boxw % 2)) + boxw += 1; + drw_rect(drw, x + boxs + boxw / 2, y + boxs, 1, boxw, filled, invert); // | + drw_rect(drw, x + boxs, y + boxs + boxw / 2, boxw + 1, 1, filled, invert); // ‒ + break; + } +} + +void +drawstateindicator(Monitor *m, Client *c, unsigned int occ, int x, int y, int w, int h, unsigned int tag, int filled, int invert) +{ + if (c->isfloating) + drawindicator(m, c, occ, x, y, w, h, tag, filled, invert, floatindicatortype); + else + drawindicator(m, c, occ, x, y, w, h, tag, filled, invert, tiledindicatortype); +} + diff --git a/patch/bar_indicators.h b/patch/bar_indicators.h new file mode 100644 index 0000000..c66e4f0 --- /dev/null +++ b/patch/bar_indicators.h @@ -0,0 +1,21 @@ +enum { + INDICATOR_NONE, + INDICATOR_TOP_LEFT_SQUARE, + INDICATOR_TOP_LEFT_LARGER_SQUARE, + INDICATOR_TOP_BAR, + INDICATOR_TOP_BAR_SLIM, + INDICATOR_BOTTOM_BAR, + INDICATOR_BOTTOM_BAR_SLIM, + INDICATOR_BOX, + INDICATOR_BOX_WIDER, + INDICATOR_BOX_FULL, + INDICATOR_CLIENT_DOTS, + INDICATOR_RIGHT_TAGS, + INDICATOR_PLUS, + INDICATOR_PLUS_AND_SQUARE, + INDICATOR_PLUS_AND_LARGER_SQUARE, +}; + +static void drawindicator(Monitor *m, Client *c, unsigned int occ, int x, int y, int w, int h, unsigned int tag, int filled, int invert, int type); +static void drawstateindicator(Monitor *m, Client *c, unsigned int occ, int x, int y, int w, int h, unsigned int tag, int filled, int invert); + diff --git a/patch/bar_ltsymbol.c b/patch/bar_ltsymbol.c new file mode 100644 index 0000000..1fbd1b8 --- /dev/null +++ b/patch/bar_ltsymbol.c @@ -0,0 +1,18 @@ +int +width_ltsymbol(Bar *bar, BarArg *a) +{ + return TEXTW(bar->mon->ltsymbol); +} + +int +draw_ltsymbol(Bar *bar, BarArg *a) +{ + return drw_text(drw, a->x, a->y, a->w, a->h, lrpad / 2, bar->mon->ltsymbol, 0, False); +} + +int +click_ltsymbol(Bar *bar, Arg *arg, BarArg *a) +{ + return ClkLtSymbol; +} + diff --git a/patch/bar_ltsymbol.h b/patch/bar_ltsymbol.h new file mode 100644 index 0000000..4de5720 --- /dev/null +++ b/patch/bar_ltsymbol.h @@ -0,0 +1,4 @@ +static int width_ltsymbol(Bar *bar, BarArg *a); +static int draw_ltsymbol(Bar *bar, BarArg *a); +static int click_ltsymbol(Bar *bar, Arg *arg, BarArg *a); + diff --git a/patch/bar_status.c b/patch/bar_status.c new file mode 100644 index 0000000..19ff219 --- /dev/null +++ b/patch/bar_status.c @@ -0,0 +1,18 @@ +int +width_status(Bar *bar, BarArg *a) +{ + return TEXTWM(stext); +} + +int +draw_status(Bar *bar, BarArg *a) +{ + return drw_text(drw, a->x, a->y, a->w, a->h, lrpad / 2, stext, 0, True); +} + +int +click_status(Bar *bar, Arg *arg, BarArg *a) +{ + return ClkStatusText; +} + diff --git a/patch/bar_status.h b/patch/bar_status.h new file mode 100644 index 0000000..c580597 --- /dev/null +++ b/patch/bar_status.h @@ -0,0 +1,4 @@ +static int width_status(Bar *bar, BarArg *a); +static int draw_status(Bar *bar, BarArg *a); +static int click_status(Bar *bar, Arg *arg, BarArg *a); + diff --git a/patch/bar_statuscmd.c b/patch/bar_statuscmd.c new file mode 100644 index 0000000..2e74f9f --- /dev/null +++ b/patch/bar_statuscmd.c @@ -0,0 +1,44 @@ +int +click_statuscmd(Bar *bar, Arg *arg, BarArg *a) +{ + return click_statuscmd_text(arg, a->x, rawstext); +} + +int +click_statuscmd_text(Arg *arg, int rel_x, char *text) +{ + int i = -1; + int x = 0; + char ch; + statussig = -1; + while (text[++i]) { + if ((unsigned char)text[i] < ' ') { + ch = text[i]; + text[i] = '\0'; + x += TEXTWM(text) - lrpad; + text[i] = ch; + text += i+1; + i = -1; + if (x >= rel_x && statussig != -1) + break; + statussig = ch; + } + } + if (statussig == -1) + statussig = 0; + return ClkStatusText; +} + +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'; +} + diff --git a/patch/bar_statuscmd.h b/patch/bar_statuscmd.h new file mode 100644 index 0000000..1ec24a6 --- /dev/null +++ b/patch/bar_statuscmd.h @@ -0,0 +1,9 @@ +static int click_statuscmd(Bar *bar, Arg *arg, BarArg *a); +static int click_statuscmd_text(Arg *arg, int rel_x, char *text); +static void copyvalidchars(char *text, char *rawtext); + +typedef struct { + const char *cmd; + int id; +} StatusCmd; + diff --git a/patch/bar_tagicons.c b/patch/bar_tagicons.c new file mode 100644 index 0000000..57d1629 --- /dev/null +++ b/patch/bar_tagicons.c @@ -0,0 +1,9 @@ +char * +tagicon(Monitor *m, int tag) +{ + int tagindex = tag + NUMTAGS * m->num; + if (tagindex >= LENGTH(tagicons[DEFAULT_TAGS])) + tagindex = tagindex % LENGTH(tagicons[DEFAULT_TAGS]); + return tagicons[DEFAULT_TAGS][tagindex]; +} + diff --git a/patch/bar_tagicons.h b/patch/bar_tagicons.h new file mode 100644 index 0000000..16fad2a --- /dev/null +++ b/patch/bar_tagicons.h @@ -0,0 +1,8 @@ +enum { + DEFAULT_TAGS, + ALTERNATIVE_TAGS, + ALT_TAGS_DECORATION, +}; + +static char * tagicon(Monitor *m, int tag); + diff --git a/patch/bar_tags.c b/patch/bar_tags.c new file mode 100644 index 0000000..8aa37b2 --- /dev/null +++ b/patch/bar_tags.c @@ -0,0 +1,81 @@ +int +width_tags(Bar *bar, BarArg *a) +{ + int w, i; + Client *c; + unsigned int occ = 0; + for (c = bar->mon->clients; c; c = c->next) + occ |= c->tags == 255 ? 0 : c->tags; + + for (w = 0, i = 0; i < NUMTAGS; i++) { + if (!(occ & 1 << i || bar->mon->tagset[bar->mon->seltags] & 1 << i)) + continue; + w += TEXTW(tagicon(bar->mon, i)); + } + return w; +} + +int +draw_tags(Bar *bar, BarArg *a) +{ + int invert; + int w, x = a->x; + unsigned int i, occ = 0, urg = 0; + char *icon; + Client *c; + Monitor *m = bar->mon; + + for (c = m->clients; c; c = c->next) { + occ |= c->tags == 255 ? 0 : c->tags; + if (c->isurgent) + urg |= c->tags; + } + for (i = 0; i < NUMTAGS; i++) { + /* do not draw vacant tags */ + if (!(occ & 1 << i || m->tagset[m->seltags] & 1 << i)) + continue; + + icon = tagicon(bar->mon, i); + invert = 0; + w = TEXTW(icon); + drw_setscheme(drw, scheme[ + m->tagset[m->seltags] & 1 << i + ? SchemeTagsSel + : urg & 1 << i + ? SchemeUrg + : SchemeTagsNorm + ]); + drw_text(drw, x, a->y, w, a->h, lrpad / 2, icon, invert, False); + drawindicator(m, NULL, occ, x, a->y, w, a->h, i, -1, invert, tagindicatortype); + x += w; + } + + return 1; +} + +int +click_tags(Bar *bar, Arg *arg, BarArg *a) +{ + int i = 0, x = 0; + Client *c; + unsigned int occ = 0; + for (c = bar->mon->clients; c; c = c->next) + occ |= c->tags == 255 ? 0 : c->tags; + + do { + if (!(occ & 1 << i || bar->mon->tagset[bar->mon->seltags] & 1 << i)) + continue; + x += TEXTW(tagicon(bar->mon, i)); + } while (a->x >= x && ++i < NUMTAGS); + if (i < NUMTAGS) { + arg->ui = 1 << i; + } + return ClkTagBar; +} + +int +hover_tags(Bar *bar, BarArg *a, XMotionEvent *ev) +{ + + return 1; +} diff --git a/patch/bar_tags.h b/patch/bar_tags.h new file mode 100644 index 0000000..70040d2 --- /dev/null +++ b/patch/bar_tags.h @@ -0,0 +1,4 @@ +static int width_tags(Bar *bar, BarArg *a); +static int draw_tags(Bar *bar, BarArg *a); +static int click_tags(Bar *bar, Arg *arg, BarArg *a); +static int hover_tags(Bar *bar, BarArg *a, XMotionEvent *ev); diff --git a/patch/bar_wintitle.c b/patch/bar_wintitle.c new file mode 100644 index 0000000..4468f93 --- /dev/null +++ b/patch/bar_wintitle.c @@ -0,0 +1,49 @@ +int +width_wintitle(Bar *bar, BarArg *a) +{ + return a->w; +} + +int +draw_wintitle(Bar *bar, BarArg *a) +{ + int x = a->x, w = a->w; + Monitor *m = bar->mon; + Client *c = m->sel; + + if (!c) { + drw_setscheme(drw, scheme[SchemeTitleNorm]); + drw_rect(drw, x, a->y, w, a->h, 1, 1); + return 0; + } + + int tpad = lrpad / 2; + int tx = x; + int tw = w; + + drw_setscheme(drw, scheme[m == selmon ? SchemeTitleSel : SchemeTitleNorm]); + XSetErrorHandler(xerrordummy); + + if (w <= TEXTW("A") - lrpad + tpad) // reduce text padding if wintitle is too small + tpad = (w - TEXTW("A") + lrpad < 0 ? 0 : (w - TEXTW("A") + lrpad) / 2); + + XSetForeground(drw->dpy, drw->gc, drw->scheme[ColBg].pixel); + XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, a->y, w, a->h); + + tx += tpad; + tw -= lrpad; + + drw_text(drw, tx, a->y, tw, a->h, 0, c->name, 0, False); + + XSync(dpy, False); + XSetErrorHandler(xerror); + drawstateindicator(m, c, 1, x, a->y, w, a->h, 0, 0, c->isfixed); + return 1; +} + +int +click_wintitle(Bar *bar, Arg *arg, BarArg *a) +{ + return ClkWinTitle; +} + diff --git a/patch/bar_wintitle.h b/patch/bar_wintitle.h new file mode 100644 index 0000000..7e8cce5 --- /dev/null +++ b/patch/bar_wintitle.h @@ -0,0 +1,4 @@ +static int width_wintitle(Bar *bar, BarArg *a); +static int draw_wintitle(Bar *bar, BarArg *a); +static int click_wintitle(Bar *bar, Arg *arg, BarArg *a); + diff --git a/patch/cool_autostart.c b/patch/cool_autostart.c new file mode 100644 index 0000000..848f5ab --- /dev/null +++ b/patch/cool_autostart.c @@ -0,0 +1,37 @@ +/* dwm will keep pid's of processes from autostart array and kill them at quit */ +static pid_t *autostart_pids; +static size_t autostart_len; + +/* execute command from autostart array */ +static void +autostart_exec() +{ + const char *const *p; + struct sigaction sa; + size_t i = 0; + + /* count entries */ + for (p = autostart; *p; autostart_len++, p++) + while (*++p); + + autostart_pids = malloc(autostart_len * sizeof(pid_t)); + for (p = autostart; *p; i++, p++) { + if ((autostart_pids[i] = fork()) == 0) { + setsid(); + + /* Restore SIGCHLD sighandler to default before spawning a program */ + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sa.sa_handler = SIG_DFL; + sigaction(SIGCHLD, &sa, NULL); + + execvp(*p, (char *const *)p); + fprintf(stderr, "dwm: execvp %s\n", *p); + perror(" failed"); + _exit(EXIT_FAILURE); + } + /* skip arguments */ + while (*++p); + } +} + diff --git a/patch/cool_autostart.h b/patch/cool_autostart.h new file mode 100644 index 0000000..5534d99 --- /dev/null +++ b/patch/cool_autostart.h @@ -0,0 +1,2 @@ +static void autostart_exec(void); + diff --git a/patch/floatpos.c b/patch/floatpos.c new file mode 100644 index 0000000..d935d23 --- /dev/null +++ b/patch/floatpos.c @@ -0,0 +1,179 @@ +void +floatpos(const Arg *arg) +{ + Client *c = selmon->sel; + + if (!c || (selmon->lt[selmon->sellt]->arrange && !c->isfloating)) + return; + + setfloatpos(c, (char *)arg->v); + resizeclient(c, c->x, c->y, c->w, c->h); + +} + +void +setfloatpos(Client *c, const char *floatpos) +{ + char xCh, yCh, wCh, hCh; + int x, y, w, h, wx, ww, wy, wh; + + if (!c || !floatpos) + return; + if (selmon->lt[selmon->sellt]->arrange && !c->isfloating) + return; + + switch(sscanf(floatpos, "%d%c %d%c %d%c %d%c", &x, &xCh, &y, &yCh, &w, &wCh, &h, &hCh)) { + case 4: + if (xCh == 'w' || xCh == 'W') { + w = x; wCh = xCh; + h = y; hCh = yCh; + x = -1; xCh = 'C'; + y = -1; yCh = 'C'; + } else if (xCh == 'p' || xCh == 'P') { + w = x; wCh = xCh; + h = y; hCh = yCh; + x = 0; xCh = 'G'; + y = 0; yCh = 'G'; + } else if (xCh == 'm' || xCh == 'M') { + getrootptr(&x, &y); + } else { + w = 0; wCh = 0; + h = 0; hCh = 0; + } + break; + case 8: + if (xCh == 'm' || xCh == 'M') + getrootptr(&x, &y); + break; + default: + return; + } + + wx = c->mon->wx; + wy = c->mon->wy; + ww = c->mon->ww; + wh = c->mon->wh; + + getfloatpos(x, xCh, w, wCh, wx, ww, c->x, c->w, c->bw, floatposgrid_x, &c->x, &c->w); + getfloatpos(y, yCh, h, hCh, wy, wh, c->y, c->h, c->bw, floatposgrid_y, &c->y, &c->h); +} + +/* p - position, s - size, cp and cs represents current position and size */ +void +getfloatpos(int pos, char pCh, int size, char sCh, int min_p, int max_s, int cp, int cs, int cbw, int defgrid, int *out_p, int *out_s) +{ + int abs_p, abs_s, i, delta, rest; + + abs_p = pCh == 'A' || pCh == 'a'; + abs_s = sCh == 'A' || sCh == 'a'; + + cs += 2*cbw; + + switch(pCh) { + case 'A': // absolute position + cp = pos; + break; + case 'a': // absolute relative position + cp += pos; + break; + case 'y': + case 'x': // client relative position + cp = MIN(cp + pos, min_p + max_s); + break; + case 'Y': + case 'X': // client position relative to monitor + cp = min_p + MIN(pos, max_s); + break; + case 'S': // fixed client position (sticky) + case 'C': // fixed client position (center) + case 'Z': // fixed client right-hand position (position + size) + if (pos == -1) + break; + pos = MAX(MIN(pos, max_s), 0); + if (pCh == 'Z') + cs = abs((cp + cs) - (min_p + pos)); + else if (pCh == 'C') + cs = abs((cp + cs / 2) - (min_p + pos)); + else + cs = abs(cp - (min_p + pos)); + cp = min_p + pos; + sCh = 0; // size determined by position, override defined size + break; + case 'G': // grid + if (pos <= 0) + pos = defgrid; // default configurable + if (size == 0 || pos < 2 || (sCh != 'p' && sCh != 'P')) + break; + delta = (max_s - cs) / (pos - 1); + rest = max_s - cs - delta * (pos - 1); + if (sCh == 'P') { + if (size < 1 || size > pos) + break; + cp = min_p + delta * (size - 1); + } else { + for (i = 0; i < pos && cp >= min_p + delta * i + (i > pos - rest ? i + rest - pos + 1 : 0); i++); + cp = min_p + delta * (MAX(MIN(i + size, pos), 1) - 1) + (i > pos - rest ? i + rest - pos + 1 : 0); + } + break; + } + + switch(sCh) { + case 'A': // absolute size + cs = size; + break; + case 'a': // absolute relative size + cs = MAX(1, cs + size); + break; + case '%': // client size percentage in relation to monitor window area size + if (size <= 0) + break; + size = max_s * MIN(size, 100) / 100; + /* falls through */ + case 'h': + case 'w': // size relative to client + if (sCh == 'w' || sCh == 'h') { + if (size == 0) + break; + size += cs; + } + /* falls through */ + case 'H': + case 'W': // normal size, position takes precedence + if (pCh == 'S' && cp + size > min_p + max_s) + size = min_p + max_s - cp; + else if (size > max_s) + size = max_s; + + if (pCh == 'C') { // fixed client center, expand or contract client + delta = size - cs; + if (delta < 0 || (cp - delta / 2 + size <= min_p + max_s)) + cp -= delta / 2; + else if (cp - delta / 2 < min_p) + cp = min_p; + else if (delta) + cp = min_p + max_s; + } else if (pCh == 'Z') + cp -= size - cs; + + cs = size; + break; + } + + if (pCh == '%') // client mid-point position in relation to monitor window area size + cp = min_p + max_s * MAX(MIN(pos, 100), 0) / 100 - (cs) / 2; + if (pCh == 'm' || pCh == 'M') + cp = pos - cs / 2; + + if (!abs_p && cp < min_p) + cp = min_p; + if (cp + cs > min_p + max_s && !(abs_p && abs_s)) { + if (abs_p || cp == min_p) + cs = min_p + max_s - cp; + else + cp = min_p + max_s - cs; + } + + *out_p = cp; + *out_s = MAX(cs - 2*cbw, 1); +} + diff --git a/patch/floatpos.h b/patch/floatpos.h new file mode 100644 index 0000000..ecdcf0b --- /dev/null +++ b/patch/floatpos.h @@ -0,0 +1,4 @@ +static void floatpos(const Arg *arg); +static void setfloatpos(Client *c, const char *floatpos); +static void getfloatpos(int pos, char pCh, int size, char sCh, int min_p, int max_s, int cp, int cs, int cbw, int defgrid, int *out_p, int *out_s); + diff --git a/patch/focusdir.c b/patch/focusdir.c new file mode 100644 index 0000000..65798c5 --- /dev/null +++ b/patch/focusdir.c @@ -0,0 +1,66 @@ +void +focusdir(const Arg *arg) +{ + Client *s = selmon->sel, *f = NULL, *c, *next; + + if (!s) + return; + + unsigned int score = -1; + unsigned int client_score; + int dist; + int dirweight = 20; + int isfloating = s->isfloating; + + next = s->next; + if (!next) + next = s->mon->clients; + for (c = next; c != s; c = next) { + + next = c->next; + if (!next) + next = s->mon->clients; + + if (!ISVISIBLE(c) || c->isfloating != isfloating) // || HIDDEN(c) + continue; + + switch (arg->i) { + case 0: // left + dist = s->x - c->x - c->w; + client_score = + dirweight * MIN(abs(dist), abs(dist + s->mon->ww)) + + abs(s->y - c->y); + break; + case 1: // right + dist = c->x - s->x - s->w; + client_score = + dirweight * MIN(abs(dist), abs(dist + s->mon->ww)) + + abs(c->y - s->y); + break; + case 2: // up + dist = s->y - c->y - c->h; + client_score = + dirweight * MIN(abs(dist), abs(dist + s->mon->wh)) + + abs(s->x - c->x); + break; + default: + case 3: // down + dist = c->y - s->y - s->h; + client_score = + dirweight * MIN(abs(dist), abs(dist + s->mon->wh)) + + abs(c->x - s->x); + break; + } + + if (((arg->i == 0 || arg->i == 2) && client_score <= score) || client_score < score) { + score = client_score; + f = c; + } + } + + if (f && f != s) { + focus(f); + restack(f->mon); + } +} + diff --git a/patch/focusdir.h b/patch/focusdir.h new file mode 100644 index 0000000..0d82ebf --- /dev/null +++ b/patch/focusdir.h @@ -0,0 +1,2 @@ +static void focusdir(const Arg *arg); + diff --git a/patch/include.c b/patch/include.c new file mode 100644 index 0000000..2676bd0 --- /dev/null +++ b/patch/include.c @@ -0,0 +1,35 @@ +/* Bar functionality */ +#include "bar_indicators.c" +#include "bar_tagicons.c" +#include "bar.c" + +#include "bar_dwmblocks.c" +#include "bar_ewmhtags.c" +#include "bar_ltsymbol.c" +#include "bar_status.c" +#include "bar_statuscmd.c" +#include "bar_tags.c" +#include "bar_wintitle.c" + +/* Other patches */ +#include "attachx.c" +#include "cool_autostart.c" +#include "floatpos.c" +#include "focusdir.c" +#include "pertag.c" +#include "restartsig.c" +#include "renamed_scratchpads.c" +#include "sizehints_ruled.c" +#include "stacker.c" +#include "togglefullscreen.c" +#include "unfloatvisible.c" +#include "seamless_restart.c" +/* Layouts */ +#include "layout_facts.c" +#include "layout_bstack.c" +#include "layout_centeredmaster.c" +#include "layout_deck.c" +#include "layout_gapplessgrid.c" +#include "layout_monocle.c" +#include "layout_tile.c" + diff --git a/patch/include.h b/patch/include.h new file mode 100644 index 0000000..d7cde24 --- /dev/null +++ b/patch/include.h @@ -0,0 +1,34 @@ +/* Bar functionality */ +#include "bar_indicators.h" +#include "bar_tagicons.h" +#include "bar.h" + +#include "bar_dwmblocks.h" +#include "bar_ewmhtags.h" +#include "bar_ltsymbol.h" +#include "bar_status.h" +#include "bar_statuscmd.h" +#include "bar_tags.h" +#include "bar_wintitle.h" + +/* Other patches */ +#include "attachx.h" +#include "cool_autostart.h" +#include "floatpos.h" +#include "focusdir.h" +#include "pertag.h" +#include "restartsig.h" +#include "renamed_scratchpads.h" +#include "seamless_restart.h" +#include "sizehints_ruled.h" +#include "stacker.h" +#include "togglefullscreen.h" +#include "unfloatvisible.h" +/* Layouts */ +#include "layout_bstack.h" +#include "layout_centeredmaster.h" +#include "layout_deck.h" +#include "layout_gapplessgrid.h" +#include "layout_monocle.h" +#include "layout_tile.h" + diff --git a/patch/ipc/IPCClient.h b/patch/ipc/IPCClient.h new file mode 100644 index 0000000..ee93030 --- /dev/null +++ b/patch/ipc/IPCClient.h @@ -0,0 +1,62 @@ +#ifndef IPC_CLIENT_H_ +#define IPC_CLIENT_H_ + +#include +#include +#include + +typedef struct IPCClient IPCClient; +/** + * This structure contains the details of an IPC Client and pointers for a + * linked list + */ +struct IPCClient { + int fd; + int subscriptions; + + char *buffer; + uint32_t buffer_size; + + struct epoll_event event; + IPCClient *next; + IPCClient *prev; +}; + +typedef IPCClient *IPCClientList; + +/** + * Allocate memory for new IPCClient with the specified file descriptor and + * initialize struct. + * + * @param fd File descriptor of IPC client + * + * @return Address to allocated IPCClient struct + */ +IPCClient *ipc_client_new(int fd); + +/** + * Add an IPC Client to the specified list + * + * @param list Address of the list to add the client to + * @param nc Address of the IPCClient + */ +void ipc_list_add_client(IPCClientList *list, IPCClient *nc); + +/** + * Remove an IPCClient from the specified list + * + * @param list Address of the list to remove the client from + * @param c Address of the IPCClient + */ +void ipc_list_remove_client(IPCClientList *list, IPCClient *c); + +/** + * Get an IPCClient from the specified IPCClient list + * + * @param list List to remove the client from + * @param fd File descriptor of the IPCClient + */ +IPCClient *ipc_list_get_client(IPCClientList list, int fd); + +#endif // IPC_CLIENT_H_ + diff --git a/patch/ipc/dwm-msg.c b/patch/ipc/dwm-msg.c new file mode 100644 index 0000000..ca1e1a4 --- /dev/null +++ b/patch/ipc/dwm-msg.c @@ -0,0 +1,549 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IPC_MAGIC "DWM-IPC" +// clang-format off +#define IPC_MAGIC_ARR { 'D', 'W', 'M', '-', 'I', 'P', 'C' } +// clang-format on +#define IPC_MAGIC_LEN 7 // Not including null char + +#define IPC_EVENT_TAG_CHANGE "tag_change_event" +#define IPC_EVENT_CLIENT_FOCUS_CHANGE "client_focus_change_event" +#define IPC_EVENT_LAYOUT_CHANGE "layout_change_event" +#define IPC_EVENT_MONITOR_FOCUS_CHANGE "monitor_focus_change_event" +#define IPC_EVENT_FOCUSED_TITLE_CHANGE "focused_title_change_event" +#define IPC_EVENT_FOCUSED_STATE_CHANGE "focused_state_change_event" + +#define YSTR(str) yajl_gen_string(gen, (unsigned char *)str, strlen(str)) +#define YINT(num) yajl_gen_integer(gen, num) +#define YDOUBLE(num) yajl_gen_double(gen, num) +#define YBOOL(v) yajl_gen_bool(gen, v) +#define YNULL() yajl_gen_null(gen) +#define YARR(body) \ + { \ + yajl_gen_array_open(gen); \ + body; \ + yajl_gen_array_close(gen); \ + } +#define YMAP(body) \ + { \ + yajl_gen_map_open(gen); \ + body; \ + yajl_gen_map_close(gen); \ + } + +typedef unsigned long Window; + +const char *DEFAULT_SOCKET_PATH = "/tmp/dwm.sock"; +static int sock_fd = -1; +static unsigned int ignore_reply = 0; + +typedef enum IPCMessageType { + IPC_TYPE_RUN_COMMAND = 0, + IPC_TYPE_GET_MONITORS = 1, + IPC_TYPE_GET_TAGS = 2, + IPC_TYPE_GET_LAYOUTS = 3, + IPC_TYPE_GET_DWM_CLIENT = 4, + IPC_TYPE_SUBSCRIBE = 5, + IPC_TYPE_EVENT = 6 +} IPCMessageType; + +// Every IPC message must begin with this +typedef struct dwm_ipc_header { + uint8_t magic[IPC_MAGIC_LEN]; + uint32_t size; + uint8_t type; +} __attribute((packed)) dwm_ipc_header_t; + +static int +recv_message(uint8_t *msg_type, uint32_t *reply_size, uint8_t **reply) +{ + uint32_t read_bytes = 0; + const int32_t to_read = sizeof(dwm_ipc_header_t); + char header[to_read]; + char *walk = header; + + // Try to read header + while (read_bytes < to_read) { + ssize_t n = read(sock_fd, header + read_bytes, to_read - read_bytes); + + if (n == 0) { + if (read_bytes == 0) { + fprintf(stderr, "Unexpectedly reached EOF while reading header."); + fprintf(stderr, + "Read %" PRIu32 " bytes, expected %" PRIu32 " total bytes.\n", + read_bytes, to_read); + return -2; + } else { + fprintf(stderr, "Unexpectedly reached EOF while reading header."); + fprintf(stderr, + "Read %" PRIu32 " bytes, expected %" PRIu32 " total bytes.\n", + read_bytes, to_read); + return -3; + } + } else if (n == -1) { + return -1; + } + + read_bytes += n; + } + + // Check if magic string in header matches + if (memcmp(walk, IPC_MAGIC, IPC_MAGIC_LEN) != 0) { + fprintf(stderr, "Invalid magic string. Got '%.*s', expected '%s'\n", + IPC_MAGIC_LEN, walk, IPC_MAGIC); + return -3; + } + + walk += IPC_MAGIC_LEN; + + // Extract reply size + memcpy(reply_size, walk, sizeof(uint32_t)); + walk += sizeof(uint32_t); + + // Extract message type + memcpy(msg_type, walk, sizeof(uint8_t)); + walk += sizeof(uint8_t); + + (*reply) = malloc(*reply_size); + + // Extract payload + read_bytes = 0; + while (read_bytes < *reply_size) { + ssize_t n = read(sock_fd, *reply + read_bytes, *reply_size - read_bytes); + + if (n == 0) { + fprintf(stderr, "Unexpectedly reached EOF while reading payload."); + fprintf(stderr, "Read %" PRIu32 " bytes, expected %" PRIu32 " bytes.\n", + read_bytes, *reply_size); + free(*reply); + return -2; + } else if (n == -1) { + if (errno == EINTR || errno == EAGAIN) continue; + free(*reply); + return -1; + } + + read_bytes += n; + } + + return 0; +} + +static int +read_socket(IPCMessageType *msg_type, uint32_t *msg_size, char **msg) +{ + int ret = -1; + + while (ret != 0) { + ret = recv_message((uint8_t *)msg_type, msg_size, (uint8_t **)msg); + + if (ret < 0) { + // Try again (non-fatal error) + if (ret == -1 && (errno == EINTR || errno == EAGAIN)) continue; + + fprintf(stderr, "Error receiving response from socket. "); + fprintf(stderr, "The connection might have been lost.\n"); + exit(2); + } + } + + return 0; +} + +static ssize_t +write_socket(const void *buf, size_t count) +{ + size_t written = 0; + + while (written < count) { + const ssize_t n = + write(sock_fd, ((uint8_t *)buf) + written, count - written); + + if (n == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) + continue; + else + return n; + } + written += n; + } + return written; +} + +static void +connect_to_socket() +{ + struct sockaddr_un addr; + + int sock = socket(AF_UNIX, SOCK_STREAM, 0); + + // Initialize struct to 0 + memset(&addr, 0, sizeof(struct sockaddr_un)); + + addr.sun_family = AF_UNIX; + strcpy(addr.sun_path, DEFAULT_SOCKET_PATH); + + connect(sock, (const struct sockaddr *)&addr, sizeof(struct sockaddr_un)); + + sock_fd = sock; +} + +static int +send_message(IPCMessageType msg_type, uint32_t msg_size, uint8_t *msg) +{ + dwm_ipc_header_t header = { + .magic = IPC_MAGIC_ARR, .size = msg_size, .type = msg_type}; + + size_t header_size = sizeof(dwm_ipc_header_t); + size_t total_size = header_size + msg_size; + + uint8_t buffer[total_size]; + + // Copy header to buffer + memcpy(buffer, &header, header_size); + // Copy message to buffer + memcpy(buffer + header_size, msg, header.size); + + write_socket(buffer, total_size); + + return 0; +} + +static int +is_float(const char *s) +{ + size_t len = strlen(s); + int is_dot_used = 0; + int is_minus_used = 0; + + // Floats can only have one decimal point in between or digits + // Optionally, floats can also be below zero (negative) + for (int i = 0; i < len; i++) { + if (isdigit(s[i])) + continue; + else if (!is_dot_used && s[i] == '.' && i != 0 && i != len - 1) { + is_dot_used = 1; + continue; + } else if (!is_minus_used && s[i] == '-' && i == 0) { + is_minus_used = 1; + continue; + } else + return 0; + } + + return 1; +} + +static int +is_unsigned_int(const char *s) +{ + size_t len = strlen(s); + + // Unsigned int can only have digits + for (int i = 0; i < len; i++) { + if (isdigit(s[i])) + continue; + else + return 0; + } + + return 1; +} + +static int +is_signed_int(const char *s) +{ + size_t len = strlen(s); + + // Signed int can only have digits and a negative sign at the start + for (int i = 0; i < len; i++) { + if (isdigit(s[i])) + continue; + else if (i == 0 && s[i] == '-') { + continue; + } else + return 0; + } + + return 1; +} + +static void +flush_socket_reply() +{ + IPCMessageType reply_type; + uint32_t reply_size; + char *reply; + + read_socket(&reply_type, &reply_size, &reply); + + free(reply); +} + +static void +print_socket_reply() +{ + IPCMessageType reply_type; + uint32_t reply_size; + char *reply; + + read_socket(&reply_type, &reply_size, &reply); + + printf("%.*s\n", reply_size, reply); + fflush(stdout); + free(reply); +} + +static int +run_command(const char *name, char *args[], int argc) +{ + const unsigned char *msg; + size_t msg_size; + + yajl_gen gen = yajl_gen_alloc(NULL); + + // Message format: + // { + // "command": "", + // "args": [ ... ] + // } + // clang-format off + YMAP( + YSTR("command"); YSTR(name); + YSTR("args"); YARR( + for (int i = 0; i < argc; i++) { + if (is_signed_int(args[i])) { + long long num = atoll(args[i]); + YINT(num); + } else if (is_float(args[i])) { + float num = atof(args[i]); + YDOUBLE(num); + } else { + YSTR(args[i]); + } + } + ) + ) + // clang-format on + + yajl_gen_get_buf(gen, &msg, &msg_size); + + send_message(IPC_TYPE_RUN_COMMAND, msg_size, (uint8_t *)msg); + + if (!ignore_reply) + print_socket_reply(); + else + flush_socket_reply(); + + yajl_gen_free(gen); + + return 0; +} + +static int +get_monitors() +{ + send_message(IPC_TYPE_GET_MONITORS, 1, (uint8_t *)""); + print_socket_reply(); + return 0; +} + +static int +get_tags() +{ + send_message(IPC_TYPE_GET_TAGS, 1, (uint8_t *)""); + print_socket_reply(); + + return 0; +} + +static int +get_layouts() +{ + send_message(IPC_TYPE_GET_LAYOUTS, 1, (uint8_t *)""); + print_socket_reply(); + + return 0; +} + +static int +get_dwm_client(Window win) +{ + const unsigned char *msg; + size_t msg_size; + + yajl_gen gen = yajl_gen_alloc(NULL); + + // Message format: + // { + // "client_window_id": "" + // } + // clang-format off + YMAP( + YSTR("client_window_id"); YINT(win); + ) + // clang-format on + + yajl_gen_get_buf(gen, &msg, &msg_size); + + send_message(IPC_TYPE_GET_DWM_CLIENT, msg_size, (uint8_t *)msg); + + print_socket_reply(); + + yajl_gen_free(gen); + + return 0; +} + +static int +subscribe(const char *event) +{ + const unsigned char *msg; + size_t msg_size; + + yajl_gen gen = yajl_gen_alloc(NULL); + + // Message format: + // { + // "event": "", + // "action": "subscribe" + // } + // clang-format off + YMAP( + YSTR("event"); YSTR(event); + YSTR("action"); YSTR("subscribe"); + ) + // clang-format on + + yajl_gen_get_buf(gen, &msg, &msg_size); + + send_message(IPC_TYPE_SUBSCRIBE, msg_size, (uint8_t *)msg); + + if (!ignore_reply) + print_socket_reply(); + else + flush_socket_reply(); + + yajl_gen_free(gen); + + return 0; +} + +static void +usage_error(const char *prog_name, const char *format, ...) +{ + va_list args; + va_start(args, format); + + fprintf(stderr, "Error: "); + vfprintf(stderr, format, args); + fprintf(stderr, "\nusage: %s [...]\n", prog_name); + fprintf(stderr, "Try '%s help'\n", prog_name); + + va_end(args); + exit(1); +} + +static void +print_usage(const char *name) +{ + printf("usage: %s [options] [...]\n", name); + puts(""); + puts("Commands:"); + puts(" run_command [args...] Run an IPC command"); + puts(""); + puts(" get_monitors Get monitor properties"); + puts(""); + puts(" get_tags Get list of tags"); + puts(""); + puts(" get_layouts Get list of layouts"); + puts(""); + puts(" get_dwm_client Get dwm client proprties"); + puts(""); + puts(" subscribe [events...] Subscribe to specified events"); + puts(" Options: " IPC_EVENT_TAG_CHANGE ","); + puts(" " IPC_EVENT_LAYOUT_CHANGE ","); + puts(" " IPC_EVENT_CLIENT_FOCUS_CHANGE ","); + puts(" " IPC_EVENT_MONITOR_FOCUS_CHANGE ","); + puts(" " IPC_EVENT_FOCUSED_TITLE_CHANGE ","); + puts(" " IPC_EVENT_FOCUSED_STATE_CHANGE); + puts(""); + puts(" help Display this message"); + puts(""); + puts("Options:"); + puts(" --ignore-reply Don't print reply messages from"); + puts(" run_command and subscribe."); + puts(""); +} + +int +main(int argc, char *argv[]) +{ + const char *prog_name = argv[0]; + + connect_to_socket(); + if (sock_fd == -1) { + fprintf(stderr, "Failed to connect to socket\n"); + return 1; + } + + int i = 1; + if (i < argc && strcmp(argv[i], "--ignore-reply") == 0) { + ignore_reply = 1; + i++; + } + + if (i >= argc) usage_error(prog_name, "Expected an argument, got none"); + + if (!argc || strcmp(argv[i], "help") == 0) + print_usage(prog_name); + else if (strcmp(argv[i], "run_command") == 0) { + if (++i >= argc) usage_error(prog_name, "No command specified"); + // Command name + char *command = argv[i]; + // Command arguments are everything after command name + char **command_args = argv + ++i; + // Number of command arguments + int command_argc = argc - i; + run_command(command, command_args, command_argc); + } else if (strcmp(argv[i], "get_monitors") == 0) { + get_monitors(); + } else if (strcmp(argv[i], "get_tags") == 0) { + get_tags(); + } else if (strcmp(argv[i], "get_layouts") == 0) { + get_layouts(); + } else if (strcmp(argv[i], "get_dwm_client") == 0) { + if (++i < argc) { + if (is_unsigned_int(argv[i])) { + Window win = atol(argv[i]); + get_dwm_client(win); + } else + usage_error(prog_name, "Expected unsigned integer argument"); + } else + usage_error(prog_name, "Expected the window id"); + } else if (strcmp(argv[i], "subscribe") == 0) { + if (++i < argc) { + for (int j = i; j < argc; j++) subscribe(argv[j]); + } else + usage_error(prog_name, "Expected event name"); + // Keep listening for events forever + while (1) { + print_socket_reply(); + } + } else + usage_error(prog_name, "Invalid argument '%s'", argv[i]); + + return 0; +} + diff --git a/patch/ipc/yajl_dumps.h b/patch/ipc/yajl_dumps.h new file mode 100644 index 0000000..bb57a17 --- /dev/null +++ b/patch/ipc/yajl_dumps.h @@ -0,0 +1,66 @@ +#ifndef YAJL_DUMPS_H_ +#define YAJL_DUMPS_H_ + +#include +#include + +#define YSTR(str) yajl_gen_string(gen, (unsigned char *)str, strlen(str)) +#define YINT(num) yajl_gen_integer(gen, num) +#define YDOUBLE(num) yajl_gen_double(gen, num) +#define YBOOL(v) yajl_gen_bool(gen, v) +#define YNULL() yajl_gen_null(gen) +#define YARR(body) \ + { \ + yajl_gen_array_open(gen); \ + body; \ + yajl_gen_array_close(gen); \ + } +#define YMAP(body) \ + { \ + yajl_gen_map_open(gen); \ + body; \ + yajl_gen_map_close(gen); \ + } + +int dump_tag(yajl_gen gen, const char *name, const int tag_mask); + +int dump_tags(yajl_gen gen, int tags_len); + +int dump_client(yajl_gen gen, Client *c); + +int dump_monitor(yajl_gen gen, Monitor *mon, int is_selected); + +int dump_monitors(yajl_gen gen, Monitor *mons, Monitor *selmon); + +int dump_layouts(yajl_gen gen, const Layout layouts[], const int layouts_len); + +int dump_tag_state(yajl_gen gen, TagState state); + +int dump_tag_event(yajl_gen gen, int mon_num, TagState old_state, + TagState new_state); + +int dump_client_focus_change_event(yajl_gen gen, Client *old_client, + Client *new_client, int mon_num); + +int dump_layout_change_event(yajl_gen gen, const int mon_num, + const char *old_symbol, const Layout *old_layout, + const char *new_symbol, const Layout *new_layout); + +int dump_monitor_focus_change_event(yajl_gen gen, const int last_mon_num, + const int new_mon_num); + +int dump_focused_title_change_event(yajl_gen gen, const int mon_num, + const Window client_id, + const char *old_name, const char *new_name); + +int dump_client_state(yajl_gen gen, const ClientState *state); + +int dump_focused_state_change_event(yajl_gen gen, const int mon_num, + const Window client_id, + const ClientState *old_state, + const ClientState *new_state); + +int dump_error_message(yajl_gen gen, const char *reason); + +#endif // YAJL_DUMPS_H_ + diff --git a/patch/layout_bstack.c b/patch/layout_bstack.c new file mode 100644 index 0000000..e72c57c --- /dev/null +++ b/patch/layout_bstack.c @@ -0,0 +1,39 @@ +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; + + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); + + if (n == 0) + return; + + sx = mx = m->wx; + sy = my = m->wy; + sh = mh = m->wh; + sw = mw = m->ww; + + if (m->nmaster && n > m->nmaster) { + sh = mh * (1 - m->mfact); + mh = mh * m->mfact; + sy = my + mh; + } + + 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); + } else { + resize(c, sx, sy, (sw / sfacts) + ((i - m->nmaster) < srest ? 1 : 0) - (2*c->bw), sh - (2*c->bw), 0); + sx += WIDTH(c); + } + } +} + diff --git a/patch/layout_bstack.h b/patch/layout_bstack.h new file mode 100644 index 0000000..07e8c0a --- /dev/null +++ b/patch/layout_bstack.h @@ -0,0 +1,2 @@ +static void bstack(Monitor *m); + diff --git a/patch/layout_centeredmaster.c b/patch/layout_centeredmaster.c new file mode 100644 index 0000000..07e9d43 --- /dev/null +++ b/patch/layout_centeredmaster.c @@ -0,0 +1,84 @@ +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; + + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); + + if (n == 0) + return; + + /* initialize areas */ + mx = m->wx; + my = m->wy; + mh = m->wh; + mw = m->ww; + lh = m->wh; + rh = m->wh; + + 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 * m->mfact; + lw = (m->ww - mw) / 2; + mx += lw; + } else { + /* ||<---M--->|<-S->|| */ + mw = mw * m->mfact; + lw = m->ww - mw; + } + rw = lw; + lx = m->wx; + ly = m->wy; + rx = mx + mw; + ry = m->wy; + } + + /* 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); + } 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); + } 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); + } + } + } +} + diff --git a/patch/layout_centeredmaster.h b/patch/layout_centeredmaster.h new file mode 100644 index 0000000..703f8b8 --- /dev/null +++ b/patch/layout_centeredmaster.h @@ -0,0 +1,2 @@ +static void centeredmaster(Monitor *m); + diff --git a/patch/layout_deck.c b/patch/layout_deck.c new file mode 100644 index 0000000..c5b4842 --- /dev/null +++ b/patch/layout_deck.c @@ -0,0 +1,40 @@ +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; + + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); + + if (n == 0) + return; + + sx = mx = m->wx; + sy = my = m->wy; + sh = mh = m->wh; + sw = mw = m->ww; + + if (m->nmaster && n > m->nmaster) { + sw = mw * (1 - m->mfact); + mw = mw * m->mfact; + sx = mx + mw; + } + + 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); + } else { + resize(c, sx, sy, sw - (2*c->bw), sh - (2*c->bw), 0); + } +} + diff --git a/patch/layout_deck.h b/patch/layout_deck.h new file mode 100644 index 0000000..b6e3c41 --- /dev/null +++ b/patch/layout_deck.h @@ -0,0 +1,2 @@ +static void deck(Monitor *m); + diff --git a/patch/layout_facts.c b/patch/layout_facts.c new file mode 100644 index 0000000..241d344 --- /dev/null +++ b/patch/layout_facts.c @@ -0,0 +1,24 @@ +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 +} + diff --git a/patch/layout_gapplessgrid.c b/patch/layout_gapplessgrid.c new file mode 100644 index 0000000..ce23d5a --- /dev/null +++ b/patch/layout_gapplessgrid.c @@ -0,0 +1,48 @@ +void +gaplessgrid(Monitor *m) +{ + unsigned int i, n; + int x, y, cols, rows, ch, cw, cn, rn, rrest, crest; // counters + Client *c; + + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); + if (n == 0) + return; + + /* grid dimensions */ + for (cols = 0; cols <= n/2; cols++) + if (cols*cols >= n) + break; + if (n == 5) /* set layout against the general calculation: not 1:2:2, but 2:3 */ + cols = 2; + rows = n/cols; + cn = rn = 0; // reset column no, row no, client count + + ch = m->wh / rows; + cw = m->ww / cols; + rrest = m->wh - ch * rows; + crest = m->ww - cw * cols; + x = m->wx; + y = m->wy; + + for (i = 0, c = nexttiled(m->clients); c; i++, c = nexttiled(c->next)) { + if (i/rows + 1 > cols - n%cols) { + rows = n/cols + 1; + ch = m->wh / rows; + rrest = m->wh - ch * rows; + } + resize(c, + x, + y + rn*ch + MIN(rn, rrest), + cw + (cn < crest ? 1 : 0) - 2*c->bw, + ch + (rn < rrest ? 1 : 0) - 2*c->bw, + 0); + rn++; + if (rn >= rows) { + rn = 0; + x += cw + (cn < crest ? 1 : 0); + cn++; + } + } +} + diff --git a/patch/layout_gapplessgrid.h b/patch/layout_gapplessgrid.h new file mode 100644 index 0000000..1a4ffc2 --- /dev/null +++ b/patch/layout_gapplessgrid.h @@ -0,0 +1,2 @@ +static void gaplessgrid(Monitor *m); + diff --git a/patch/layout_monocle.c b/patch/layout_monocle.c new file mode 100644 index 0000000..4b3516c --- /dev/null +++ b/patch/layout_monocle.c @@ -0,0 +1,15 @@ +void +monocle(Monitor *m) +{ + unsigned int n = 0; + Client *c; + + for (c = m->clients; c; c = c->next) + if (ISVISIBLE(c)) + n++; + if (n > 0) /* override layout symbol */ + snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n); + for (c = nexttiled(m->clients); c; c = nexttiled(c->next)) + resize(c, m->wx, m->wy, m->ww - 2 * c->bw, m->wh - 2 * c->bw, 0); +} + diff --git a/patch/layout_monocle.h b/patch/layout_monocle.h new file mode 100644 index 0000000..f32e49f --- /dev/null +++ b/patch/layout_monocle.h @@ -0,0 +1,2 @@ +static void monocle(Monitor *m); + diff --git a/patch/layout_tile.c b/patch/layout_tile.c new file mode 100644 index 0000000..76b0078 --- /dev/null +++ b/patch/layout_tile.c @@ -0,0 +1,38 @@ +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; + + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); + + if (n == 0) + return; + + sx = mx = m->wx; + sy = my = m->wy; + sh = mh = m->wh; + sw = mw = m->ww; + + if (m->nmaster && n > m->nmaster) { + sw = mw * (1 - m->mfact); + mw = mw * m->mfact; + sx = mx + mw; + } + + 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); + } else { + resize(c, sx, sy, sw - (2*c->bw), (sh / sfacts) + ((i - m->nmaster) < srest ? 1 : 0) - (2*c->bw), 0); + sy += HEIGHT(c); + } +} + diff --git a/patch/layout_tile.h b/patch/layout_tile.h new file mode 100644 index 0000000..4aff634 --- /dev/null +++ b/patch/layout_tile.h @@ -0,0 +1,2 @@ +static void tile(Monitor *m); + diff --git a/patch/pertag.c b/patch/pertag.c new file mode 100644 index 0000000..c5f7f92 --- /dev/null +++ b/patch/pertag.c @@ -0,0 +1,28 @@ +struct Pertag { + unsigned int curtag; /* current tag index */ + int nmasters[NUMTAGS + 1]; /* number of windows in master area */ + const Layout *ltidxs[NUMTAGS + 1][2]; /* matrix of tags and layouts indexes */ + float mfacts[NUMTAGS + 1]; /* mfacts per tag */ + unsigned int sellts[NUMTAGS + 1]; /* selected layouts */ +}; + +void +pertagview(const Arg *arg) +{ + int i; + + if (arg->ui == ~0) + selmon->pertag->curtag = 0; + else { + for (i = 0; !(selmon->tagset[selmon->seltags] & 1 << i); i++); + selmon->pertag->curtag = i + 1; + } + + selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag]; + selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag]; + selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; + selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; + selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1]; + +} + diff --git a/patch/pertag.h b/patch/pertag.h new file mode 100644 index 0000000..5c53ac9 --- /dev/null +++ b/patch/pertag.h @@ -0,0 +1,2 @@ +static void pertagview(const Arg *arg); + diff --git a/patch/renamed_scratchpads.c b/patch/renamed_scratchpads.c new file mode 100644 index 0000000..1a41e22 --- /dev/null +++ b/patch/renamed_scratchpads.c @@ -0,0 +1,144 @@ +void +removescratch(const Arg *arg) +{ + Client *c = selmon->sel; + if (!c) + return; + c->scratchkey = 0; +} + +void +setscratch(const Arg *arg) +{ + Client *c = selmon->sel; + if (!c) + return; + + c->scratchkey = ((char**)arg->v)[0][0]; +} + +void spawnscratch(const Arg *arg) +{ + if (fork() == 0) { + if (dpy) + close(ConnectionNumber(dpy)); + setsid(); + execvp(((char **)arg->v)[1], ((char **)arg->v)+1); + fprintf(stderr, "dwm: execvp %s", ((char **)arg->v)[1]); + perror(" failed"); + exit(EXIT_SUCCESS); + } +} + +void +togglescratch(const Arg *arg) +{ + Client *c, *next, *last = NULL, *found = NULL, *monclients = NULL; + Monitor *mon; + int scratchvisible = 0; // whether the scratchpads are currently visible or not + int multimonscratch = 0; // whether we have scratchpads that are placed on multiple monitors + int scratchmon = -1; // the monitor where the scratchpads exist + int numscratchpads = 0; // count of scratchpads + + /* Looping through monitors and client's twice, the first time to work out whether we need + to move clients across from one monitor to another or not */ + for (mon = mons; mon; mon = mon->next) + for (c = mon->clients; c; c = c->next) { + if (c->scratchkey != ((char**)arg->v)[0][0]) + continue; + if (scratchmon != -1 && scratchmon != mon->num) + multimonscratch = 1; + if (c->mon->tagset[c->mon->seltags] & c->tags) // && !HIDDEN(c) + ++scratchvisible; + scratchmon = mon->num; + ++numscratchpads; + } + + /* Now for the real deal. The logic should go like: + - hidden scratchpads will be shown + - shown scratchpads will be hidden, unless they are being moved to the current monitor + - the scratchpads will be moved to the current monitor if they all reside on the same monitor + - multiple scratchpads residing on separate monitors will be left in place + */ + for (mon = mons; mon; mon = mon->next) { + for (c = mon->stack; c; c = next) { + next = c->snext; + if (c->scratchkey != ((char**)arg->v)[0][0]) + continue; + + /* Record the first found scratchpad client for focus purposes, but prioritise the + scratchpad on the current monitor if one exists */ + if (!found || (mon == selmon && found->mon != selmon)) + found = c; + + /* If scratchpad clients reside on another monitor and we are moving them across then + as we are looping through monitors we could be moving a client to a monitor that has + not been processed yet, hence we could be processing a scratchpad twice. To avoid + this we detach them and add them to a temporary list (monclients) which is to be + processed later. */ + if (!multimonscratch && c->mon != selmon) { + detach(c); + detachstack(c); + c->next = NULL; + /* Note that we are adding clients at the end of the list, this is to preserve the + order of clients as they were on the adjacent monitor (relevant when tiled) */ + if (last) + last = last->next = c; + else + last = monclients = c; + } else if (scratchvisible == numscratchpads) { + c->tags = 0; + } else { + XSetWindowBorder(dpy, c->win, scheme[SchemeScratchNorm][ColBorder].pixel); + c->tags = c->mon->tagset[c->mon->seltags]; + if (c->isfloating) + XRaiseWindow(dpy, c->win); + } + } + } + + /* Attach moved scratchpad clients on the selected monitor */ + for (c = monclients; c; c = next) { + next = c->next; + mon = c->mon; + c->mon = selmon; + c->tags = selmon->tagset[selmon->seltags]; + /* Attach scratchpad clients from other monitors at the bottom of the stack */ + if (selmon->clients) { + for (last = selmon->clients; last && last->next; last = last->next); + last->next = c; + } else + selmon->clients = c; + c->next = NULL; + attachstack(c); + + /* Center floating scratchpad windows when moved from one monitor to another */ + if (c->isfloating) { + if (c->w > selmon->ww) + c->w = selmon->ww - c->bw * 2; + if (c->h > selmon->wh) + c->h = selmon->wh - c->bw * 2; + + if (numscratchpads > 1) { + c->x = c->mon->wx + (c->x - mon->wx) * ((double)(abs(c->mon->ww - WIDTH(c))) / MAX(abs(mon->ww - WIDTH(c)), 1)); + c->y = c->mon->wy + (c->y - mon->wy) * ((double)(abs(c->mon->wh - HEIGHT(c))) / MAX(abs(mon->wh - HEIGHT(c)), 1)); + } else if (c->x < c->mon->mx || c->x > c->mon->mx + c->mon->mw || + c->y < c->mon->my || c->y > c->mon->my + c->mon->mh) { + c->x = c->mon->wx + (c->mon->ww / 2 - WIDTH(c) / 2); + c->y = c->mon->wy + (c->mon->wh / 2 - HEIGHT(c) / 2); + } + resizeclient(c, c->x, c->y, c->w, c->h); + XRaiseWindow(dpy, c->win); + } + } + + if (found) { + focus(ISVISIBLE(found) ? found : NULL); + arrange(NULL); + if (found->isfloating) + XRaiseWindow(dpy, found->win); + } else { + spawnscratch(arg); + } +} + diff --git a/patch/renamed_scratchpads.h b/patch/renamed_scratchpads.h new file mode 100644 index 0000000..3260462 --- /dev/null +++ b/patch/renamed_scratchpads.h @@ -0,0 +1,4 @@ +static void removescratch(const Arg *arg); +static void setscratch(const Arg *arg); +static void spawnscratch(const Arg *arg); +static void togglescratch(const Arg *arg); diff --git a/patch/restartsig.c b/patch/restartsig.c new file mode 100644 index 0000000..adb61b5 --- /dev/null +++ b/patch/restartsig.c @@ -0,0 +1,16 @@ +static int restart = 0; + +void +sighup(int unused) +{ + Arg a = {.i = 1}; + quit(&a); +} + +void +sigterm(int unused) +{ + Arg a = {.i = 0}; + quit(&a); +} + diff --git a/patch/restartsig.h b/patch/restartsig.h new file mode 100644 index 0000000..b16975b --- /dev/null +++ b/patch/restartsig.h @@ -0,0 +1,3 @@ +static void sighup(int unused); +static void sigterm(int unused); + diff --git a/patch/seamless_restart.c b/patch/seamless_restart.c new file mode 100644 index 0000000..dd3c157 --- /dev/null +++ b/patch/seamless_restart.c @@ -0,0 +1,314 @@ +void +persistmonitorstate(Monitor *m) +{ + Client *c; + unsigned int i; + + setmonitortags(m); + setmonitorfields(m); + + /* Set client atoms */ + for (i = 1, c = m->clients; c; c = c->next, ++i) { + c->idx = i; + persistclientstate(c); + } +} + +int +restoremonitorstate(Monitor *m) +{ + return getmonitortags(m) | getmonitorfields(m); +} + +void +persistclientstate(Client *c) +{ + setclienttags(c); + setclientfields(c); + savewindowfloatposition(c, c->mon); +} + +int +restoreclientstate(Client *c) +{ + int restored = getclientfields(c); + getclienttags(c); + restorewindowfloatposition(c, c->mon ? c->mon : selmon); + return restored; +} + +void setmonitorfields(Monitor *m) +{ + unsigned int i; + char atom[22] = {0}; + Atom monitor_fields; + + sprintf(atom, "_DWM_MONITOR_FIELDS_%u", m->num); + monitor_fields = XInternAtom(dpy, atom, False); + + /* Perists workspace information in 32 bits laid out like this: + * + * |0|0000|0|0000|0000|0000|0000|0000|000|000 + * | | | | | | | | | |-- nmaster + * | | | | | | | | |-- nstack + * | | | | | | | |-- layout + * | | | | | | |-- flextile LAYOUT (split) + * | | | | | |-- flextile MASTER + * | | | | |-- flextile STACK1 + * | | | |-- flextile STACK2 + * | | |-- flextile mirror layout (indicated by negative layout) + * | | + * | |-- reserved + * |-- showbar + */ + for (i = 0; i <= NUMTAGS; i++) { + uint32_t data[] = { + (m->pertag->nmasters[i] & 0x7) | + (getlayoutindex(m->pertag->ltidxs[i][m->pertag->sellts[i]]) & 0xF) << 6 | + m->showbar << 31 + }; + + XChangeProperty(dpy, root, monitor_fields, XA_CARDINAL, 32, + i ? PropModeAppend : PropModeReplace, (unsigned char *)data, 1); + } +} + +int +getlayoutindex(const Layout *layout) +{ + int i; + + for (i = 0; i < LENGTH(layouts) && &layouts[i] != layout; i++); + if (i == LENGTH(layouts)) + i = 0; + return i; +} + +int +getmonitorfields(Monitor *m) +{ + int di, layout_index; + unsigned int i, restored = 0; + unsigned int tags = m->tagset[m->seltags] << 1; + unsigned long dl, nitems; + unsigned char *p = NULL; + char atom[22] = {0}; + Atom da, state = None; + + sprintf(atom, "_DWM_MONITOR_FIELDS_%u", m->num); + Atom dwm_monitor = XInternAtom(dpy, atom, False); + if (!dwm_monitor) + return 0; + + for (i = 0; i <= NUMTAGS; i++) { + if (!(XGetWindowProperty(dpy, root, dwm_monitor, i, (NUMTAGS + 1) * sizeof dl, + False, AnyPropertyType, &da, &di, &nitems, &dl, &p) == Success && p)) { + break; + } + + if (!nitems) { + XFree(p); + break; + } + + /* See bit layout in the persistmonitorstate function */ + state = *(Atom *)p; + + m->pertag->nmasters[i] = state & 0x7; + layout_index = (state >> 6) & 0xF; + if (layout_index < LENGTH(layouts)) + m->pertag->ltidxs[i][m->pertag->sellts[i]] = &layouts[layout_index]; + + if (!restored && i && (tags & (1 << i))) { + m->nmaster = m->pertag->nmasters[i]; + m->sellt = m->pertag->sellts[i]; + m->lt[m->sellt] = m->pertag->ltidxs[i][m->sellt]; + m->showbar = (state >> 31) & 0x1; + restored = 1; + } + + XFree(p); + } + + return restored; +} + +void +setmonitortags(Monitor *m) +{ + char atom[22] = {0}; + Atom monitor_tags; + + sprintf(atom, "_DWM_MONITOR_TAGS_%u", m->num); + monitor_tags = XInternAtom(dpy, atom, False); + + uint32_t data[] = { m->tagset[m->seltags] }; + XChangeProperty(dpy, root, monitor_tags, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)data, 1); +} + +int +getmonitortags(Monitor *m) +{ + int di; + unsigned long dl, nitems; + unsigned char *p = NULL; + char atom[22] = {0}; + Atom da, monitor_tags = None, tags; + + sprintf(atom, "_DWM_MONITOR_TAGS_%u", m->num); + monitor_tags = XInternAtom(dpy, atom, False); + + if (!(XGetWindowProperty(dpy, root, monitor_tags, 0L, sizeof dl, + False, AnyPropertyType, &da, &di, &nitems, &dl, &p) == Success && p)) { + return 0; + } + + if (nitems) { + tags = *(Atom *)p; + m->tagset[m->seltags] = tags & TAGMASK; + } + + XFree(p); + return 1; +} + +void +setclientfields(Client *c) +{ + /* Perists client information in 32 bits laid out like this: + * + * |00000000|00000|0|0|0|0|0|0|0|0|00000000|000 + * | | | | | | | | | | | |-- monitor index + * | | | | | | | | | | |-- client index + * | | | | | | | | | |-- isfloating + * | | | | | | | | |-- ispermanent + * | | | | | | | |-- isterminal + * | | | | | | |-- noswallow + * | | | | | |-- issteam + * | | | | |-- issticky + * | | | |-- fakefullscreen + * | | |-- isfreesize + * | | + * | |-- reserved + * |-- scratchkey (for scratchpads) + */ + uint32_t data[] = { + (c->mon->num & 0x7) + | (c->idx & 0xFF) << 3 + | (c->isfloating & 0x1) << 11 + | (c->scratchkey & 0xFF) << 24 + }; + XChangeProperty(dpy, c->win, clientatom[ClientFields], XA_CARDINAL, 32, PropModeReplace, (unsigned char *)data, 1); +} + +int +getclientfields(Client *c) +{ + Monitor *m; + Atom fields = getatomprop(c, clientatom[ClientFields], AnyPropertyType); + if (fields == None) + return 0; + + /* See bit layout in the setclientfields function */ + for (m = mons; m; m = m->next) + if (m->num == (fields & 0x7)) { + c->mon = m; + break; + } + c->idx = (fields >> 3) & 0xFF; + c->isfloating = (fields >> 11) & 0x1; + c->scratchkey = (fields >> 24) & 0xFF; + return 1; +} + +void +setclienttags(Client *c) +{ + uint32_t data[] = { c->tags }; + XChangeProperty(dpy, c->win, clientatom[ClientTags], XA_CARDINAL, 32, PropModeReplace, (unsigned char *)data, 1); +} + +int +getclienttags(Client *c) +{ + Atom tags = getatomprop(c, clientatom[ClientTags], AnyPropertyType); + if (tags == None) + return 0; + + c->tags = tags & TAGMASK; + return 1; +} + +void +savewindowfloatposition(Client *c, Monitor *m) +{ + char atom[22] = {0}; + if (c->sfx == -9999) + return; + + sprintf(atom, "_DWM_FLOATPOS_%u", m->num); + uint32_t pos[] = { (MAX(c->sfx - m->mx, 0) & 0xffff) | ((MAX(c->sfy - m->my, 0) & 0xffff) << 16) }; + XChangeProperty(dpy, c->win, XInternAtom(dpy, atom, False), XA_CARDINAL, 32, PropModeReplace, (unsigned char *)pos, 1); + + sprintf(atom, "_DWM_FLOATSIZE_%u", m->num); + uint32_t size[] = { (c->sfw & 0xffff) | ((c->sfh & 0xffff) << 16) }; + XChangeProperty(dpy, c->win, XInternAtom(dpy, atom, False), XA_CARDINAL, 32, PropModeReplace, (unsigned char *)size, 1); + + XSync(dpy, False); +} + +int +restorewindowfloatposition(Client *c, Monitor *m) +{ + char atom[22] = {0}; + Atom key, value; + int x, y, w, h; + + if (m == NULL) + return 0; + + sprintf(atom, "_DWM_FLOATPOS_%u", m->num); + + key = XInternAtom(dpy, atom, False); + if (!key) + return 0; + + value = getatomprop(c, key, AnyPropertyType); + if (!value) + return 0; + + x = value & 0xffff; + y = value >> 16; + + sprintf(atom, "_DWM_FLOATSIZE_%u", m->num); + + key = XInternAtom(dpy, atom, False); + if (!key) + return 0; + + value = getatomprop(c, key, AnyPropertyType); + if (!value) + return 0; + + w = value & 0xffff; + h = value >> 16; + + if (w <= 0 || h <= 0) { + fprintf(stderr, "restorewindowfloatposition: bad float values x = %d, y = %d, w = %d, h = %d for client = %s\n", x, y, w, h, c->name); + return 0; + } + + c->sfx = m->mx + x; + c->sfy = m->my + y; + c->sfw = w; + c->sfh = h; + + if (c->isfloating) { + c->x = c->sfx; + c->y = c->sfy; + c->w = c->sfw; + c->h = c->sfh; + } + + return 1; +} diff --git a/patch/seamless_restart.h b/patch/seamless_restart.h new file mode 100644 index 0000000..4d95b6a --- /dev/null +++ b/patch/seamless_restart.h @@ -0,0 +1,17 @@ +#include + +static void persistmonitorstate(Monitor *m); +static int restoremonitorstate(Monitor *m); +static void persistclientstate(Client *c); +static int restoreclientstate(Client *c); +static void setmonitorfields(Monitor *m); +static int getmonitorfields(Monitor *m); +static void setmonitortags(Monitor *m); +static int getmonitortags(Monitor *m); +static void setclientfields(Client *c); +static int getclientfields(Client *c); +static void setclienttags(Client *c); +static int getclienttags(Client *c); +static int getlayoutindex(const Layout *layout); +static void savewindowfloatposition(Client *c, Monitor *m); +static int restorewindowfloatposition(Client *c, Monitor *m); diff --git a/patch/sizehints_ruled.c b/patch/sizehints_ruled.c new file mode 100644 index 0000000..32de43b --- /dev/null +++ b/patch/sizehints_ruled.c @@ -0,0 +1,33 @@ +void +checkfloatingrules(Client *c) +{ + const char *class, *instance; + Atom wintype; + char role[64]; + unsigned int i; + const Rule *r; + XClassHint ch = { NULL, NULL }; + + XGetClassHint(dpy, c->win, &ch); + class = ch.res_class ? ch.res_class : broken; + instance = ch.res_name ? ch.res_name : broken; + wintype = getatomprop(c, netatom[NetWMWindowType], XA_ATOM); + gettextprop(c->win, wmatom[WMWindowRole], role, sizeof(role)); + + 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->role || strstr(role, r->role)) + && (!r->instance || strstr(instance, r->instance)) + && (!r->wintype || wintype == XInternAtom(dpy, r->wintype, False))) + { + c->isfloating = r->isfloating; + } + } + if (ch.res_class) + XFree(ch.res_class); + if (ch.res_name) + XFree(ch.res_name); +} + diff --git a/patch/sizehints_ruled.h b/patch/sizehints_ruled.h new file mode 100644 index 0000000..14c6719 --- /dev/null +++ b/patch/sizehints_ruled.h @@ -0,0 +1,2 @@ +static void checkfloatingrules(Client *c); + diff --git a/patch/stacker.c b/patch/stacker.c new file mode 100644 index 0000000..0ed1344 --- /dev/null +++ b/patch/stacker.c @@ -0,0 +1,73 @@ +void +focusstack(const Arg *arg) +{ + int i = stackpos(arg); + Client *c, *p; + + if (i < 0) + return; + + if (!selmon->sel) + 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); +} + +void +pushstack(const Arg *arg) +{ + int i = stackpos(arg); + Client *sel = selmon->sel, *c, *p; + + if (i < 0) + 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); +} + +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; +} + diff --git a/patch/stacker.h b/patch/stacker.h new file mode 100644 index 0000000..ee420bd --- /dev/null +++ b/patch/stacker.h @@ -0,0 +1,11 @@ +#define GETINC(X) ((X) - 2000) +#define INC(X) ((X) + 2000) +#define ISINC(X) ((X) > 1000 && (X) < 3000) +#define PREVSEL 3000 +#define MOD(N,M) ((N)%(M) < 0 ? (N)%(M) + (M) : (N)%(M)) +#define TRUNC(X,A,B) (MAX((A), MIN((X), (B)))) + +static void focusstack(const Arg *arg); +static void pushstack(const Arg *arg); +static int stackpos(const Arg *arg); + diff --git a/patch/togglefullscreen.c b/patch/togglefullscreen.c new file mode 100644 index 0000000..a62edef --- /dev/null +++ b/patch/togglefullscreen.c @@ -0,0 +1,10 @@ +void +togglefullscreen(const Arg *arg) +{ + Client *c = selmon->sel; + if (!c) + return; + + setfullscreen(c, !c->isfullscreen); +} + diff --git a/patch/togglefullscreen.h b/patch/togglefullscreen.h new file mode 100644 index 0000000..96a6770 --- /dev/null +++ b/patch/togglefullscreen.h @@ -0,0 +1,2 @@ +static void togglefullscreen(const Arg *arg); + diff --git a/patch/unfloatvisible.c b/patch/unfloatvisible.c new file mode 100644 index 0000000..fb8888f --- /dev/null +++ b/patch/unfloatvisible.c @@ -0,0 +1,15 @@ +void +unfloatvisible(const Arg *arg) +{ + Client *c; + + for (c = selmon->clients; c; c = c->next) + if (ISVISIBLE(c) && c->isfloating) + c->isfloating = c->isfixed; + + if (arg && arg->v) + setlayout(arg); + else + arrange(selmon); +} + diff --git a/patch/unfloatvisible.h b/patch/unfloatvisible.h new file mode 100644 index 0000000..f15bc66 --- /dev/null +++ b/patch/unfloatvisible.h @@ -0,0 +1,2 @@ +static void unfloatvisible(const Arg *arg); + diff --git a/transient.c b/transient.c new file mode 100644 index 0000000..158460f --- /dev/null +++ b/transient.c @@ -0,0 +1,43 @@ +/* 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..1e3cf9a --- /dev/null +++ b/util.h @@ -0,0 +1,19 @@ +/* See LICENSE file for copyright and license details. */ + +#ifndef MAX +#define MAX(A, B) ((A) > (B) ? (A) : (B)) +#endif +#ifndef MIN +#define MIN(A, B) ((A) < (B) ? (A) : (B)) +#endif +#define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B)) + +#ifdef _DEBUG +#define DEBUG(...) fprintf(stderr, __VA_ARGS__) +#else +#define DEBUG(...) +#endif + +void die(const char *fmt, ...); +void *ecalloc(size_t nmemb, size_t size); +