From ee142e71ec0715cadbcdff24bb101b02c129324f Mon Sep 17 00:00:00 2001 From: Clay Gomera Date: Thu, 28 Mar 2024 17:00:58 -0400 Subject: [PATCH] Startup commit --- LICENSE | 38 + Makefile | 51 + README.md | 119 + autostart | 19 + config.h | 388 +++ config.mk | 39 + drw.c | 500 +++ drw.h | 67 + dwm.1 | 192 ++ dwm.c | 2825 +++++++++++++++++ dwm.png | Bin 0 -> 1160 bytes patches/dwm-adjacenttag-6.2.diff | 126 + patches/dwm-alpha-20230401-348f655.diff | 288 ++ .../dwm-alwayscenter-20200625-f04cac6.diff | 12 + patches/dwm-attachbottom-6.3.diff | 54 + patches/dwm-autostart-20161205-bb3bd6f.diff | 39 + patches/dwm-barpadding-20211020-a786211.diff | 118 + patches/dwm-centeredmaster-6.1.diff | 142 + patches/dwm-clientindicators-6.2.diff | 48 + patches/dwm-combo-6.1.diff | 75 + patches/dwm-cyclelayouts-20180524-6.2.diff | 93 + patches/dwm-fibonacci-20200418-c82db69.diff | 114 + patches/dwm-focusmaster-return-6.2.diff | 90 + patches/dwm-fullscreen-6.2.diff | 56 + patches/dwm-gridmode-20170909-ceac8c9.diff | 73 + patches/dwm-keychord-6.2.diff | 214 ++ patches/dwm-movestack-20211115-a786211.diff | 95 + patches/dwm-pertag-20200914-61bb8b2.diff | 177 ++ patches/dwm-restartsig-20180523-6.2.diff | 139 + patches/dwm-rmaster-6.2.diff | 113 + .../dwm-scratchpads-20200414-728d397b.diff | 199 ++ patches/dwm-statuspadding-6.3.diff | 62 + patches/dwm-sticky-6.4.diff | 141 + patches/dwm-stickyindicator-6.2.diff | 65 + patches/dwm-tag-preview-6.3.diff | 315 ++ patches/dwm-tapresize-20200819-f04cac6.diff | 128 + patches/dwm-truecenteredtitle-6.3.diff | 34 + patches/dwm-warp-6.4.diff | 79 + patches/dwm-winicon-6.3-v2.1.diff | 371 +++ transient.c | 42 + util.c | 32 + util.h | 8 + 42 files changed, 7780 insertions(+) create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 100755 autostart create mode 100644 config.h create mode 100644 config.mk create mode 100644 drw.c create mode 100644 drw.h create mode 100644 dwm.1 create mode 100644 dwm.c create mode 100644 dwm.png create mode 100644 patches/dwm-adjacenttag-6.2.diff create mode 100644 patches/dwm-alpha-20230401-348f655.diff create mode 100644 patches/dwm-alwayscenter-20200625-f04cac6.diff create mode 100644 patches/dwm-attachbottom-6.3.diff create mode 100644 patches/dwm-autostart-20161205-bb3bd6f.diff create mode 100644 patches/dwm-barpadding-20211020-a786211.diff create mode 100644 patches/dwm-centeredmaster-6.1.diff create mode 100644 patches/dwm-clientindicators-6.2.diff create mode 100644 patches/dwm-combo-6.1.diff create mode 100644 patches/dwm-cyclelayouts-20180524-6.2.diff create mode 100644 patches/dwm-fibonacci-20200418-c82db69.diff create mode 100644 patches/dwm-focusmaster-return-6.2.diff create mode 100644 patches/dwm-fullscreen-6.2.diff create mode 100644 patches/dwm-gridmode-20170909-ceac8c9.diff create mode 100644 patches/dwm-keychord-6.2.diff create mode 100644 patches/dwm-movestack-20211115-a786211.diff create mode 100644 patches/dwm-pertag-20200914-61bb8b2.diff create mode 100644 patches/dwm-restartsig-20180523-6.2.diff create mode 100644 patches/dwm-rmaster-6.2.diff create mode 100644 patches/dwm-scratchpads-20200414-728d397b.diff create mode 100644 patches/dwm-statuspadding-6.3.diff create mode 100644 patches/dwm-sticky-6.4.diff create mode 100644 patches/dwm-stickyindicator-6.2.diff create mode 100644 patches/dwm-tag-preview-6.3.diff create mode 100644 patches/dwm-tapresize-20200819-f04cac6.diff create mode 100644 patches/dwm-truecenteredtitle-6.3.diff create mode 100644 patches/dwm-warp-6.4.diff create mode 100644 patches/dwm-winicon-6.3-v2.1.diff create mode 100644 transient.c create mode 100644 util.c create mode 100644 util.h diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..995172fa4 --- /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 new file mode 100644 index 000000000..77bcbc02c --- /dev/null +++ b/Makefile @@ -0,0 +1,51 @@ +# dwm - dynamic window manager +# See LICENSE file for copyright and license details. + +include config.mk + +SRC = drw.c dwm.c util.c +OBJ = ${SRC:.c=.o} + +all: options dwm + +options: + @echo dwm build options: + @echo "CFLAGS = ${CFLAGS}" + @echo "LDFLAGS = ${LDFLAGS}" + @echo "CC = ${CC}" + +.c.o: + ${CC} -c ${CFLAGS} $< + +${OBJ}: config.h config.mk + +config.h: + cp config.def.h $@ + +dwm: ${OBJ} + ${CC} -o $@ ${OBJ} ${LDFLAGS} + +clean: + rm -f dwm ${OBJ} dwm-${VERSION}.tar.gz + +dist: clean + mkdir -p dwm-${VERSION} + cp -R LICENSE Makefile README config.def.h config.mk\ + dwm.1 drw.h util.h ${SRC} dwm.png transient.c dwm-${VERSION} + tar -cf dwm-${VERSION}.tar dwm-${VERSION} + gzip dwm-${VERSION}.tar + rm -rf dwm-${VERSION} + +install: all + mkdir -p ${DESTDIR}${PREFIX}/bin + cp -f dwm ${DESTDIR}${PREFIX}/bin + chmod 755 ${DESTDIR}${PREFIX}/bin/dwm + mkdir -p ${DESTDIR}${MANPREFIX}/man1 + sed "s/VERSION/${VERSION}/g" < dwm.1 > ${DESTDIR}${MANPREFIX}/man1/dwm.1 + chmod 644 ${DESTDIR}${MANPREFIX}/man1/dwm.1 + +uninstall: + rm -f ${DESTDIR}${PREFIX}/bin/dwm\ + ${DESTDIR}${MANPREFIX}/man1/dwm.1 + +.PHONY: all options clean dist install uninstall diff --git a/README.md b/README.md new file mode 100644 index 000000000..6d3478694 --- /dev/null +++ b/README.md @@ -0,0 +1,119 @@ +

+
+
+
+ dwm +
+

+ +

My custom build of the blazing fast and amazing dwm tiling window manager, made for absolute productivity and workflow control.

+ +![screenshot](./screenshot.png) + +## Key Features + +* A bunch of extra layouts + - *Default ones:* + - master & stack. + - floating. + - monocle + - *Added ones:* + - dwindle. + - spiral. + - centered master (also known as three column). + - centered floating master (master window floating at the center of the screen, stack on the back). + - grid. +* Keychord based keybindings + - Just like emacs, you can have chained keybindings, which exponentially extends the amount of keybindings you can have. +* Scratchpad support + - Convenient scratchpad functionality for storing and retrieving frequently used applications. +* Tag based workflow + - Each tag (also called workspaces) is meant to have it's purpose, this is achieved with an extensive set of window rules: + - *Tag 1:* Coding + - *Tag 2:* Testing + - *Tag 3:* Web browsing + - *Tag 4:* Chatting + - *Tag 5:* Audio tools + - *Tag 6:* Video tools + - *Tag 7:* Graphic tools + - *Tag 8:* Office & Document tools + - *Tag 9:* Gaming +* And much more () + +## Installation & How To Modify + +Make sure to have these dependencies installed in your system: + +``` +libX11-devel +libXft-devel +libXrender-devel +libXinerama-devel +imlib2-devel +fontconfig-devel +xinit +make +gcc +``` + +After installing them with your package manager of choice, you can do the following to get the source code and start to modify it to your liking. + +```bash +# Clone this repository +$ git clone https://github.com/d4r1us-drk/dwm.git + +# Go into the repository +$ cd dwm + +# To compile +$ make + +# To install +$ sudo make install && make clean +``` + +This repository is not a tutorial on how to modify or configure dwm, you obviusly don't need to learn C to do this, with this build you can start with an usable base and you wont even need to patch anything. If you want to add a patch though, you will need to do this manually, because most patching utilities like `patch` and `git apply` will fail due to how much of the code base I modified myself. + +To configure my build, the only file you really need to modify is the `config.h` file, which has everything commented and explained. Of course this being *my* build, it is already configured for my needs. + +## Patch list + +These are the patches I applied to this build (some of them I modified): + +- adjacenttag +- alpha +- alwayscenter +- attachbottom +- autostart +- barpadding +- centeredmaster +- clientindicators +- combo +- cyclelayouts +- fibonacci +- focusmaster-return +- fullscreen +- gridmode +- keychord +- movestack +- pertag +- restartsig +- rmaster +- scratchpads +- statuspadding +- sticky +- stickyindicator +- tag-preview +- tapresize +- truecenteredtitle +- warp +- winicon + +## Credits + +dwm is made by the suckless guys at [https://suckless.org](https://suckless.org) + +## License + +This project is licenced under the MIT License + diff --git a/autostart b/autostart new file mode 100755 index 000000000..b2afabfdc --- /dev/null +++ b/autostart @@ -0,0 +1,19 @@ +#!/bin/sh +## ____ __ ## +## / __ \_________ _/ /_____ ## +## / / / / ___/ __ `/ //_/ _ \ ## +## / /_/ / / / /_/ / ,< / __/ Clay Gomera (Drake) ## +## /_____/_/ \__,_/_/|_|\___/ My custom dwm build ## + +/usr/libexec/kf5/polkit-kde-authentication-agent-1 & +dwmblocks & +sh "$HOME"/.fehbg & +unclutter --hide-on-touch & +herbed & +picom --config "$HOME/.config/picom/picom.conf" & + +/usr/bin/gnome-keyring-daemon --start & +export SSH_AUTH_SOCK +export GPG_AGENT_INFO +export GNOME_KEYRING_CONTROL +export GNOME_KEYRING_PID diff --git a/config.h b/config.h new file mode 100644 index 000000000..25547a831 --- /dev/null +++ b/config.h @@ -0,0 +1,388 @@ +/* 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 vertpad = 0; /* vertical padding of bar */ +static const int sidepad = 0; /* horizontal padding of bar */ +static const int horizpadbar = 2; /* horizontal padding for statusbar */ +static const int vertpadbar = 4; /* vertical padding for statusbar */ +static const int rmaster = 0; /* 1 means master-area is initially on the left */ +static const char *fonts[] = {"Symbols Nerd Font Mono:size=12", "mononoki Nerd Font:size=12"}; +static const char dmenufont[] = {"mononoki Nerd Font:size=12"}; +static const char col_gray1[] = "#1d2021"; +static const char col_gray2[] = "#32302f"; +static const char col_gray3[] = "#d5c4a1"; +static const char col_gray4[] = "#fbf1c7"; +static const char col_cyan[] = "#cc241d"; +static const unsigned int baralpha = 0xd0; +static const unsigned int borderalpha = OPAQUE; +static const XPoint stickyicon[] = { {0,0}, {4,0}, {4,8}, {2,6}, {0,8}, {0,0} }; /* represents the icon as an array of vertices */ +static const XPoint stickyiconbb = {4,8}; /* defines the bottom right corner of the polygon's bounding box (speeds up scaling) */ + +#define ICONSIZE 16 /* window icon size */ +#define ICONSPACING 5 /* space between window icon and title (only when text is truncated) */ + +/* Color Definitions */ +static const char *colors[][3] = { + /* fg bg border */ + [SchemeNorm] = {col_gray3, col_gray1, col_gray2}, + [SchemeSel] = {col_gray4, col_cyan, col_cyan}, +}; + +static const unsigned int alphas[][3] = { + /* fg bg border */ + [SchemeNorm] = {OPAQUE, baralpha, borderalpha}, + [SchemeSel] = {OPAQUE, baralpha, borderalpha}, +}; + + +/* Scratchpad Definitions */ +const char *spcmd1[] = {"st", "-n", "sptrm", "-c", "sptrm", "-g", "140x35", NULL}; +const char *spcmd2[] = {"st", "-n", "sptop", "-c", "sptop", "-g", "140x35", "-e", "btop", NULL}; +const char *spcmd3[] = {"st", "-n", "sppmx", "-c", "sppmx", "-g", "140x35", "-e", "pulsemixer", NULL}; +const char *spcmd4[] = {"st", "-n", "spfli", "-c", "spfli", "-g", "140x35", "-e", "flix-cli", NULL}; +const char *spcmd5[] = {"st", "-n", "spani", "-c", "spani", "-g", "140x35", "-e", "ani-cli", NULL}; +const char *spcmd6[] = {"st", "-n", "spytf", "-c", "spytf", "-g", "140x35", "-e", "ytfzf", "-flst", NULL}; +const char *spcmd7[] = {"st", "-n", "spytm", "-c", "spytm", "-g", "140x35", "-e", "ytfzf", "-mlst", NULL}; +const char *spcmd8[] = {"st", "-n", "spmsc", "-c", "spmsc", "-g", "140x35", "-e", "musikcube", NULL}; +const char *spcmd9[] = {"st", "-n", "spflm", "-c", "spflm", "-g", "140x35", "-e", "yazi", NULL}; +const char *spcmd10[] = {"st", "-n", "sprss", "-c", "sprss", "-g", "140x35", "-e", "newsboat", NULL}; +const char *spcmd11[] = {"st", "-n", "sptut", "-c", "sptut", "-g", "140x35", "-e", "tut", NULL}; +const char *spcmd12[] = {"flatpak", "run", "com.bitwarden.desktop", NULL}; + +static Sp scratchpads[] = { + /* NAME CMD */ + {"sptrm", spcmd1}, + {"sptop", spcmd2}, + {"sppmx", spcmd3}, + {"spfli", spcmd4}, + {"spani", spcmd5}, + {"spytf", spcmd6}, + {"spytm", spcmd7}, + {"spmsc", spcmd8}, + {"spflm", spcmd9}, + {"sprss", spcmd10}, + {"sptut", spcmd11}, + {"spbit", spcmd12}, +}; + +/* Tag Definitions */ +static const char *tags[] = { + "", /* EDITOR */ + "󰙨", /* TESTING */ + "󰖟", /* WEB */ + "󰭹", /* CHAT */ + "󱡭", /* AUDIO */ + "󰕧", /* VIDEO */ + "󰏘", /* GRAPHICS */ + "󰈙", /* OFFICE */ + "󰊖" /* GAMES */ +}; + +/* Window Rules */ +static const Rule rules[] = { + /* xprop(1): + * WM_CLASS(STRING) = instance, class + * WM_NAME(STRING) = title + */ + /* CLASS - INSTANCE - TITLE - TAGS MASK - ISFLOATING - MONITOR */ + /* 0 - No Tag */ + {"Galculator", NULL, NULL, 0, 1, -1}, + {"Qalculate-gtk", NULL, NULL, 0, 1, -1}, + {"Blueman-manager", NULL, NULL, 0, 1, -1}, + {"Gpick", NULL, NULL, 0, 1, -1}, + {"Kruler", NULL, NULL, 0, 1, -1}, + {"Tor Browser", NULL, NULL, 0, 1, -1}, + {"Wpa_gui", NULL, NULL, 0, 1, -1}, + {"veromix", NULL, NULL, 0, 1, -1}, + {"xtightvncviewer", NULL, NULL, 0, 1, -1}, + /* 1 - Code Tag */ + {"editor", NULL, NULL, 1, 0, -1}, + {"lvim", NULL, NULL, 1, 0, -1}, + {"Emacs", NULL, NULL, 1, 0, -1}, + {"Godot", NULL, NULL, 1, 0, -1}, + {"neovim", NULL, NULL, 1, 0, -1}, + {"neovide", NULL, NULL, 1, 0, -1}, + /* 2 - Test Tag */ + {"Virt-manager", NULL, NULL, 1 << 1, 0, -1}, + {"Gnome-boxes", NULL, NULL, 1 << 1, 0, -1}, + /* 3 - Web Tag */ + {"LibreWolf", NULL, NULL, 1 << 2, 0, -1}, + {"libreWolf", NULL, NULL, 1 << 2, 0, -1}, + {"librewolf-default", NULL, NULL, 1 << 2, 0, -1}, + {"firefox", NULL, NULL, 1 << 2, 0, -1}, + {"Luakit", NULL, NULL, 1 << 2, 0, -1}, + {"qutebrowser", NULL, NULL, 1 << 2, 0, -1}, + {"Chromium", NULL, NULL, 1 << 2, 0, -1}, + {"Brave-browser", NULL, NULL, 1 << 2, 0, -1}, + {"newsboat", NULL, NULL, 1 << 2, 0, -1}, + /* 4 - Chat Tag */ + {"Signal", NULL, NULL, 1 << 3, 0, -1}, + {"gomuks", NULL, NULL, 1 << 3, 0, -1}, + {"Revolt", NULL, NULL, 1 << 3, 0, -1}, + {"Element", NULL, NULL, 1 << 3, 0, -1}, + /* 5 - Audio Tools Tag */ + {"Audacity", NULL, NULL, 1 << 4, 0, -1}, + {"Ardour", NULL, NULL, 1 << 4, 0, -1}, + {"Carla2", NULL, NULL, 1 << 4, 0, -1}, + {"Carla2-Control", NULL, NULL, 1 << 4, 0, -1}, + {"QjackCtl", NULL, NULL, 1 << 4, 1, -1}, + {"lsp-plugins", NULL, NULL, 1 << 4, 1, -1}, + {"qpwgraph", NULL, NULL, 1 << 4, 0, -1}, + {"Cadence", NULL, NULL, 1 << 4, 0, -1}, + {"easyeffects", NULL, NULL, 1 << 4, 0, -1}, + /* 6 - Video Tag */ + {"kdenlive", NULL, NULL, 1 << 5, 0, -1}, + {"Pitivi", NULL, NULL, 1 << 5, 0, -1}, + {"Natron", NULL, NULL, 1 << 5, 0, -1}, + {"SimpleScreenRecorder", NULL, NULL, 1 << 5, 0, -1}, + {"Ghb", NULL, NULL, 1 << 5, 0, -1}, + {"obs", NULL, NULL, 1 << 5, 0, -1}, + {"mpv", NULL, NULL, 1 << 5, 0, -1}, + /* 7 - Graphics Tools Tag */ + {"Blender", NULL, NULL, 1 << 6, 0, -1}, + {"Gimp-2.10", NULL, NULL, 1 << 6, 0, -1}, + {"krita", NULL, NULL, 1 << 6, 0, -1}, + {"Inkscape", NULL, NULL, 1 << 6, 0, -1}, + {"Xournalpp", NULL, NULL, 1 << 6, 0, -1}, + {"Com.github.xournalpp.xournalpp", NULL, NULL, 1 << 6, 0, -1}, + /* 8 - Office Tag */ + {"DesktopEditors", NULL, NULL, 1 << 7, 0, -1}, + {"Soffice", "soffice", NULL, 1 << 7, 0, -1}, + {"libreoffice-startcenter", NULL, NULL, 1 << 7, 0, -1}, + {"libreoffice-calc", NULL, NULL, 1 << 7, 0, -1}, + {"libreoffice-writer", NULL, NULL, 1 << 7, 0, -1}, + {"libreoffice-impress", NULL, NULL, 1 << 7, 0, -1}, + {"libreoffice-base", NULL, NULL, 1 << 7, 0, -1}, + {"libreoffice-draw", NULL, NULL, 1 << 7, 0, -1}, + {"libreoffice-math", NULL, NULL, 1 << 7, 0, -1}, + {"Joplin", NULL, NULL, 1 << 7, 0, -1}, + {"Notesnook", NULL, NULL, 1 << 7, 0, -1}, + {"com.github.johnfactotum.Foliate", NULL, NULL, 1 << 7, 0, -1}, + /* 9 - Games Tag */ + {"retroarch", NULL, NULL, 1 << 8, 0, -1}, + {"steam", NULL, NULL, 1 << 8, 0, -1}, + {"airshipper", NULL, NULL, 1 << 8, 0, -1}, + {"pyrogenesis", NULL, NULL, 1 << 8, 0, -1}, + {"DarkPlaces", NULL, NULL, 1 << 8, 0, -1}, + {"xonotic-sdl", NULL, NULL, 1 << 8, 0, -1}, + {"supertuxkart-sdl", NULL, NULL, 1 << 8, 0, -1}, + {"supertux2", NULL, NULL, 1 << 8, 0, -1}, + {"Minetest", NULL, NULL, 1 << 8, 0, -1}, + {"openttd", NULL, NULL, 1 << 8, 0, -1}, + {"warzone2100", NULL, NULL, 1 << 8, 0, -1}, + {"wesnoth", NULL, NULL, 1 << 8, 0, -1}, + /* Scratchpads */ + {NULL, "sptrm", NULL, SPTAG(0), 1, -1}, + {NULL, "sptop", NULL, SPTAG(1), 1, -1}, + {NULL, "sppmx", NULL, SPTAG(2), 1, -1}, + {NULL, "spfli", NULL, SPTAG(3), 1, -1}, + {NULL, "spani", NULL, SPTAG(4), 1, -1}, + {NULL, "spytf", NULL, SPTAG(5), 1, -1}, + {NULL, "spytm", NULL, SPTAG(6), 1, -1}, + {NULL, "spmsc", NULL, SPTAG(7), 1, -1}, + {NULL, "spflm", NULL, SPTAG(8), 1, -1}, + {NULL, "sprss", NULL, SPTAG(9), 1, -1}, + {NULL, "sptut", NULL, SPTAG(10), 1, -1}, + {"Bitwarden", NULL, NULL, SPTAG(11), 1, -1}, +}; + +/* Layout(s) */ +static const float mfact = 0.5; /* 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 }, + { "[M]", monocle }, + { "|||", centeredmaster }, + { ">|>", centeredfloatingmaster }, + { "[@]", spiral }, + { "[\\]", dwindle }, + { "HHH", grid }, + { NULL, NULL }, +}; + +/* Tag control keybindings */ +#define MODKEY Mod4Mask +#define TAGKEYS(KEY,TAG) \ + &((Keychord){1, {{MODKEY, KEY}}, comboview, {.ui = 1 << TAG} }), \ + &((Keychord){1, {{MODKEY|ControlMask, KEY}}, toggleview, {.ui = 1 << TAG} }), \ + &((Keychord){1, {{MODKEY|ShiftMask, KEY}}, combotag, {.ui = 1 << TAG} }), \ + &((Keychord){1, {{MODKEY|ControlMask|ShiftMask, KEY}}, toggletag, {.ui = 1 << TAG} }), + +/* Helper for spawning shell commands in the pre dwm-5.0 fashion */ +#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } } + +/* Main commands */ +static const char *dmenucmd[] = { "dmenu_run", "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL }; +static const char *termcmd[] = { "st", NULL }; +static const char *editor[] = { "st", "-n", "editor", "-c", "editor", "-e", ".local/bin/lvim", NULL}; +static const char *browser[] = { "flatpak", "run", "org.mozilla.firefox", NULL }; +static const char *chat[] = { "flatpak", "run", "org.signal.Signal", NULL }; +static const char *vm[] = { "virt-manager", NULL }; +static const char *office[] = { "flatpak", "run", "org.libreoffice.LibreOffice", NULL }; +static const char *videoeditor[] = { "flatpak", "run", "org.kde.kdenlive", NULL }; +static const char *imgeditor[] = { "flatpak", "run", "org.gimp.GIMP", NULL }; +static const char *handnote[] = { "flatpak", "run", "com.github.xournalpp.xournalpp", NULL }; + +/* Keybindings */ +static Keychord *keychords[] = { + /* Keys function argument */ + /* Terminal */ + &((Keychord){1, {{MODKEY, XK_Return}}, spawn, {.v = termcmd } }), /* Launch terminal */ + + /* dmenu */ + &((Keychord){2, {{MODKEY, XK_p}, {0, XK_r}}, spawn, {.v = dmenucmd } }), /* Launch application launcher menu */ + &((Keychord){2, {{MODKEY, XK_p}, {0, XK_d}}, spawn, SHCMD("$HOME/.config/suckless/dmenu/scripts/dmenu_drun") }), /* Launch application launcher menu (desktop files) */ + &((Keychord){2, {{MODKEY, XK_p}, {0, XK_i}}, spawn, SHCMD("$HOME/.config/suckless/dmenu/scripts/dmenu_wifi") }), /* Launch wifi configuration menu */ + &((Keychord){2, {{MODKEY, XK_p}, {0, XK_s}}, spawn, SHCMD("$HOME/.config/suckless/dmenu/scripts/dmenu_scrot") }), /* Launch screenshot/screencast menu */ + &((Keychord){2, {{MODKEY, XK_p}, {0, XK_w}}, spawn, SHCMD("$HOME/.config/suckless/dmenu/scripts/dmenu_wall") }), /* Launch wallpaper configuration menu */ + &((Keychord){2, {{MODKEY, XK_p}, {0, XK_e}}, spawn, SHCMD("$HOME/.config/suckless/dmenu/scripts/dmenu_edit") }), /* Launch open-in-editor menu */ + &((Keychord){2, {{MODKEY, XK_p}, {0, XK_b}}, spawn, SHCMD("$HOME/.config/suckless/dmenu/scripts/dmenu_blue") }), /* Launch bluetooth configuration menu */ + &((Keychord){2, {{MODKEY, XK_p}, {0, XK_q}}, spawn, SHCMD("$HOME/.config/suckless/dmenu/scripts/dmenu_power") }), /* Launch power menu */ + + /* Apps */ + &((Keychord){2, {{MODKEY, XK_a}, {0, XK_e}}, spawn, {.v = editor } }), /* Launch text editor (tag 1) */ + &((Keychord){2, {{MODKEY, XK_a}, {0, XK_v}}, spawn, {.v = vm } }), /* Launch vm manager (tag 2) */ + &((Keychord){2, {{MODKEY, XK_a}, {0, XK_w}}, spawn, {.v = browser } }), /* Launch web browser (tag 3) */ + &((Keychord){2, {{MODKEY, XK_a}, {0, XK_c}}, spawn, {.v = chat } }), /* Launch chat app (tag 4) */ + &((Keychord){2, {{MODKEY, XK_a}, {0, XK_o}}, spawn, {.v = office } }), /* Launch office suite (tag 8) */ + &((Keychord){2, {{MODKEY, XK_a}, {0, XK_k}}, spawn, {.v = videoeditor } }), /* Launch video editor (tag 6) */ + &((Keychord){2, {{MODKEY, XK_a}, {0, XK_i}}, spawn, {.v = imgeditor } }), /* Launch image editor (tag 7) */ + &((Keychord){2, {{MODKEY, XK_a}, {0, XK_x}}, spawn, {.v = handnote } }), /* Launch hand written notes app (tag 7) */ + + /* Keyboard Layouts */ + &((Keychord){2, {{MODKEY, XK_x}, {0, XK_e}}, spawn, SHCMD("setxkbmap -layout es && pkill -RTMIN+10 dwmblocks") }), /* Switch to Spanish keyboard layout */ + &((Keychord){2, {{MODKEY, XK_x}, {0, XK_u}}, spawn, SHCMD("setxkbmap -layout us && pkill -RTMIN+10 dwmblocks") }), /* Switch to US keyboard layout */ + + /* Audio Control */ + &((Keychord){1, {{0, XF86XK_AudioRaiseVolume}}, spawn, SHCMD("pamixer -i 5 && pkill -RTMIN+10 dwmblocks") }), /* Increase volume by 5%+ */ + &((Keychord){1, {{0, XF86XK_AudioLowerVolume}}, spawn, SHCMD("pamixer -d 5 && pkill -RTMIN+10 dwmblocks") }), /* Decrease volume by 5%- */ + &((Keychord){1, {{0, XF86XK_AudioMute}}, spawn, SHCMD("pamixer -t && pkill -RTMIN+10 dwmblocks") }), /* Toggle mute */ + &((Keychord){1, {{0, XF86XK_AudioMicMute}}, spawn, SHCMD("pamixer --default-source -t && pkill -RTMIN+10 dwmblocks") }), /* Toggle mic mute */ + + /* Brightness Control */ + &((Keychord){1, {{0, XF86XK_MonBrightnessUp}}, spawn, SHCMD("brightnessctl s 5%+ && pkill -RTMIN+10 dwmblocks") }), /* Increase brightness by 5%+ */ + &((Keychord){1, {{0, XF86XK_MonBrightnessDown}}, spawn, SHCMD("brightnessctl s 5%- && pkill -RTMIN+10 dwmblocks") }), /* Decrease brightness by 5%- *, + + /* Display Control */ + &((Keychord){1, {{0, XF86XK_Display}}, spawn, SHCMD("arandr") }), /* Launch display configuration tool */ + + /* Media Control */ + &((Keychord){1, {{0, XF86XK_AudioPause}}, spawn, SHCMD("playerctl play-pause") }), /* Pause playback */ + &((Keychord){1, {{0, XF86XK_AudioPlay}}, spawn, SHCMD("playerctl play-pause") }), /* Also pause playback */ + &((Keychord){1, {{0, XF86XK_AudioNext}}, spawn, SHCMD("playerctl next") }), /* Next song/media */ + &((Keychord){1, {{0, XF86XK_AudioPrev}}, spawn, SHCMD("playerctl previous") }), /* Previous song/media */ + &((Keychord){1, {{0, XF86XK_AudioStop}}, spawn, SHCMD("playerctl stop") }), /* Stop playback */ + + /* RSS Feed */ + &((Keychord){1, {{0, XF86XK_News}}, spawn, SHCMD("st -n newsboat -c newsboat -e newsboat") }), /* Launch RSS feed reader with media key */ + + /* Window Management */ + &((Keychord){1, {{MODKEY, XK_j}}, focusstack, {.i = +1 } }), /* Focus next window in the stack */ + &((Keychord){1, {{MODKEY, XK_k}}, focusstack, {.i = -1 } }), /* Focus previous window in the stack */ + &((Keychord){1, {{MODKEY|ShiftMask, XK_j}}, movestack, {.i = -1 } }), /* Move window to next position in the stack */ + &((Keychord){1, {{MODKEY|ShiftMask, XK_k}}, movestack, {.i = +1 } }), /* Move window to previous position in the stack */ + &((Keychord){1, {{MODKEY, XK_i}}, incnmaster, {.i = +1 } }), /* Increase master area window count */ + &((Keychord){1, {{MODKEY, XK_d}}, incnmaster, {.i = -1 } }), /* Decrease master area window count */ + &((Keychord){1, {{MODKEY, XK_h}}, setmfact, {.f = -0.05} }), /* Decrease master area size */ + &((Keychord){1, {{MODKEY, XK_l}}, setmfact, {.f = +0.05} }), /* Increase master area size */ + &((Keychord){1, {{MODKEY|ControlMask, XK_Return}}, zoom, {0} }), /* Move window to master area */ + &((Keychord){1, {{MODKEY|ShiftMask, XK_Return}}, focusmaster, {0} }), /* Focus master area */ + &((Keychord){1, {{MODKEY|Mod1Mask, XK_Return}}, togglemaster, {0} }), /* Toggle master area position (depends in the layout) */ + &((Keychord){1, {{MODKEY|ShiftMask, XK_Tab}}, togglesticky, {0} }), /* Toggle sticky on active window (show on all tags) */ + &((Keychord){1, {{MODKEY, XK_Tab}}, view, {0} }), /* Focus last active tag */ + &((Keychord){1, {{MODKEY, XK_q}}, killclient, {0} }), /* Kill active window */ + &((Keychord){2, {{MODKEY, XK_c}, {0, XK_t}}, setlayout, {.v = &layouts[0]} }), /* Switch to master-stack layout */ + &((Keychord){2, {{MODKEY, XK_c}, {0, XK_f}}, setlayout, {.v = &layouts[1]} }), /* Switch to floating layout */ + &((Keychord){2, {{MODKEY, XK_c}, {0, XK_m}}, setlayout, {.v = &layouts[2]} }), /* Switch to monocle layout */ + &((Keychord){2, {{MODKEY, XK_c}, {0, XK_c}}, setlayout, {.v = &layouts[3]} }), /* Switch to centerd master layout */ + &((Keychord){2, {{MODKEY, XK_c}, {0, XK_v}}, setlayout, {.v = &layouts[4]} }), /* Switch to floating centered master layout */ + &((Keychord){2, {{MODKEY, XK_c}, {0, XK_d}}, setlayout, {.v = &layouts[5]} }), /* Switch to dwindle layout */ + &((Keychord){2, {{MODKEY, XK_c}, {0, XK_s}}, setlayout, {.v = &layouts[6]} }), /* Switch to spiral layout */ + &((Keychord){2, {{MODKEY, XK_c}, {0, XK_g}}, setlayout, {.v = &layouts[7]} }), /* Switch to grid layout */ + &((Keychord){1, {{MODKEY|ShiftMask, XK_space}}, setlayout, {0} }), /* Switch to last used layout */ + &((Keychord){1, {{MODKEY, XK_space}}, cyclelayout, {.i = -1 } }), /* Switch to next layout */ + &((Keychord){1, {{MODKEY|ControlMask, XK_space}}, cyclelayout, {.i = +1 } }), /* Switch to previous layout */ + &((Keychord){1, {{MODKEY, XK_f}}, fullscreen, {0} }), /* Toggle fullscreen mode on active window */ + &((Keychord){1, {{MODKEY|Mod1Mask, XK_space}}, togglefloating, {0} }), /* Toggle floating mode on active window */ + &((Keychord){1, {{MODKEY, XK_0}}, view, {.ui = ~0 } }), /* View all windows from all tags */ + &((Keychord){1, {{MODKEY|ShiftMask, XK_0}}, tag, {.ui = ~0 } }), /* View active window on all tags (similar to sticky, more nuclear) */ + &((Keychord){1, {{MODKEY, XK_comma}}, focusmon, {.i = -1 } }), /* Focus next screen */ + &((Keychord){1, {{MODKEY, XK_period}}, focusmon, {.i = +1 } }), /* Focus previous screen */ + &((Keychord){1, {{MODKEY|ShiftMask, XK_comma}}, tagmon, {.i = -1 } }), /* Move active window to next screen */ + &((Keychord){1, {{MODKEY|ShiftMask, XK_period}}, tagmon, {.i = +1 } }), /* Move active window to previous screen */ + &((Keychord){1, {{MODKEY, XK_b}}, togglebar, {0} }), /* Toggle bar */ + + /* Scratchpads */ + &((Keychord){2, {{MODKEY, XK_s}, {0, XK_Return}}, togglescratch, {.ui = 0 } }), /* Toggle scratch terminal */ + &((Keychord){2, {{MODKEY, XK_s}, {0, XK_b}}, togglescratch, {.ui = 1 } }), /* Toggle system monitor scratchpad */ + &((Keychord){2, {{MODKEY, XK_s}, {0, XK_p}}, togglescratch, {.ui = 2 } }), /* Toggle audio mixer scratchpad */ + &((Keychord){2, {{MODKEY, XK_s}, {0, XK_f}}, togglescratch, {.ui = 3 } }), /* Toggle flix-cli scratchpad */ + &((Keychord){2, {{MODKEY, XK_s}, {0, XK_a}}, togglescratch, {.ui = 4 } }), /* Toggle ani-cli scratchpad */ + &((Keychord){2, {{MODKEY, XK_s}, {0, XK_y}}, togglescratch, {.ui = 5 } }), /* Toggle ytfzf scratchpad */ + &((Keychord){2, {{MODKEY, XK_s}, {0, XK_n}}, togglescratch, {.ui = 6 } }), /* Toggle ytfzf (music) scratchpad */ + &((Keychord){2, {{MODKEY, XK_s}, {0, XK_m}}, togglescratch, {.ui = 7 } }), /* Toggle musikcube scratchpad */ + &((Keychord){2, {{MODKEY, XK_s}, {0, XK_v}}, togglescratch, {.ui = 8 } }), /* Toggle yazi scratchpad */ + &((Keychord){2, {{MODKEY, XK_s}, {0, XK_r}}, togglescratch, {.ui = 9 } }), /* Toggle newsboat scratchpad */ + &((Keychord){2, {{MODKEY, XK_s}, {0, XK_t}}, togglescratch, {.ui = 10 } }), /* Toggle tut scratchpad */ + &((Keychord){2, {{MODKEY, XK_s}, {0, XK_k}}, togglescratch, {.ui = 11 } }), /* Toggle Bitwarden scratchpad */ + + /* Session Management */ + &((Keychord){1, {{MODKEY|ControlMask|ShiftMask, XK_q}}, quit, {0} }), + &((Keychord){1, {{MODKEY|ControlMask, XK_r}}, quit, {1} }), + + /* Tag Keys */ + 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) + &((Keychord){1, {{MODKEY, XK_Right}}, viewnext, {0} }), /* Switch to next tag */ + &((Keychord){1, {{MODKEY, XK_Left}}, viewprev, {0} }), /* Switch to previous tag */ + &((Keychord){1, {{MODKEY|ShiftMask, XK_Right}}, tagtonext, {0} }), /* Move active window to next tag */ + &((Keychord){1, {{MODKEY|ShiftMask, XK_Left}}, tagtoprev, {0} }), /* Move active window to previous tag */ +}; + +/* Mouse scroll resize */ +static const int scrollsensetivity = 30; /* 1 means resize window by 1 pixel for each scroll event */ + +/* Resizemousescroll direction argument list */ +static const int scrollargs[][2] = { + /* width change height change */ + { +scrollsensetivity, 0 }, + { -scrollsensetivity, 0 }, + { 0, +scrollsensetivity }, + { 0, -scrollsensetivity }, +}; + +/* Mouse Bindings */ +/* click can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */ +static const Button buttons[] = { + /* click event mask button function argument */ + { ClkLtSymbol, 0, Button1, setlayout, {0} }, + { ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} }, + { ClkWinTitle, 0, Button2, zoom, {0} }, + { ClkStatusText, 0, Button2, spawn, {.v = termcmd } }, + { ClkClientWin, MODKEY, Button1, movemouse, {0} }, + { ClkClientWin, MODKEY, Button2, togglefloating, {0} }, + { ClkClientWin, MODKEY, Button3, resizemouse, {0} }, + { ClkClientWin, MODKEY, Button4, resizemousescroll, {.v = &scrollargs[0]} }, + { ClkClientWin, MODKEY, Button5, resizemousescroll, {.v = &scrollargs[1]} }, + { ClkClientWin, MODKEY, Button6, resizemousescroll, {.v = &scrollargs[2]} }, + { ClkClientWin, MODKEY, Button7, resizemousescroll, {.v = &scrollargs[3]} }, + { ClkTagBar, 0, Button1, view, {0} }, + { ClkTagBar, 0, Button3, toggleview, {0} }, + { ClkTagBar, MODKEY, Button1, tag, {0} }, + { ClkTagBar, MODKEY, Button3, toggletag, {0} }, +}; + + diff --git a/config.mk b/config.mk new file mode 100644 index 000000000..18af5189f --- /dev/null +++ b/config.mk @@ -0,0 +1,39 @@ +# dwm version +VERSION = 6.4 + +# Customize below to fit your system + +# paths +PREFIX = /usr/local +MANPREFIX = ${PREFIX}/share/man + +X11INC = /usr/X11R6/include +X11LIB = /usr/X11R6/lib + +# Xinerama, comment if you don't want it +XINERAMALIBS = -lXinerama +XINERAMAFLAGS = -DXINERAMA + +# freetype +FREETYPELIBS = -lfontconfig -lXft +FREETYPEINC = /usr/include/freetype2 +# OpenBSD (uncomment) +#FREETYPEINC = ${X11INC}/freetype2 +#MANPREFIX = ${PREFIX}/man + +# includes and libs +INCS = -I${X11INC} -I${FREETYPEINC} +LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} -lXrender -lImlib2 + +# flags +CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} +#CFLAGS = -g -std=c99 -pedantic -Wall -O0 ${INCS} ${CPPFLAGS} +CFLAGS = -std=c99 -pedantic -Wall -Wno-deprecated-declarations -O3 -march=native ${INCS} ${CPPFLAGS} +LDFLAGS = ${LIBS} + +# Solaris +#CFLAGS = -fast ${INCS} -DVERSION=\"${VERSION}\" +#LDFLAGS = ${LIBS} + +# compiler and linker +CC = cc diff --git a/drw.c b/drw.c new file mode 100644 index 000000000..aa4ead18c --- /dev/null +++ b/drw.c @@ -0,0 +1,500 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include +#include +#include +#include +#include + +#include "drw.h" +#include "util.h" + +#define UTF_INVALID 0xFFFD +#define UTF_SIZ 4 + +static const unsigned char utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; +static const unsigned char utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; +static const long utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000}; +static const long utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; + +static long utf8decodebyte(const char c, size_t *i) { + for (*i = 0; *i < (UTF_SIZ + 1); ++(*i)) + if (((unsigned char)c & utfmask[*i]) == utfbyte[*i]) + return (unsigned char)c & ~utfmask[*i]; + return 0; +} + +static size_t utf8validate(long *u, size_t i) { + if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF)) + *u = UTF_INVALID; + for (i = 1; *u > utfmax[i]; ++i) + ; + return i; +} + +static size_t utf8decode(const char *c, long *u, size_t clen) { + size_t i, j, len, type; + long udecoded; + + *u = UTF_INVALID; + if (!clen) + return 0; + udecoded = utf8decodebyte(c[0], &len); + if (!BETWEEN(len, 1, UTF_SIZ)) + return 1; + for (i = 1, j = 1; i < clen && j < len; ++i, ++j) { + udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type); + if (type) + return j; + } + if (j < len) + return 0; + *u = udecoded; + utf8validate(u, len); + + return len; +} + +Drw *drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h, Visual *visual, unsigned int depth, Colormap cmap) { + Drw *drw = ecalloc(1, sizeof(Drw)); + + drw->dpy = dpy; + drw->screen = screen; + drw->root = root; + drw->w = w; + drw->h = h; + drw->visual = visual; + drw->depth = depth; + drw->cmap = cmap; + drw->drawable = XCreatePixmap(dpy, root, w, h, depth); + drw->picture = XRenderCreatePicture(dpy, drw->drawable, XRenderFindVisualFormat(dpy, visual), 0, NULL); + drw->gc = XCreateGC(dpy, drw->drawable, 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->picture) + XRenderFreePicture(drw->dpy, drw->picture); + if (drw->drawable) + XFreePixmap(drw->dpy, drw->drawable); + drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, drw->depth); + drw->picture = XRenderCreatePicture(drw->dpy, drw->drawable, XRenderFindVisualFormat(drw->dpy, drw->visual), 0, NULL); +} + +void drw_free(Drw *drw) { + XRenderFreePicture(drw->dpy, drw->picture); + XFreePixmap(drw->dpy, drw->drawable); + XFreeGC(drw->dpy, drw->gc); + drw_fontset_free(drw->fonts); + free(drw); +} + +/* This function is an implementation detail. Library users should use + * drw_fontset_create instead. + */ +static Fnt * xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern) { + Fnt *font; + XftFont *xfont = NULL; + FcPattern *pattern = NULL; + + if (fontname) { + /* Using the pattern found at font->xfont->pattern does not yield the + * same substitution results as using the pattern returned by + * FcNameParse; using the latter results in the desired fallback + * behaviour whereas the former just results in missing-character + * rectangles being drawn, at least with some fonts. */ + if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fontname))) { + fprintf(stderr, "error, cannot load font from name: '%s'\n", fontname); + return NULL; + } + if (!(pattern = FcNameParse((FcChar8 *) fontname))) { + fprintf(stderr, "error, cannot parse font name to pattern: '%s'\n", fontname); + XftFontClose(drw->dpy, xfont); + return NULL; + } + } else if (fontpattern) { + if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) { + fprintf(stderr, "error, cannot load font from pattern.\n"); + return NULL; + } + } else { + die("no font specified."); + } + + font = ecalloc(1, sizeof(Fnt)); + font->xfont = xfont; + font->pattern = pattern; + font->h = xfont->ascent + xfont->descent; + font->dpy = drw->dpy; + + return font; +} + +static void xfont_free(Fnt *font) { + if (!font) + return; + if (font->pattern) + FcPatternDestroy(font->pattern); + XftFontClose(font->dpy, font->xfont); + free(font); +} + +Fnt* drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount) { + Fnt *cur, *ret = NULL; + size_t i; + + if (!drw || !fonts) + return NULL; + + for (i = 1; i <= fontcount; i++) { + if ((cur = xfont_create(drw, fonts[fontcount - i], NULL))) { + cur->next = ret; + ret = cur; + } + } + return (drw->fonts = ret); +} + +void drw_fontset_free(Fnt *font) { + if (font) { + drw_fontset_free(font->next); + xfont_free(font); + } +} + +void drw_clr_create(Drw *drw, Clr *dest, const char *clrname, unsigned int alpha) { + if (!drw || !dest || !clrname) + return; + + if (!XftColorAllocName(drw->dpy, drw->visual, drw->cmap, + clrname, dest)) + die("error, cannot allocate color '%s'", clrname); + + dest->pixel = (dest->pixel & 0x00ffffffU) | (alpha << 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, const char *clrnames[], const unsigned int alphas[], 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], alphas[i]); + return ret; +} + +void drw_setfontset(Drw *drw, Fnt *set) { + if (drw) + drw->fonts = set; +} + +void drw_setscheme(Drw *drw, Clr *scm) { + if (drw) + drw->scheme = scm; +} + +Picture drw_picture_create_resized(Drw *drw, char *src, unsigned int srcw, unsigned int srch, unsigned int dstw, unsigned int dsth) { + Pixmap pm; + Picture pic; + GC gc; + + if (srcw <= (dstw << 1u) && srch <= (dsth << 1u)) { + XImage img = { + srcw, srch, 0, ZPixmap, src, + ImageByteOrder(drw->dpy), BitmapUnit(drw->dpy), BitmapBitOrder(drw->dpy), 32, + 32, 0, 32, + 0, 0, 0 + }; + XInitImage(&img); + + pm = XCreatePixmap(drw->dpy, drw->root, srcw, srch, 32); + gc = XCreateGC(drw->dpy, pm, 0, NULL); + XPutImage(drw->dpy, pm, gc, &img, 0, 0, 0, 0, srcw, srch); + XFreeGC(drw->dpy, gc); + + pic = XRenderCreatePicture(drw->dpy, pm, XRenderFindStandardFormat(drw->dpy, PictStandardARGB32), 0, NULL); + XFreePixmap(drw->dpy, pm); + + XRenderSetPictureFilter(drw->dpy, pic, FilterBilinear, NULL, 0); + XTransform xf; + xf.matrix[0][0] = (srcw << 16u) / dstw; xf.matrix[0][1] = 0; xf.matrix[0][2] = 0; + xf.matrix[1][0] = 0; xf.matrix[1][1] = (srch << 16u) / dsth; xf.matrix[1][2] = 0; + xf.matrix[2][0] = 0; xf.matrix[2][1] = 0; xf.matrix[2][2] = 65536; + XRenderSetPictureTransform(drw->dpy, pic, &xf); + } else { + Imlib_Image origin = imlib_create_image_using_data(srcw, srch, (DATA32 *)src); + if (!origin) return None; + imlib_context_set_image(origin); + imlib_image_set_has_alpha(1); + Imlib_Image scaled = imlib_create_cropped_scaled_image(0, 0, srcw, srch, dstw, dsth); + imlib_free_image_and_decache(); + if (!scaled) return None; + imlib_context_set_image(scaled); + imlib_image_set_has_alpha(1); + + XImage img = { + dstw, dsth, 0, ZPixmap, (char *)imlib_image_get_data_for_reading_only(), + ImageByteOrder(drw->dpy), BitmapUnit(drw->dpy), BitmapBitOrder(drw->dpy), 32, + 32, 0, 32, + 0, 0, 0 + }; + XInitImage(&img); + + pm = XCreatePixmap(drw->dpy, drw->root, dstw, dsth, 32); + gc = XCreateGC(drw->dpy, pm, 0, NULL); + XPutImage(drw->dpy, pm, gc, &img, 0, 0, 0, 0, dstw, dsth); + imlib_free_image_and_decache(); + XFreeGC(drw->dpy, gc); + + pic = XRenderCreatePicture(drw->dpy, pm, XRenderFindStandardFormat(drw->dpy, PictStandardARGB32), 0, NULL); + XFreePixmap(drw->dpy, pm); + } + + return pic; +} + +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); +} + +void drw_pic(Drw *drw, int x, int y, unsigned int w, unsigned int h, Picture pic) { + if (!drw) + return; + XRenderComposite(drw->dpy, PictOpOver, pic, None, drw->picture, 0, 0, 0, 0, x, y, w, h); +} + +/* wrapper function to scale and draw a polygon with X11 */ +void drw_polygon(Drw *drw, int x, int y, int ow, int oh, int sw, int sh, const XPoint *points, int npoints, int shape, int filled) { + if (!drw || !drw->scheme) + return; + XSetForeground(drw->dpy, drw->gc, drw->scheme[ColFg].pixel); + if (!filled) { /* reduces the scaled width and height by 1 when drawing the outline to compensate for X11 drawing the line 1 pixel over */ + sw -= 1; + sh -= 1; + } + XPoint scaledpoints[npoints]; + memcpy(scaledpoints, points, npoints); + for (int v = 0; v < npoints; v++) + scaledpoints[v] = (XPoint){ .x = points[v].x * sw / ow + x, .y = points[v].y * sh / oh + y }; + if (filled) + XFillPolygon(drw->dpy, drw->drawable, drw->gc, scaledpoints, npoints, shape, CoordModeOrigin); /* Change shape to 'Convex' or 'Complex' in dwm.c if the shape is not 'Nonconvex' */ + else + XDrawLines(drw->dpy, drw->drawable, drw->gc, scaledpoints, npoints, CoordModeOrigin); +} + + +int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert) { + int i, ty, ellipsis_x = 0; + unsigned int tmpw, ew, ellipsis_w = 0, ellipsis_len; + XftDraw *d = NULL; + Fnt *usedfont, *curfont, *nextfont; + int utf8strlen, utf8charlen, render = x || y || w || h; + long utf8codepoint = 0; + const char *utf8str; + FcCharSet *fccharset; + FcPattern *fcpattern; + FcPattern *match; + XftResult result; + int charexists = 0, overflow = 0; + /* keep track of a couple codepoints for which we have no match. */ + enum { nomatches_len = 64 }; + static struct { long codepoint[nomatches_len]; unsigned int idx; } nomatches; + static unsigned int ellipsis_width = 0; + + if (!drw || (render && (!drw->scheme || !w)) || !text || !drw->fonts) + return 0; + + if (!render) { + w = invert ? invert : ~invert; + } else { + XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel); + XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); + d = XftDrawCreate(drw->dpy, drw->drawable, drw->visual, drw->cmap); + x += lpad; + w -= lpad; + } + + usedfont = drw->fonts; + if (!ellipsis_width && render) + ellipsis_width = drw_fontset_getwidth(drw, "..."); + while (1) { + ew = ellipsis_len = utf8strlen = 0; + utf8str = text; + nextfont = NULL; + while (*text) { + utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ); + for (curfont = drw->fonts; curfont; curfont = curfont->next) { + charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint); + if (charexists) { + drw_font_getexts(curfont, text, utf8charlen, &tmpw, NULL); + if (ew + ellipsis_width <= w) { + /* keep track where the ellipsis still fits */ + ellipsis_x = x + ew; + ellipsis_w = w - ew; + ellipsis_len = utf8strlen; + } + + if (ew + tmpw > w) { + overflow = 1; + /* called from drw_fontset_getwidth_clamp(): + * it wants the width AFTER the overflow + */ + if (!render) + x += tmpw; + else + utf8strlen = ellipsis_len; + } else if (curfont == usedfont) { + utf8strlen += utf8charlen; + text += utf8charlen; + ew += tmpw; + } else { + nextfont = curfont; + } + break; + } + } + + if (overflow || !charexists || nextfont) + break; + else + charexists = 0; + } + + if (utf8strlen) { + if (render) { + ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent; + XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg], + usedfont->xfont, x, ty, (XftChar8 *)utf8str, utf8strlen); + } + x += ew; + w -= ew; + } + if (render && overflow) + drw_text(drw, ellipsis_x, y, ellipsis_w, h, 0, "...", invert); + + if (!*text || overflow) { + break; + } else if (nextfont) { + charexists = 0; + usedfont = nextfont; + } else { + /* Regardless of whether or not a fallback font is found, the + * character must be drawn. */ + charexists = 1; + + for (i = 0; i < nomatches_len; ++i) { + /* avoid calling XftFontMatch if we know we won't find a match */ + if (utf8codepoint == nomatches.codepoint[i]) + goto no_match; + } + + fccharset = FcCharSetCreate(); + FcCharSetAddChar(fccharset, utf8codepoint); + + if (!drw->fonts->pattern) { + /* Refer to the comment in xfont_create for more information. */ + die("the first font in the cache must be loaded from a font string."); + } + + fcpattern = FcPatternDuplicate(drw->fonts->pattern); + FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset); + FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue); + + FcConfigSubstitute(NULL, fcpattern, FcMatchPattern); + FcDefaultSubstitute(fcpattern); + match = XftFontMatch(drw->dpy, drw->screen, fcpattern, &result); + + FcCharSetDestroy(fccharset); + FcPatternDestroy(fcpattern); + + if (match) { + usedfont = xfont_create(drw, NULL, match); + if (usedfont && XftCharExists(drw->dpy, usedfont->xfont, utf8codepoint)) { + for (curfont = drw->fonts; curfont->next; curfont = curfont->next) + ; /* NOP */ + curfont->next = usedfont; + } else { + xfont_free(usedfont); + nomatches.codepoint[++nomatches.idx % nomatches_len] = utf8codepoint; +no_match: + usedfont = drw->fonts; + } + } + } + } + if (d) + XftDrawDestroy(d); + + return x + (render ? w : 0); +} + +void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h) { + if (!drw) + return; + + XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y); + XSync(drw->dpy, False); +} + +unsigned int drw_fontset_getwidth(Drw *drw, const char *text) { + if (!drw || !drw->fonts || !text) + return 0; + return drw_text(drw, 0, 0, 0, 0, 0, text, 0); +} + +unsigned int drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n) { + unsigned int tmp = 0; + if (drw && drw->fonts && text && n) + tmp = drw_text(drw, 0, 0, 0, 0, 0, text, n); + return MIN(n, tmp); +} + +void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h) { + XGlyphInfo ext; + + if (!font || !text) + return; + + XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext); + if (w) + *w = ext.xOff; + if (h) + *h = font->h; +} + +Cur *drw_cur_create(Drw *drw, int shape) { + Cur *cur; + + if (!drw || !(cur = ecalloc(1, sizeof(Cur)))) + return NULL; + + cur->cursor = XCreateFontCursor(drw->dpy, shape); + + return cur; +} + +void drw_cur_free(Drw *drw, Cur *cursor) { + if (!cursor) + return; + + XFreeCursor(drw->dpy, cursor->cursor); + free(cursor); +} diff --git a/drw.h b/drw.h new file mode 100644 index 000000000..7c1f2a9ba --- /dev/null +++ b/drw.h @@ -0,0 +1,67 @@ +/* See LICENSE file for copyright and license details. */ + +typedef struct { + Cursor cursor; +} Cur; + +typedef struct Fnt { + Display *dpy; + unsigned int h; + XftFont *xfont; + FcPattern *pattern; + struct Fnt *next; +} Fnt; + +enum { ColFg, ColBg, ColBorder }; /* Clr scheme index */ +typedef XftColor Clr; + +typedef struct { + unsigned int w, h; + Display *dpy; + int screen; + Window root; + Visual *visual; + unsigned int depth; + Colormap cmap; + Drawable drawable; + Picture picture; + GC gc; + Clr *scheme; + Fnt *fonts; +} Drw; + +/* Drawable abstraction */ +Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h, Visual *visual, unsigned int depth, Colormap cmap); +void drw_resize(Drw *drw, unsigned int w, unsigned int h); +void drw_free(Drw *drw); + +/* Fnt abstraction */ +Fnt *drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount); +void drw_fontset_free(Fnt* set); +unsigned int drw_fontset_getwidth(Drw *drw, const char *text); +unsigned int drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n); +void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h); + +/* Colorscheme abstraction */ +void drw_clr_create(Drw *drw, Clr *dest, const char *clrname, unsigned int alpha); +Clr *drw_scm_create(Drw *drw, const char *clrnames[], const unsigned int alphas[], size_t clrcount); + +/* Cursor abstraction */ +Cur *drw_cur_create(Drw *drw, int shape); +void drw_cur_free(Drw *drw, Cur *cursor); + +/* Icon abstraction */ +Picture drw_picture_create_resized(Drw *drw, char *src, unsigned int src_w, unsigned int src_h, unsigned int dst_w, unsigned int dst_h); + +/* Drawing context manipulation */ +void drw_setfontset(Drw *drw, Fnt *set); +void drw_setscheme(Drw *drw, Clr *scm); + +/* Drawing functions */ +void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert); +void drw_polygon(Drw *drw, int x, int y, int ow, int oh, int sw, int sh, const XPoint *points, int npoints, int shape, int filled); +int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert); +void drw_pic(Drw *drw, int x, int y, unsigned int w, unsigned int h, Picture pic); + +/* Map functions */ +void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h); diff --git a/dwm.1 b/dwm.1 new file mode 100644 index 000000000..b9cc023a4 --- /dev/null +++ b/dwm.1 @@ -0,0 +1,192 @@ +.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. +.B Mod1\-Control\-, +Cycles backwards in layout list. +.TP +.B Mod1\-Control\-. +Cycles forwards in layout list. +.TP +.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. +.TP +.B Mod1\-Control\-Shift\-q +Restart 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 SIGNALS +.TP +.B SIGHUP - 1 +Restart the dwm process. +.TP +.B SIGTERM - 15 +Cleanly terminate the dwm process. +.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 000000000..b8d183c25 --- /dev/null +++ b/dwm.c @@ -0,0 +1,2825 @@ +/* See LICENSE file for copyright and license details. + * + * dynamic window manager is designed like any other X client as well. It is + * driven through handling X events. In contrast to other X clients, a window + * manager selects for SubstructureRedirectMask on the root window, to receive + * events about window (dis-)appearance. Only one X connection at a time is + * allowed to select for this event mask. + * + * The event handlers of dwm are organized in an array which is accessed + * whenever a new event has been fetched. This allows event dispatching + * in O(1) time. + * + * Each child of the root window is called a client, except windows which have + * set the override_redirect flag. Clients are organized in a linked client + * list on each monitor, the focus history is remembered through a stack list + * on each monitor. Each client contains a bit array to indicate the tags of a + * client. + * + * Keys and tagging rules are organized as arrays and defined in config.h. + * + * To understand everything else, start reading main(). + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef XINERAMA +#include +#endif /* XINERAMA */ +#include +#include + +#include "drw.h" +#include "util.h" + +/* macros */ +#define BUTTONMASK (ButtonPressMask|ButtonReleaseMask) +#define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)) +#define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \ + * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy))) +#define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags]) || C->issticky) +#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 NUMTAGS (LENGTH(tags) + LENGTH(scratchpads)) +#define TAGMASK ((1 << NUMTAGS) - 1) +#define SPTAG(i) ((1 << LENGTH(tags)) << (i)) +#define SPTAGMASK (((1 << LENGTH(scratchpads))-1) << LENGTH(tags)) +#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) +#define OPAQUE 0xffU + +/* Undefined in X11/X.h buttons that are actualy exist and correspond to + * horizontal scroll +*/ +#define Button6 6 +#define Button7 7 + +/* enums */ +enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ +enum { SchemeNorm, SchemeSel }; /* color schemes */ +enum { NetSupported, NetWMName, NetWMIcon, NetWMState, NetWMCheck, + NetWMFullscreen, NetWMSticky, NetActiveWindow, NetWMWindowType, + NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ +enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ +enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, + ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ + +typedef union { + int i; + unsigned int ui; + float f; + const void *v; +} Arg; + +typedef struct { + unsigned int click; + unsigned int mask; + unsigned int button; + void (*func)(const Arg *arg); + const Arg arg; +} Button; + +typedef struct Monitor Monitor; +typedef struct Client Client; +struct Client { + char name[256]; + float mina, maxa; + int x, y, w, h; + int oldx, oldy, oldw, oldh; + int basew, baseh, incw, inch, maxw, maxh, minw, minh, hintsvalid; + int bw, oldbw; + unsigned int tags; + int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen, issticky; + unsigned int icw, ich; Picture icon; + Client *next; + Client *snext; + Monitor *mon; + Window win; +}; + +typedef struct { + unsigned int mod; + KeySym keysym; +} Key; + +typedef struct { + unsigned int n; + const Key keys[5]; + void (*func)(const Arg *); + const Arg arg; +} Keychord; + +typedef struct { + const char *symbol; + void (*arrange)(Monitor *); +} Layout; + +typedef struct Pertag Pertag; +struct Monitor { + char ltsymbol[16]; + float mfact; + int nmaster; + int rmaster; + int num; + int by; /* bar geometry */ + int mx, my, mw, mh; /* screen size */ + int wx, wy, ww, wh; /* window area */ + unsigned int seltags; + unsigned int sellt; + unsigned int tagset[2]; + int showbar; + int topbar; + Client *clients; + Client *sel; + Client *stack; + Client *tagmarked[32]; + Monitor *next; + Window barwin; + const Layout *lt[2]; + Pertag *pertag; +}; + +typedef struct { + const char *class; + const char *instance; + const char *title; + unsigned int tags; + int isfloating; + int monitor; +} Rule; + +typedef struct { + const char *name; + const void *cmd; +} Sp; + +/* 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 attachbottom(Client *c); +static void attachstack(Client *c); +static void buttonpress(XEvent *e); +static void centeredmaster(Monitor *m); +static void centeredfloatingmaster(Monitor *m); +static void checkotherwm(void); +static void cleanup(void); +static void cleanupmon(Monitor *mon); +static void clientmessage(XEvent *e); +static void combotag(const Arg *arg); +static void comboview(const Arg *arg); +static void configure(Client *c); +static void configurenotify(XEvent *e); +static void configurerequest(XEvent *e); +static Monitor *createmon(void); +static void cyclelayout(const Arg *arg); +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 dwindle (Monitor *m); +static void enternotify(XEvent *e); +static void expose(XEvent *e); +static void fullscreen(const Arg *arg); +static void focus(Client *c); +static void focusin(XEvent *e); +static void focusmaster(const Arg *arg); +static void focusmon(const Arg *arg); +static void focusstack(const Arg *arg); +static Atom getatomprop(Client *c, Atom prop); +static Picture geticonprop(Window w, unsigned int *icw, unsigned int *ich); +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 grid (Monitor *m); +static void incnmaster(const Arg *arg); +static void keypress(XEvent *e); +static void killclient(const Arg *arg); +static void manage(Window w, XWindowAttributes *wa); +static void mappingnotify(XEvent *e); +static void maprequest(XEvent *e); +static void monocle(Monitor *m); +static void fibonacci (Monitor *m, int s); +static void keyrelease(XEvent *e); +static void motionnotify(XEvent *e); +static void movemouse(const Arg *arg); +static void movestack(const Arg *arg); +static unsigned int nexttag(void); +static Client *nexttiled(Client *c); +static void pop(Client *c); +static unsigned int prevtag(void); +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 resizemousescroll(const Arg *arg); +static void restack(Monitor *m); +static void run(void); +static void runAutostart(void); +static void scan(void); +static int sendevent(Client *c, Atom proto); +static void sendmon(Client *c, Monitor *m); +static void setclientstate(Client *c, long state); +static void setfocus(Client *c); +static void setfullscreen(Client *c, int fullscreen); +static void setsticky(Client *c, int sticky); +static void spiral (Monitor *m); +static void setlayout(const Arg *arg); +static void setmfact(const Arg *arg); +static void setup(void); +static void seturgent(Client *c, int urg); +static void showhide(Client *c); +static void sigchld(int unused); +static void sighup(int unused); +static void sigterm(int unused); +static void spawn(const Arg *arg); +static void tag(const Arg *arg); +static void tagtonext(const Arg *arg); +static void tagtoprev(const Arg *arg); +static void tagmon(const Arg *arg); +static void tile(Monitor *m); +static void togglebar(const Arg *arg); +static void togglefloating(const Arg *arg); +static void togglesticky(const Arg *arg); +static void togglemaster(const Arg *arg); +static void togglescratch(const Arg *arg); +static void toggletag(const Arg *arg); +static void toggleview(const Arg *arg); +static void freeicon(Client *c); +static void unfocus(Client *c, int setfocus); +static void unmanage(Client *c, int destroyed); +static void unmapnotify(XEvent *e); +static void updatebarpos(Monitor *m); +static void updatebars(void); +static void updateclientlist(void); +static int updategeom(void); +static void updatenumlockmask(void); +static void updatesizehints(Client *c); +static void updatestatus(void); +static void updatetitle(Client *c); +static void updateicon(Client *c); +static void updatewindowtype(Client *c); +static void updatewmhints(Client *c); +static void view(const Arg *arg); +static void viewnext(const Arg *arg); +static void viewprev(const Arg *arg); +static void warp(const Client *c); +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 xinitvisual(); +static void zoom(const Arg *arg); + +/* variables */ +static const char broken[] = "broken"; +static char stext[256]; +static int screen; +static int sw, sh; /* X display screen geometry width, height */ +static int bh; /* bar height */ +static int lrpad; /* sum of left and right padding for text */ +static int vp; /* vertical padding for bar */ +static int sp; /* side padding for bar */ +static int (*xerrorxlib)(Display *, XErrorEvent *); +static unsigned int numlockmask = 0; +static void (*handler[LASTEvent]) (XEvent *) = { + [ButtonPress] = buttonpress, + [ButtonRelease] = keyrelease, + [ClientMessage] = clientmessage, + [ConfigureRequest] = configurerequest, + [ConfigureNotify] = configurenotify, + [DestroyNotify] = destroynotify, + [EnterNotify] = enternotify, + [Expose] = expose, + [FocusIn] = focusin, + [KeyRelease] = keyrelease, + [KeyPress] = keypress, + [MappingNotify] = mappingnotify, + [MapRequest] = maprequest, + [MotionNotify] = motionnotify, + [PropertyNotify] = propertynotify, + [UnmapNotify] = unmapnotify +}; +static Atom wmatom[WMLast], netatom[NetLast]; +static int restart = 0; +static int running = 1; +static Cur *cursor[CurLast]; +static Clr **scheme; +static Display *dpy; +static Drw *drw; +static Monitor *mons, *selmon; +static Window root, wmcheckwin; +unsigned int currentkey = 0; +static int combo = 0; + +static int useargb = 0; +static Visual *visual; +static int depth; +static Colormap cmap; + +/* configuration, allows nested code to access above variables */ +#include "config.h" + +struct Pertag { + unsigned int curtag, prevtag; /* current and previous tag */ + int nmasters[LENGTH(tags) + 1]; /* number of windows in master area */ + float mfacts[LENGTH(tags) + 1]; /* mfacts per tag */ + unsigned int sellts[LENGTH(tags) + 1]; /* selected layouts */ + const Layout *ltidxs[LENGTH(tags) + 1][2]; /* matrix of tags and layouts indexes */ + int showbars[LENGTH(tags) + 1]; /* display bar for the current tag */ +}; + +/* compile-time check if all tags fit into an unsigned int bit array. */ +struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; + +/* function implementations */ +void applyrules(Client *c) { + const char *class, *instance; + unsigned int i; + const Rule *r; + Monitor *m; + XClassHint ch = { NULL, NULL }; + + /* rule matching */ + c->isfloating = 0; + c->tags = 0; + XGetClassHint(dpy, c->win, &ch); + class = ch.res_class ? ch.res_class : broken; + instance = ch.res_name ? ch.res_name : broken; + + for (i = 0; i < LENGTH(rules); i++) { + r = &rules[i]; + if ((!r->title || strstr(c->name, r->title)) + && (!r->class || strstr(class, r->class)) + && (!r->instance || strstr(instance, r->instance))) + { + c->isfloating = r->isfloating; + c->tags |= r->tags; + if ((r->tags & SPTAGMASK) && r->isfloating) { + c->x = c->mon->wx + (c->mon->ww / 2 - WIDTH(c) / 2); + c->y = c->mon->wy + (c->mon->wh / 2 - HEIGHT(c) / 2); + } + + for (m = mons; m && m->num != r->monitor; m = m->next); + if (m) + c->mon = m; + } + } + if (ch.res_class) + XFree(ch.res_class); + if (ch.res_name) + XFree(ch.res_name); + c->tags = c->tags & TAGMASK ? c->tags & TAGMASK : (c->mon->tagset[c->mon->seltags] & ~SPTAGMASK); +} + +int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact) { + int baseismin; + Monitor *m = c->mon; + + /* set minimum possible */ + *w = MAX(1, *w); + *h = MAX(1, *h); + if (interact) { + if (*x > sw) + *x = sw - WIDTH(c); + if (*y > sh) + *y = sh - HEIGHT(c); + if (*x + *w + 2 * c->bw < 0) + *x = 0; + if (*y + *h + 2 * c->bw < 0) + *y = 0; + } else { + if (*x >= m->wx + m->ww) + *x = m->wx + m->ww - WIDTH(c); + if (*y >= m->wy + m->wh) + *y = m->wy + m->wh - HEIGHT(c); + if (*x + *w + 2 * c->bw <= m->wx) + *x = m->wx; + if (*y + *h + 2 * c->bw <= m->wy) + *y = m->wy; + } + if (*h < bh) + *h = bh; + if (*w < bh) + *w = bh; + if (resizehints || c->isfloating || !c->mon->lt[c->mon->sellt]->arrange) { + if (!c->hintsvalid) + updatesizehints(c); + /* see last two sentences in ICCCM 4.1.2.3 */ + baseismin = c->basew == c->minw && c->baseh == c->minh; + if (!baseismin) { /* temporarily remove base dimensions */ + *w -= c->basew; + *h -= c->baseh; + } + /* adjust for aspect limits */ + if (c->mina > 0 && c->maxa > 0) { + if (c->maxa < (float)*w / *h) + *w = *h * c->maxa + 0.5; + else if (c->mina < (float)*h / *w) + *h = *w * c->mina + 0.5; + } + if (baseismin) { /* increment calculation requires this */ + *w -= c->basew; + *h -= c->baseh; + } + /* adjust for increment value */ + if (c->incw) + *w -= *w % c->incw; + if (c->inch) + *h -= *h % c->inch; + /* restore base dimensions */ + *w = MAX(*w + c->basew, c->minw); + *h = MAX(*h + c->baseh, c->minh); + if (c->maxw) + *w = MIN(*w, c->maxw); + if (c->maxh) + *h = MIN(*h, c->maxh); + } + return *x != c->x || *y != c->y || *w != c->w || *h != c->h; +} + +void arrange(Monitor *m) { + if (m) + showhide(m->stack); + else for (m = mons; m; m = m->next) + showhide(m->stack); + if (m) { + arrangemon(m); + restack(m); + } else for (m = mons; m; m = m->next) + arrangemon(m); +} + +void arrangemon(Monitor *m) { + strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol); + if (m->lt[m->sellt]->arrange) + m->lt[m->sellt]->arrange(m); +} + +void attach(Client *c) { + c->next = c->mon->clients; + c->mon->clients = c; +} + +void attachbottom(Client *c) { + Client **tc; + c->next = NULL; + for (tc = &c->mon->clients; *tc; tc = &(*tc)->next); + *tc = c; +} + +void attachstack(Client *c) { + c->snext = c->mon->stack; + c->mon->stack = c; +} + +void buttonpress(XEvent *e) { + unsigned int i, x, click; + Arg arg = {0}; + Client *c; + Monitor *m; + XButtonPressedEvent *ev = &e->xbutton; + + click = ClkRootWin; + /* focus monitor if necessary */ + if ((m = wintomon(ev->window)) && m != selmon) { + unfocus(selmon->sel, 1); + selmon = m; + focus(NULL); + } + if (ev->window == selmon->barwin) { + i = x = 0; + do + x += TEXTW(tags[i]); + while (ev->x >= x && ++i < LENGTH(tags)); + if (i < LENGTH(tags)) { + click = ClkTagBar; + arg.ui = 1 << i; + } else if (ev->x < x + TEXTW(selmon->ltsymbol)) + click = ClkLtSymbol; + else if (ev->x > selmon->ww - (int)TEXTW(stext)) + click = ClkStatusText; + else + click = ClkWinTitle; + } else if ((c = wintoclient(ev->window))) { + focus(c); + restack(selmon); + XAllowEvents(dpy, ReplayPointer, CurrentTime); + click = ClkClientWin; + } + for (i = 0; i < LENGTH(buttons); i++) + if (click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button + && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) + buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg); +} + +void checkotherwm(void) { + xerrorxlib = XSetErrorHandler(xerrorstart); + /* this causes an error if some other window manager is running */ + XSelectInput(dpy, DefaultRootWindow(dpy), SubstructureRedirectMask); + XSync(dpy, False); + XSetErrorHandler(xerror); + XSync(dpy, False); +} + +void cleanup(void) { + Arg a = {.ui = ~0}; + Layout foo = { "", NULL }; + Monitor *m; + size_t i; + + view(&a); + selmon->lt[selmon->sellt] = &foo; + for (m = mons; m; m = m->next) + while (m->stack) + unmanage(m->stack, 0); + XUngrabKey(dpy, AnyKey, AnyModifier, root); + while (mons) + cleanupmon(mons); + for (i = 0; i < CurLast; i++) + drw_cur_free(drw, cursor[i]); + for (i = 0; i < LENGTH(colors); i++) + free(scheme[i]); + free(scheme); + XDestroyWindow(dpy, wmcheckwin); + drw_free(drw); + XSync(dpy, False); + XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); + XDeleteProperty(dpy, root, netatom[NetActiveWindow]); +} + +void cleanupmon(Monitor *mon) { + Monitor *m; + + if (mon == mons) + mons = mons->next; + else { + for (m = mons; m && m->next != mon; m = m->next); + m->next = mon->next; + } + XUnmapWindow(dpy, mon->barwin); + XDestroyWindow(dpy, mon->barwin); + free(mon); +} + +void clientmessage(XEvent *e) { + XClientMessageEvent *cme = &e->xclient; + Client *c = wintoclient(cme->window); + + if (!c) + return; + if (cme->message_type == netatom[NetWMState]) { + if (cme->data.l[1] == netatom[NetWMFullscreen] + || cme->data.l[2] == netatom[NetWMFullscreen]) + setfullscreen(c, (cme->data.l[0] == 1 /* _NET_WM_STATE_ADD */ + || (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */ && !c->isfullscreen))); + if (cme->data.l[1] == netatom[NetWMSticky] + || cme->data.l[2] == netatom[NetWMSticky]) + setsticky(c, (cme->data.l[0] == 1 || (cme->data.l[0] == 2 && !c->issticky))); + } else if (cme->message_type == netatom[NetActiveWindow]) { + if (c != selmon->sel && !c->isurgent) + seturgent(c, 1); + } +} + +void combotag(const Arg *arg) { + if(selmon->sel && arg->ui & TAGMASK) { + if (combo) { + selmon->sel->tags |= arg->ui & TAGMASK; + } else { + combo = 1; + selmon->sel->tags = arg->ui & TAGMASK; + } + focus(NULL); + arrange(selmon); + } +} + +void comboview(const Arg *arg) { + unsigned int newtags = arg->ui & TAGMASK; + if (combo) { + selmon->tagset[selmon->seltags] |= newtags; + } + else { + selmon->seltags ^= 1; /* toggle tagset */ + combo = 1; + if (newtags) + selmon->tagset[selmon->seltags] = newtags; + + // Update pertag information for the current tag + selmon->pertag->prevtag = selmon->pertag->curtag; + selmon->pertag->curtag = newtags ? ffs(newtags) : 0; + + // 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]; + + if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag]) + togglebar(NULL); + } + focus(NULL); + arrange(selmon); +} + +void configure(Client *c) { + XConfigureEvent ce; + + ce.type = ConfigureNotify; + ce.display = dpy; + ce.event = c->win; + ce.window = c->win; + ce.x = c->x; + ce.y = c->y; + ce.width = c->w; + ce.height = c->h; + ce.border_width = c->bw; + ce.above = None; + ce.override_redirect = False; + XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce); +} + +void configurenotify(XEvent *e) { + Monitor *m; + Client *c; + XConfigureEvent *ev = &e->xconfigure; + int dirty; + + /* TODO: updategeom handling sucks, needs to be simplified */ + if (ev->window == root) { + dirty = (sw != ev->width || sh != ev->height); + sw = ev->width; + sh = ev->height; + if (updategeom() || dirty) { + drw_resize(drw, sw, bh); + updatebars(); + for (m = mons; m; m = m->next) { + for (c = m->clients; c; c = c->next) + if (c->isfullscreen) + resizeclient(c, m->mx, m->my, m->mw, m->mh); + XMoveResizeWindow(dpy, m->barwin, m->wx + sp, m->by + vp, m->ww - 2 * sp, bh); + } + focus(NULL); + arrange(NULL); + } + } +} + +void configurerequest(XEvent *e) { + Client *c; + Monitor *m; + XConfigureRequestEvent *ev = &e->xconfigurerequest; + XWindowChanges wc; + + if ((c = wintoclient(ev->window))) { + if (ev->value_mask & CWBorderWidth) + c->bw = ev->border_width; + else if (c->isfloating || !selmon->lt[selmon->sellt]->arrange) { + m = c->mon; + if (ev->value_mask & CWX) { + c->oldx = c->x; + c->x = m->mx + ev->x; + } + if (ev->value_mask & CWY) { + c->oldy = c->y; + c->y = m->my + ev->y; + } + if (ev->value_mask & CWWidth) { + c->oldw = c->w; + c->w = ev->width; + } + if (ev->value_mask & CWHeight) { + c->oldh = c->h; + c->h = ev->height; + } + if ((c->x + c->w) > m->mx + m->mw && c->isfloating) + c->x = m->mx + (m->mw / 2 - WIDTH(c) / 2); /* center in x direction */ + if ((c->y + c->h) > m->my + m->mh && c->isfloating) + c->y = m->my + (m->mh / 2 - HEIGHT(c) / 2); /* center in y direction */ + if ((ev->value_mask & (CWX|CWY)) && !(ev->value_mask & (CWWidth|CWHeight))) + configure(c); + if (ISVISIBLE(c)) + XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); + } else + configure(c); + } else { + wc.x = ev->x; + wc.y = ev->y; + wc.width = ev->width; + wc.height = ev->height; + wc.border_width = ev->border_width; + wc.sibling = ev->above; + wc.stack_mode = ev->detail; + XConfigureWindow(dpy, ev->window, ev->value_mask, &wc); + } + XSync(dpy, False); +} + +Monitor *createmon(void) { + Monitor *m; + unsigned int i; + + m = ecalloc(1, sizeof(Monitor)); + m->tagset[0] = m->tagset[1] = 1; + m->mfact = mfact; + m->nmaster = nmaster; + m->rmaster = rmaster; + m->showbar = showbar; + m->topbar = topbar; + m->lt[0] = &layouts[0]; + m->lt[1] = &layouts[1 % LENGTH(layouts)]; + strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); + m->pertag = ecalloc(1, sizeof(Pertag)); + m->pertag->curtag = m->pertag->prevtag = 1; + + for (i = 0; i <= LENGTH(tags); i++) { + m->pertag->nmasters[i] = m->nmaster; + m->pertag->mfacts[i] = m->mfact; + + m->pertag->ltidxs[i][0] = m->lt[0]; + m->pertag->ltidxs[i][1] = m->lt[1]; + m->pertag->sellts[i] = m->sellt; + + m->pertag->showbars[i] = m->showbar; + } + + return m; +} + +void cyclelayout(const Arg *arg) { + Layout *l; + for(l = (Layout *)layouts; l != selmon->lt[selmon->sellt]; l++); + if(arg->i > 0) { + if(l->symbol && (l + 1)->symbol) + setlayout(&((Arg) { .v = (l + 1) })); + else + setlayout(&((Arg) { .v = layouts })); + } else { + if(l != layouts && (l - 1)->symbol) + setlayout(&((Arg) { .v = (l - 1) })); + else + setlayout(&((Arg) { .v = &layouts[LENGTH(layouts) - 2] })); + } +} + +void destroynotify(XEvent *e) { + Client *c; + XDestroyWindowEvent *ev = &e->xdestroywindow; + + if ((c = wintoclient(ev->window))) + unmanage(c, 1); +} + +void detach(Client *c) { + Client **tc; + + for (int i = 1; i < LENGTH(tags); i++) + if (c == c->mon->tagmarked[i]) + c->mon->tagmarked[i] = NULL; + + for (tc = &c->mon->clients; *tc && *tc != c; tc = &(*tc)->next); + *tc = c->next; +} + +void detachstack(Client *c) { + Client **tc, *t; + + for (tc = &c->mon->stack; *tc && *tc != c; tc = &(*tc)->snext); + *tc = c->snext; + + if (c == c->mon->sel) { + for (t = c->mon->stack; t && !ISVISIBLE(t); t = t->snext); + c->mon->sel = t; + } +} + +Monitor *dirtomon(int dir) { + Monitor *m = NULL; + + if (dir > 0) { + if (!(m = selmon->next)) + m = mons; + } else if (selmon == mons) + for (m = mons; m->next; m = m->next); + else + for (m = mons; m->next != selmon; m = m->next); + return m; +} + +void drawbar(Monitor *m) { + int indn; + int x, w, tw = 0; + int boxs = drw->fonts->h / 9; + int boxw = drw->fonts->h / 6 + 2; + unsigned int i, occ = 0, urg = 0; + Client *c; + + if (!m->showbar) + return; + + /* draw status first so it can be overdrawn by tags later */ + if (m == selmon) { /* status is only drawn on selected monitor */ + drw_setscheme(drw, scheme[SchemeNorm]); + tw = TEXTW(stext); + drw_text(drw, m->ww - tw, 0, tw, bh, lrpad / 2, stext, 0); + } + + for (c = m->clients; c; c = c->next) { + occ |= c->tags; + if (c->isurgent) + urg |= c->tags; + } + x = 0; + for (i = 0; i < LENGTH(tags); i++) { + indn = 0; + w = TEXTW(tags[i]); + drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]); + drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i); + + for (c = m->clients; c; c = c->next) { + if (c->tags & (1 << i)) { + drw_rect(drw, x, 1 + (indn * 2), selmon->sel == c ? 6 : 1, 1, 1, urg & 1 << i); + indn++; + } + } + + x += w; + } + w = TEXTW(m->ltsymbol); + drw_setscheme(drw, scheme[SchemeNorm]); + x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); + + if ((w = m->ww - tw - x) > bh) { + if (m->sel) { + drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); + if (TEXTW(m->sel->name) > w) /* title is bigger than the width of the title rectangle, don't center */ + drw_text(drw, x, 0, w - 2 * sp, bh, lrpad / 2 + (m->sel->icon ? m->sel->icw + ICONSPACING : 0), m->sel->name, 0); + else /* center window title */ + drw_text(drw, x, 0, w - 2 * sp, bh, (w - TEXTW(m->sel->name)) / 2, m->sel->name, 0); + if (m->sel->icon) + drw_pic(drw, x + lrpad / 2, (bh - m->sel->ich) / 2, m->sel->icw, m->sel->ich, m->sel->icon); + if (m->sel->isfloating) + drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0); + if (m->sel->issticky) + drw_polygon(drw, x + boxs, m->sel->isfloating ? boxs * 2 + boxw : boxs, stickyiconbb.x, stickyiconbb.y, boxw, boxw * stickyiconbb.y / stickyiconbb.x, stickyicon, LENGTH(stickyicon), Nonconvex, m->sel->tags & m->tagset[m->seltags]); + } else { + drw_setscheme(drw, scheme[SchemeNorm]); + drw_rect(drw, x, 0, w - 2 * sp, bh, 1, 1); + } + } + drw_map(drw, m->barwin, 0, 0, m->ww, bh); +} + +void drawbars(void) { + Monitor *m; + + for (m = mons; m; m = m->next) + drawbar(m); +} + +void enternotify(XEvent *e) { + Client *c; + Monitor *m; + XCrossingEvent *ev = &e->xcrossing; + + if ((ev->mode != NotifyNormal || ev->detail == NotifyInferior) && ev->window != root) + return; + c = wintoclient(ev->window); + m = c ? c->mon : wintomon(ev->window); + if (m != selmon) { + unfocus(selmon->sel, 1); + selmon = m; + } else if (!c || c == selmon->sel) + return; + focus(c); +} + +void expose(XEvent *e) { + Monitor *m; + XExposeEvent *ev = &e->xexpose; + + if (ev->count == 0 && (m = wintomon(ev->window))) + drawbar(m); +} + +void focus(Client *c) { + if (!c || !ISVISIBLE(c)) + for (c = selmon->stack; c && !ISVISIBLE(c); c = c->snext); + if (selmon->sel && selmon->sel != c) + unfocus(selmon->sel, 0); + if (c) { + if (c->mon != selmon) + selmon = c->mon; + if (c->isurgent) + seturgent(c, 0); + detachstack(c); + attachstack(c); + grabbuttons(c, 1); + XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel); + setfocus(c); + } else { + XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); + XDeleteProperty(dpy, root, netatom[NetActiveWindow]); + } + selmon->sel = c; + drawbars(); +} + +/* there are some broken focus acquiring clients needing extra handling */ +void focusin(XEvent *e) { + XFocusChangeEvent *ev = &e->xfocus; + + if (selmon->sel && ev->window != selmon->sel->win) + setfocus(selmon->sel); +} + +void focusmaster(const Arg *arg) { + Client *master; + + if (selmon->nmaster > 1) + return; + if (!selmon->sel || (selmon->sel->isfullscreen && lockfullscreen)) + return; + + master = nexttiled(selmon->clients); + + if (!master) + return; + + int i; + for (i = 0; !(selmon->tagset[selmon->seltags] & 1 << i); i++); + i++; + + if (selmon->sel == master) { + if (selmon->tagmarked[i] && ISVISIBLE(selmon->tagmarked[i])) + focus(selmon->tagmarked[i]); + } else { + selmon->tagmarked[i] = selmon->sel; + focus(master); + } +} + +void focusmon(const Arg *arg) { + Monitor *m; + + if (!mons->next) + return; + if ((m = dirtomon(arg->i)) == selmon) + return; + unfocus(selmon->sel, 0); + selmon = m; + focus(NULL); + warp(selmon->sel); +} + +void focusstack(const Arg *arg) { + Client *c = NULL, *i; + + if (!selmon->sel || (selmon->sel->isfullscreen && lockfullscreen)) + return; + if (arg->i > 0) { + for (c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next); + if (!c) + for (c = selmon->clients; c && !ISVISIBLE(c); c = c->next); + } else { + for (i = selmon->clients; i != selmon->sel; i = i->next) + if (ISVISIBLE(i)) + c = i; + if (!c) + for (; i; i = i->next) + if (ISVISIBLE(i)) + c = i; + } + if (c) { + focus(c); + restack(selmon); + } +} + +Atom getatomprop(Client *c, Atom prop) { + int di; + unsigned long dl; + unsigned char *p = NULL; + Atom da, atom = None; + + if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM, + &da, &di, &dl, &dl, &p) == Success && p) { + atom = *(Atom *)p; + XFree(p); + } + return atom; +} + +static uint32_t prealpha(uint32_t p) { + uint8_t a = p >> 24u; + uint32_t rb = (a * (p & 0xFF00FFu)) >> 8u; + uint32_t g = (a * (p & 0x00FF00u)) >> 8u; + return (rb & 0xFF00FFu) | (g & 0x00FF00u) | (a << 24u); +} + +Picture geticonprop(Window win, unsigned int *picw, unsigned int *pich) { + int format; + unsigned long n, extra, *p = NULL; + Atom real; + + if (XGetWindowProperty(dpy, win, netatom[NetWMIcon], 0L, LONG_MAX, False, AnyPropertyType, + &real, &format, &n, &extra, (unsigned char **)&p) != Success) + return None; + if (n == 0 || format != 32) { XFree(p); return None; } + + unsigned long *bstp = NULL; + uint32_t w, h, sz; + { + unsigned long *i; const unsigned long *end = p + n; + uint32_t bstd = UINT32_MAX, d, m; + for (i = p; i < end - 1; i += sz) { + if ((w = *i++) >= 16384 || (h = *i++) >= 16384) { XFree(p); return None; } + if ((sz = w * h) > end - i) break; + if ((m = w > h ? w : h) >= ICONSIZE && (d = m - ICONSIZE) < bstd) { bstd = d; bstp = i; } + } + if (!bstp) { + for (i = p; i < end - 1; i += sz) { + if ((w = *i++) >= 16384 || (h = *i++) >= 16384) { XFree(p); return None; } + if ((sz = w * h) > end - i) break; + if ((d = ICONSIZE - (w > h ? w : h)) < bstd) { bstd = d; bstp = i; } + } + } + if (!bstp) { XFree(p); return None; } + } + + if ((w = *(bstp - 2)) == 0 || (h = *(bstp - 1)) == 0) { XFree(p); return None; } + + uint32_t icw, ich; + if (w <= h) { + ich = ICONSIZE; icw = w * ICONSIZE / h; + if (icw == 0) icw = 1; + } + else { + icw = ICONSIZE; ich = h * ICONSIZE / w; + if (ich == 0) ich = 1; + } + *picw = icw; *pich = ich; + + uint32_t i, *bstp32 = (uint32_t *)bstp; + for (sz = w * h, i = 0; i < sz; ++i) bstp32[i] = prealpha(bstp[i]); + + Picture ret = drw_picture_create_resized(drw, (char *)bstp, w, h, icw, ich); + XFree(p); + + return ret; +} + + +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, k; + unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; + KeyCode code; + XUngrabKey(dpy, AnyKey, AnyModifier, root); + for (i = 0; i < LENGTH(keychords); i++) + if ((code = XKeysymToKeycode(dpy, keychords[i]->keys[currentkey].keysym))) + for (k = 0; k < LENGTH(modifiers); k++) + XGrabKey(dpy, code, keychords[i]->keys[currentkey].mod | modifiers[k], root, + True, GrabModeAsync, GrabModeAsync); + if(currentkey > 0) + XGrabKey(dpy, XKeysymToKeycode(dpy, XK_Escape), AnyModifier, root, True, GrabModeAsync, GrabModeAsync); + } +} + +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) { + XEvent event = *e; + unsigned int ran = 0; + KeySym keysym; + XKeyEvent *ev; + + Keychord *arr1[sizeof(keychords) / sizeof(Keychord*)]; + Keychord *arr2[sizeof(keychords) / sizeof(Keychord*)]; + memcpy(arr1, keychords, sizeof(keychords)); + Keychord **rpointer = arr1; + Keychord **wpointer = arr2; + + size_t r = sizeof(keychords)/ sizeof(Keychord*); + + while(1){ + ev = &event.xkey; + keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0); + size_t w = 0; + for (int i = 0; i < r; i++){ + if(keysym == (*(rpointer + i))->keys[currentkey].keysym + && CLEANMASK((*(rpointer + i))->keys[currentkey].mod) == CLEANMASK(ev->state) + && (*(rpointer + i))->func){ + if((*(rpointer + i))->n == currentkey +1){ + (*(rpointer + i))->func(&((*(rpointer + i))->arg)); + ran = 1; + }else{ + *(wpointer + w) = *(rpointer + i); + w++; + } + } + } + currentkey++; + if(w == 0 || ran == 1) + break; + grabkeys(); + while (running && !XNextEvent(dpy, &event) && !ran) + if(event.type == KeyPress) + break; + r = w; + Keychord **holder = rpointer; + rpointer = wpointer; + wpointer = holder; + } + currentkey = 0; + grabkeys(); +} + +void killclient(const Arg *arg) { + if (!selmon->sel) + return; + if (!sendevent(selmon->sel, wmatom[WMDelete])) { + XGrabServer(dpy); + XSetErrorHandler(xerrordummy); + XSetCloseDownMode(dpy, DestroyAll); + XKillClient(dpy, selmon->sel->win); + XSync(dpy, False); + XSetErrorHandler(xerror); + XUngrabServer(dpy); + } +} + +void manage(Window w, XWindowAttributes *wa) { + Client *c, *t = NULL; + Window trans = None; + XWindowChanges wc; + + c = ecalloc(1, sizeof(Client)); + c->win = w; + /* geometry */ + c->x = c->oldx = wa->x; + c->y = c->oldy = wa->y; + c->w = c->oldw = wa->width; + c->h = c->oldh = wa->height; + c->oldbw = wa->border_width; + + updateicon(c); + updatetitle(c); + if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) { + c->mon = t->mon; + c->tags = t->tags; + } else { + c->mon = selmon; + applyrules(c); + } + + if (c->x + WIDTH(c) > c->mon->wx + c->mon->ww) + c->x = c->mon->wx + c->mon->ww - WIDTH(c); + if (c->y + HEIGHT(c) > c->mon->wy + c->mon->wh) + c->y = c->mon->wy + c->mon->wh - HEIGHT(c); + c->x = MAX(c->x, c->mon->wx); + c->y = MAX(c->y, c->mon->wy); + c->bw = borderpx; + + wc.border_width = c->bw; + XConfigureWindow(dpy, w, CWBorderWidth, &wc); + XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel); + configure(c); /* propagates border_width, if size doesn't change */ + updatewindowtype(c); + updatesizehints(c); + updatewmhints(c); + c->x = c->mon->mx + (c->mon->mw - WIDTH(c)) / 2; + c->y = c->mon->my + (c->mon->mh - HEIGHT(c)) / 2; + XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask); + grabbuttons(c, 0); + if (!c->isfloating) + c->isfloating = c->oldstate = trans != None || c->isfixed; + if (c->isfloating) + XRaiseWindow(dpy, c->win); + attachbottom(c); + attachstack(c); + XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, + (unsigned char *) &(c->win), 1); + XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */ + setclientstate(c, NormalState); + if (c->mon == selmon) + unfocus(selmon->sel, 0); + c->mon->sel = c; + arrange(c->mon); + XMapWindow(dpy, c->win); + focus(NULL); +} + +void mappingnotify(XEvent *e) { + XMappingEvent *ev = &e->xmapping; + + XRefreshKeyboardMapping(ev); + if (ev->request == MappingKeyboard) + grabkeys(); +} + +void maprequest(XEvent *e) { + static XWindowAttributes wa; + XMapRequestEvent *ev = &e->xmaprequest; + + if (!XGetWindowAttributes(dpy, ev->window, &wa) || wa.override_redirect) + return; + if (!wintoclient(ev->window)) + manage(ev->window, &wa); +} + +void monocle(Monitor *m) { + unsigned int n = 0; + Client *c; + + for (c = m->clients; c; c = c->next) + if (ISVISIBLE(c)) + n++; + if (n > 0) /* override layout symbol */ + snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n); + for (c = nexttiled(m->clients); c; c = nexttiled(c->next)) + resize(c, m->wx, m->wy, m->ww - 2 * c->bw, m->wh - 2 * c->bw, 0); +} + +void centeredmaster(Monitor *m) { + unsigned int i, n, h, mw, mx, my, oty, ety, tw; + Client *c; + + /* count number of clients in the selected monitor */ + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); + if (n == 0) + return; + + /* initialize areas */ + mw = m->ww; + mx = 0; + my = 0; + tw = mw; + + if (n > m->nmaster) { + /* go mfact box in the center if more than nmaster clients */ + mw = m->nmaster ? m->ww * m->mfact : 0; + tw = m->ww - mw; + + if (n - m->nmaster > 1) { + /* only one client */ + mx = (m->ww - mw) / 2; + tw = (m->ww - mw) / 2; + } + } + + oty = 0; + ety = 0; + for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) + if (i < m->nmaster) { + /* nmaster clients are stacked vertically, in the center + * of the screen */ + h = (m->wh - my) / (MIN(n, m->nmaster) - i); + resize(c, m->wx + mx, m->wy + my, mw - (2*c->bw), + h - (2*c->bw), 0); + my += HEIGHT(c); + } else { + /* stack clients are stacked vertically */ + if ((i - m->nmaster) % 2 ) { + h = (m->wh - ety) / ( (1 + n - i) / 2); + resize(c, m->wx, m->wy + ety, tw - (2*c->bw), + h - (2*c->bw), 0); + ety += HEIGHT(c); + } else { + h = (m->wh - oty) / ((1 + n - i) / 2); + resize(c, m->wx + mx + mw, m->wy + oty, + tw - (2*c->bw), h - (2*c->bw), 0); + oty += HEIGHT(c); + } + } +} + +void centeredfloatingmaster(Monitor *m) { + unsigned int i, n, w, mh, mw, mx, mxo, my, myo, tx; + Client *c; + + /* count number of clients in the selected monitor */ + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); + if (n == 0) + return; + + /* initialize nmaster area */ + if (n > m->nmaster) { + /* go mfact box in the center if more than nmaster clients */ + if (m->ww > m->wh) { + mw = m->nmaster ? m->ww * m->mfact : 0; + mh = m->nmaster ? m->wh * 0.9 : 0; + } else { + mh = m->nmaster ? m->wh * m->mfact : 0; + mw = m->nmaster ? m->ww * 0.9 : 0; + } + mx = mxo = (m->ww - mw) / 2; + my = myo = (m->wh - mh) / 2; + } else { + /* go fullscreen if all clients are in the master area */ + mh = m->wh; + mw = m->ww; + mx = mxo = 0; + my = myo = 0; + } + + for(i = tx = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) + if (i < m->nmaster) { + /* nmaster clients are stacked horizontally, in the center + * of the screen */ + w = (mw + mxo - mx) / (MIN(n, m->nmaster) - i); + resize(c, m->wx + mx, m->wy + my, w - (2*c->bw), + mh - (2*c->bw), 0); + mx += WIDTH(c); + } else { + /* stack clients are stacked horizontally */ + w = (m->ww - tx) / (n - i); + resize(c, m->wx + tx, m->wy, w - (2*c->bw), + m->wh - (2*c->bw), 0); + tx += WIDTH(c); + } +} + +void fibonacci(Monitor *m, int s) { + unsigned int i, n, nx, ny, nw, nh; + Client *c; + + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); + if (n == 0) + return; + + nx = m->wx; + ny = 0; + nw = m->ww; + nh = m->wh; + + for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next)) { + if ((i % 2 && nh / 2 > 2 * c->bw) || (!(i % 2) && nw / 2 > 2 * c->bw)) { + if (i < n - 1) { + if (i % 2) + nh /= 2; + else + nw /= 2; + if ((i % 4) == 2 && !s) + nx += nw; + else if ((i % 4) == 3 && !s) + ny += nh; + } + if ((i % 4) == 0) { + if(s) + ny += nh; + else + ny -= nh; + } + else if ((i % 4) == 1) + nx += nw; + else if ((i % 4) == 2) + ny += nh; + else if ((i % 4) == 3) { + if(s) + nx += nw; + else + nx -= nw; + } + if (i == 0) { + if (n != 1) + nw = m->ww * m->mfact; + ny = m->wy; + } + else if (i == 1) + nw = m->ww - nw; + i++; + } + resize(c, nx, ny, nw - 2 * c->bw, nh - 2 * c->bw, False); + } +} + +void dwindle(Monitor *m) { + fibonacci(m, 1); +} + +void spiral(Monitor *m) { + fibonacci(m, 0); +} + +void grid(Monitor *m) { + unsigned int i, n, cx, cy, cw, ch, aw, ah, cols, rows; + Client *c; + + for(n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next)) + n++; + + /* grid dimensions */ + for(rows = 0; rows <= n/2; rows++) + if(rows*rows >= n) + break; + cols = (rows && (rows - 1) * rows >= n) ? rows - 1 : rows; + + /* window geoms (cell height/width) */ + ch = m->wh / (rows ? rows : 1); + cw = m->ww / (cols ? cols : 1); + for(i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next)) { + cx = m->wx + (i / rows) * cw; + cy = m->wy + (i % rows) * ch; + /* adjust height/width of last row/column's windows */ + ah = ((i + 1) % rows == 0) ? m->wh - ch * rows : 0; + aw = (i >= rows * (cols - 1)) ? m->ww - cw * cols : 0; + resize(c, cx, cy, cw - 2 * c->bw + aw, ch - 2 * c->bw + ah, False); + i++; + } +} + +void keyrelease(XEvent *e) { + combo = 0; +} + +void motionnotify(XEvent *e) { + static Monitor *mon = NULL; + Monitor *m; + XMotionEvent *ev = &e->xmotion; + + if (ev->window != root) + return; + if ((m = recttomon(ev->x_root, ev->y_root, 1, 1)) != mon && mon) { + unfocus(selmon->sel, 1); + selmon = m; + focus(NULL); + } + mon = m; +} + +void movemouse(const Arg *arg) { + int x, y, ocx, ocy, nx, ny; + Client *c; + Monitor *m; + XEvent ev; + Time lasttime = 0; + + if (!(c = selmon->sel)) + return; + if (c->isfullscreen) /* no support moving fullscreen windows by mouse */ + return; + restack(selmon); + ocx = c->x; + ocy = c->y; + if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, + None, cursor[CurMove]->cursor, CurrentTime) != GrabSuccess) + return; + if (!getrootptr(&x, &y)) + return; + do { + XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); + switch(ev.type) { + case ConfigureRequest: + case Expose: + case MapRequest: + handler[ev.type](&ev); + break; + case MotionNotify: + if ((ev.xmotion.time - lasttime) <= (1000 / 60)) + continue; + lasttime = ev.xmotion.time; + + nx = ocx + (ev.xmotion.x - x); + ny = ocy + (ev.xmotion.y - y); + if (abs(selmon->wx - nx) < snap) + nx = selmon->wx; + else if (abs((selmon->wx + selmon->ww) - (nx + WIDTH(c))) < snap) + nx = selmon->wx + selmon->ww - WIDTH(c); + if (abs(selmon->wy - ny) < snap) + ny = selmon->wy; + else if (abs((selmon->wy + selmon->wh) - (ny + HEIGHT(c))) < snap) + ny = selmon->wy + selmon->wh - HEIGHT(c); + if (!c->isfloating && selmon->lt[selmon->sellt]->arrange + && (abs(nx - c->x) > snap || abs(ny - c->y) > snap)) + togglefloating(NULL); + if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) + resize(c, nx, ny, c->w, c->h, 1); + break; + } + } while (ev.type != ButtonRelease); + XUngrabPointer(dpy, CurrentTime); + if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { + sendmon(c, m); + selmon = m; + focus(NULL); + } +} + +unsigned int nexttag(void) { + unsigned int seltag = selmon->tagset[selmon->seltags]; + return seltag == (1 << (LENGTH(tags) - 1)) ? 1 : seltag << 1; +} + +void movestack(const Arg *arg) { + Client *c = NULL, *p = NULL, *pc = NULL, *i; + + if(arg->i > 0) { + /* find the client after selmon->sel */ + for(c = selmon->sel->next; c && (!ISVISIBLE(c) || c->isfloating); c = c->next); + if(!c) + for(c = selmon->clients; c && (!ISVISIBLE(c) || c->isfloating); c = c->next); + + } + else { + /* find the client before selmon->sel */ + for(i = selmon->clients; i != selmon->sel; i = i->next) + if(ISVISIBLE(i) && !i->isfloating) + c = i; + if(!c) + for(; i; i = i->next) + if(ISVISIBLE(i) && !i->isfloating) + c = i; + } + + /* find the client before selmon->sel and c */ + for(i = selmon->clients; i && (!p || !pc); i = i->next) { + if(i->next == selmon->sel) + p = i; + if(i->next == c) + pc = i; + } + + /* swap c and selmon->sel selmon->clients in the selmon->clients list */ + if(c && c != selmon->sel) { + Client *temp = selmon->sel->next==c?selmon->sel:selmon->sel->next; + selmon->sel->next = c->next==selmon->sel?c:c->next; + c->next = temp; + + if(p && p != c) + p->next = c; + if(pc && pc != selmon->sel) + pc->next = selmon->sel; + + if(selmon->sel == selmon->clients) + selmon->clients = c; + else if(c == selmon->clients) + selmon->clients = selmon->sel; + + arrange(selmon); + } +} + +Client * nexttiled(Client *c) { + for (; c && (c->isfloating || !ISVISIBLE(c)); c = c->next); + return c; +} + +void pop(Client *c) { + int i; + for (i = 0; !(selmon->tagset[selmon->seltags] & 1 << i); i++); + i++; + + c->mon->tagmarked[i] = nexttiled(c->mon->clients); + detach(c); + attach(c); + focus(c); + arrange(c->mon); +} + +unsigned int prevtag(void) { + unsigned int seltag = selmon->tagset[selmon->seltags]; + return seltag == 1 ? (1 << (LENGTH(tags) - 1)) : seltag >> 1; +} + +void propertynotify(XEvent *e) { + Client *c; + Window trans; + XPropertyEvent *ev = &e->xproperty; + + if ((ev->window == root) && (ev->atom == XA_WM_NAME)) + updatestatus(); + else if (ev->state == PropertyDelete) + return; /* ignore */ + else if ((c = wintoclient(ev->window))) { + switch(ev->atom) { + default: break; + case XA_WM_TRANSIENT_FOR: + if (!c->isfloating && (XGetTransientForHint(dpy, c->win, &trans)) && + (c->isfloating = (wintoclient(trans)) != NULL)) + arrange(c->mon); + break; + case XA_WM_NORMAL_HINTS: + c->hintsvalid = 0; + break; + case XA_WM_HINTS: + updatewmhints(c); + drawbars(); + break; + } + if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { + updatetitle(c); + if (c == c->mon->sel) + drawbar(c->mon); + } + else if (ev->atom == netatom[NetWMIcon]) { + updateicon(c); + if (c == c->mon->sel) + drawbar(c->mon); + } + if (ev->atom == netatom[NetWMWindowType]) + updatewindowtype(c); + } +} + +void quit(const Arg *arg) { + if(arg->i) restart = 1; + running = 0; +} + +Monitor *recttomon(int x, int y, int w, int h) { + Monitor *m, *r = selmon; + int a, area = 0; + + for (m = mons; m; m = m->next) + if ((a = INTERSECT(x, y, w, h, m)) > area) { + area = a; + r = m; + } + return r; +} + +void resize(Client *c, int x, int y, int w, int h, int interact) { + if (applysizehints(c, &x, &y, &w, &h, interact)) + resizeclient(c, x, y, w, h); +} + +void resizeclient(Client *c, int x, int y, int w, int h) { + XWindowChanges wc; + + c->oldx = c->x; c->x = wc.x = x; + c->oldy = c->y; c->y = wc.y = y; + c->oldw = c->w; c->w = wc.width = w; + c->oldh = c->h; c->h = wc.height = h; + + wc.border_width = c->bw; + + XConfigureWindow(dpy, c->win, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc); + configure(c); + XSync(dpy, False); +} + +void resizemouse(const Arg *arg) { + int ocx, ocy, nw, nh; + Client *c; + Monitor *m; + XEvent ev; + Time lasttime = 0; + + if (!(c = selmon->sel)) + return; + if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */ + return; + restack(selmon); + ocx = c->x; + ocy = c->y; + if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, + None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess) + return; + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); + do { + XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); + switch(ev.type) { + case ConfigureRequest: + case Expose: + case MapRequest: + handler[ev.type](&ev); + break; + case MotionNotify: + if ((ev.xmotion.time - lasttime) <= (1000 / 60)) + continue; + lasttime = ev.xmotion.time; + + nw = MAX(ev.xmotion.x - ocx - 2 * c->bw + 1, 1); + nh = MAX(ev.xmotion.y - ocy - 2 * c->bw + 1, 1); + if (c->mon->wx + nw >= selmon->wx && c->mon->wx + nw <= selmon->wx + selmon->ww + && c->mon->wy + nh >= selmon->wy && c->mon->wy + nh <= selmon->wy + selmon->wh) + { + if (!c->isfloating && selmon->lt[selmon->sellt]->arrange + && (abs(nw - c->w) > snap || abs(nh - c->h) > snap)) + togglefloating(NULL); + } + if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) + resize(c, c->x, c->y, nw, nh, 1); + break; + } + } while (ev.type != ButtonRelease); + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); + XUngrabPointer(dpy, CurrentTime); + while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); + if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { + sendmon(c, m); + selmon = m; + focus(NULL); + } +} + +void resizemousescroll(const Arg *arg) { + int nw, nh; + Client *c; + Monitor *m; + XEvent ev; + int dw = *((int*)arg->v + 1); + int dh = *(int*)arg->v; + + if (!(c = selmon->sel)) + return; + if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */ + return; + restack(selmon); + if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, + None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess) + return; + nw = MAX(c->w + dw, 1); + nh = MAX(c->h + dh, 1); + if (c->mon->wx + nw >= selmon->wx && c->mon->wx + nw <= selmon->wx + selmon->ww + && c->mon->wy + nh >= selmon->wy && c->mon->wy + nh <= selmon->wy + selmon->wh) + { + if (!c->isfloating && selmon->lt[selmon->sellt]->arrange + && (abs(nw - c->w) > snap || abs(nh - c->h) > snap)) + togglefloating(NULL); + } + if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) + resize(c, c->x, c->y, nw, nh, 1); + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); + XUngrabPointer(dpy, CurrentTime); + while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); + if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { + sendmon(c, m); + selmon = m; + focus(NULL); + } +} + +void restack(Monitor *m) { + Client *c; + XEvent ev; + XWindowChanges wc; + + drawbar(m); + if (!m->sel) + return; + if (m->sel->isfloating || !m->lt[m->sellt]->arrange) + XRaiseWindow(dpy, m->sel->win); + if (m->lt[m->sellt]->arrange) { + wc.stack_mode = Below; + wc.sibling = m->barwin; + for (c = m->stack; c; c = c->snext) + if (!c->isfloating && ISVISIBLE(c)) { + XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc); + wc.sibling = c->win; + } + } + if (m == selmon && (m->tagset[m->seltags] & m->sel->tags) && m->lt[m->sellt]->arrange != &monocle) + warp(m->sel); + XSync(dpy, False); + while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); +} + +void run(void) { + XEvent ev; + /* main event loop */ + XSync(dpy, False); + while (running && !XNextEvent(dpy, &ev)) + if (handler[ev.type]) + handler[ev.type](&ev); /* call handler */ +} + +void runAutostart(void) { + system("$HOME/.config/suckless/dwm/autostart"); +} + +void scan(void) { + unsigned int i, num; + Window d1, d2, *wins = NULL; + XWindowAttributes wa; + + if (XQueryTree(dpy, root, &d1, &d2, &wins, &num)) { + for (i = 0; i < num; i++) { + if (!XGetWindowAttributes(dpy, wins[i], &wa) + || wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1)) + continue; + if (wa.map_state == IsViewable || getstate(wins[i]) == IconicState) + manage(wins[i], &wa); + } + for (i = 0; i < num; i++) { /* now the transients */ + if (!XGetWindowAttributes(dpy, wins[i], &wa)) + continue; + if (XGetTransientForHint(dpy, wins[i], &d1) + && (wa.map_state == IsViewable || getstate(wins[i]) == IconicState)) + manage(wins[i], &wa); + } + if (wins) + XFree(wins); + } +} + +void sendmon(Client *c, Monitor *m) { + if (c->mon == m) + return; + unfocus(c, 1); + detach(c); + detachstack(c); + c->mon = m; + c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ + attachbottom(c); + attachstack(c); + focus(NULL); + arrange(NULL); +} + +void setclientstate(Client *c, long state) { + long data[] = { state, None }; + + XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32, + PropModeReplace, (unsigned char *)data, 2); +} + +int sendevent(Client *c, Atom proto) { + int n; + Atom *protocols; + int exists = 0; + XEvent ev; + + if (XGetWMProtocols(dpy, c->win, &protocols, &n)) { + while (!exists && n--) + exists = protocols[n] == proto; + XFree(protocols); + } + if (exists) { + ev.type = ClientMessage; + ev.xclient.window = c->win; + ev.xclient.message_type = wmatom[WMProtocols]; + ev.xclient.format = 32; + ev.xclient.data.l[0] = proto; + ev.xclient.data.l[1] = CurrentTime; + XSendEvent(dpy, c->win, False, NoEventMask, &ev); + } + return exists; +} + +void setfocus(Client *c) { + if (!c->neverfocus) { + XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); + XChangeProperty(dpy, root, netatom[NetActiveWindow], + XA_WINDOW, 32, PropModeReplace, + (unsigned char *) &(c->win), 1); + } + sendevent(c, wmatom[WMTakeFocus]); +} + +void setfullscreen(Client *c, int fullscreen) { + if (fullscreen && !c->isfullscreen) { + XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, + PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1); + c->isfullscreen = 1; + c->oldstate = c->isfloating; + c->oldbw = c->bw; + c->bw = 0; + c->isfloating = 1; + resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); + XRaiseWindow(dpy, c->win); + } else if (!fullscreen && c->isfullscreen){ + XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, + PropModeReplace, (unsigned char*)0, 0); + c->isfullscreen = 0; + c->isfloating = c->oldstate; + c->bw = c->oldbw; + c->x = c->oldx; + c->y = c->oldy; + c->w = c->oldw; + c->h = c->oldh; + resizeclient(c, c->x, c->y, c->w, c->h); + arrange(c->mon); + } +} + +void setsticky(Client *c, int sticky) { + + if(sticky && !c->issticky) { + XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, + PropModeReplace, (unsigned char *) &netatom[NetWMSticky], 1); + c->issticky = 1; + } else if(!sticky && c->issticky){ + XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, + PropModeReplace, (unsigned char *)0, 0); + c->issticky = 0; + arrange(c->mon); + } +} + +Layout *last_layout; +void fullscreen(const Arg *arg) { + if (selmon->showbar) { + for(last_layout = (Layout *)layouts; last_layout != selmon->lt[selmon->sellt]; last_layout++); + setlayout(&((Arg) { .v = &layouts[2] })); + } else { + setlayout(&((Arg) { .v = last_layout })); + } + togglebar(arg); +} + +void setlayout(const Arg *arg) { + if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) + selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag] ^= 1; + if (arg && arg->v) + selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt] = (Layout *)arg->v; + strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol); + if (selmon->sel) + arrange(selmon); + else + drawbar(selmon); +} + +/* arg > 1.0 will set mfact absolutely */ +void setmfact(const Arg *arg) { + float f; + + if (!arg || !selmon->lt[selmon->sellt]->arrange) + return; + f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0; + if (f < 0.05 || f > 0.95) + return; + selmon->mfact = 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); + + /* init screen */ + screen = DefaultScreen(dpy); + sw = DisplayWidth(dpy, screen); + sh = DisplayHeight(dpy, screen); + root = RootWindow(dpy, screen); + xinitvisual(); + drw = drw_create(dpy, screen, root, sw, sh, visual, depth, cmap); + if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) + die("no fonts could be loaded."); + lrpad = drw->fonts->h + horizpadbar; + bh = drw->fonts->h + vertpadbar; + sp = sidepad; + vp = (topbar == 1) ? vertpad : - vertpad; + updategeom(); + + /* init atoms */ + utf8string = XInternAtom(dpy, "UTF8_STRING", False); + wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); + wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); + wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False); + wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False); + netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); + netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); + netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); + netatom[NetWMIcon] = XInternAtom(dpy, "_NET_WM_ICON", 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[NetWMSticky] = XInternAtom(dpy, "_NET_WM_STATE_STICKY", False); + netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); + netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); + netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False); + /* init cursors */ + cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr); + cursor[CurResize] = drw_cur_create(drw, XC_sizing); + cursor[CurMove] = drw_cur_create(drw, XC_fleur); + /* init appearance */ + scheme = ecalloc(LENGTH(colors), sizeof(Clr *)); + for (i = 0; i < LENGTH(colors); i++) + scheme[i] = drw_scm_create(drw, colors[i], alphas[i], 3); + /* init bars */ + updatebars(); + updatestatus(); + /* supporting window for NetWMCheck */ + wmcheckwin = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 0, 0, 0); + XChangeProperty(dpy, wmcheckwin, netatom[NetWMCheck], XA_WINDOW, 32, + PropModeReplace, (unsigned char *) &wmcheckwin, 1); + XChangeProperty(dpy, wmcheckwin, netatom[NetWMName], utf8string, 8, + PropModeReplace, (unsigned char *) "dwm", 3); + XChangeProperty(dpy, root, netatom[NetWMCheck], XA_WINDOW, 32, + PropModeReplace, (unsigned char *) &wmcheckwin, 1); + /* EWMH support per view */ + XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32, + PropModeReplace, (unsigned char *) netatom, NetLast); + XDeleteProperty(dpy, root, netatom[NetClientList]); + /* select events */ + wa.cursor = cursor[CurNormal]->cursor; + wa.event_mask = SubstructureRedirectMask|SubstructureNotifyMask + |ButtonPressMask|PointerMotionMask|EnterWindowMask + |LeaveWindowMask|StructureNotifyMask|PropertyChangeMask; + XChangeWindowAttributes(dpy, root, CWEventMask|CWCursor, &wa); + XSelectInput(dpy, root, wa.event_mask); + grabkeys(); + focus(NULL); +} + +void seturgent(Client *c, int urg) { + XWMHints *wmh; + + c->isurgent = urg; + if (!(wmh = XGetWMHints(dpy, c->win))) + return; + wmh->flags = urg ? (wmh->flags | XUrgencyHint) : (wmh->flags & ~XUrgencyHint); + XSetWMHints(dpy, c->win, wmh); + XFree(wmh); +} + +void showhide(Client *c) { + if (!c) + return; + if (ISVISIBLE(c)) { + if ((c->tags & SPTAGMASK) && c->isfloating) { + c->x = c->mon->wx + (c->mon->ww / 2 - WIDTH(c) / 2); + c->y = c->mon->wy + (c->mon->wh / 2 - HEIGHT(c) / 2); + } + /* show clients top down */ + XMoveWindow(dpy, c->win, c->x, c->y); + if ((!c->mon->lt[c->mon->sellt]->arrange || c->isfloating) && !c->isfullscreen) + resize(c, c->x, c->y, c->w, c->h, 0); + showhide(c->snext); + } else { + /* hide clients bottom up */ + showhide(c->snext); + XMoveWindow(dpy, c->win, WIDTH(c) * -2, c->y); + } +} + +void sigchld(int unused) { + if (signal(SIGCHLD, sigchld) == SIG_ERR) + die("can't install SIGCHLD handler:"); + while (0 < waitpid(-1, NULL, WNOHANG)); +} + +void sighup(int unused) { + Arg a = {.i = 1}; + quit(&a); +} + +void sigterm(int unused) { + Arg a = {.i = 0}; + quit(&a); +} + +void spawn(const Arg *arg) { + if (fork() == 0) { + if (dpy) + close(ConnectionNumber(dpy)); + setsid(); + execvp(((char **)arg->v)[0], (char **)arg->v); + die("dwm: execvp '%s' failed:", ((char **)arg->v)[0]); + } +} + +void tag(const Arg *arg) { + if (selmon->sel && arg->ui & TAGMASK) { + selmon->sel->tags = arg->ui & TAGMASK; + focus(NULL); + arrange(selmon); + } +} + +void tagmon(const Arg *arg) { + if (!selmon->sel || !mons->next) + return; + sendmon(selmon->sel, dirtomon(arg->i)); +} + + +void tagtonext(const Arg *arg) { + unsigned int tmp; + + if (selmon->sel == NULL) + return; + + tmp = nexttag(); + tag(&(const Arg){.ui = tmp }); + view(&(const Arg){.ui = tmp }); +} + +void tagtoprev(const Arg *arg) { + unsigned int tmp; + + if (selmon->sel == NULL) + return; + + tmp = prevtag(); + tag(&(const Arg){.ui = tmp }); + view(&(const Arg){.ui = tmp }); +} + +void tile(Monitor *m) { + unsigned int i, n, h, mw, my, ty; + Client *c; + + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); + if (n == 0) + return; + + if (n > m->nmaster) + mw = m->nmaster + ? m->ww * (m->rmaster ? 1.0 - m->mfact : m->mfact) + : 0; + else + mw = m->ww; + for (i = my = ty = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) + if (i < m->nmaster) { + h = (m->wh - my) / (MIN(n, m->nmaster) - i); + resize(c, m->rmaster ? m->wx + m->ww - mw : m->wx, + m->wy + my, mw - (2*c->bw), h - (2*c->bw), 0); + if (my + HEIGHT(c) < m->wh) + my += HEIGHT(c); + } else { + h = (m->wh - ty) / (n - i); + resize(c, m->rmaster ? m->wx : m->wx + mw, m->wy + ty, + m->ww - mw - (2*c->bw), h - (2*c->bw), 0); + if (ty + HEIGHT(c) < m->wh) + ty += HEIGHT(c); + } +} + +void togglebar(const Arg *arg) { + selmon->showbar = selmon->pertag->showbars[selmon->pertag->curtag] = !selmon->showbar; + updatebarpos(selmon); + XMoveResizeWindow(dpy, selmon->barwin, selmon->wx + sp, selmon->by + vp, selmon->ww - 2 * sp, bh); + arrange(selmon); +} + +void togglefloating(const Arg *arg) { + if (!selmon->sel) + return; + if (selmon->sel->isfullscreen) /* no support for fullscreen windows */ + return; + selmon->sel->isfloating = !selmon->sel->isfloating || selmon->sel->isfixed; + if (selmon->sel->isfloating) + resize(selmon->sel, selmon->sel->x, selmon->sel->y, + selmon->sel->w, selmon->sel->h, 0); + arrange(selmon); +} + +void togglesticky(const Arg *arg) { + if (!selmon->sel) + return; + setsticky(selmon->sel, !selmon->sel->issticky); + arrange(selmon); +} + +void togglemaster(const Arg *arg) { + selmon->rmaster = !selmon->rmaster; + /* now mfact represents the left factor */ + selmon->mfact = 1.0 - selmon->mfact; + if (selmon->lt[selmon->sellt]->arrange) + arrange(selmon); +} + + +void togglescratch(const Arg *arg) { + Client *c; + unsigned int found = 0; + unsigned int scratchtag = SPTAG(arg->ui); + Arg sparg = {.v = scratchpads[arg->ui].cmd}; + + for (c = selmon->clients; c && !(found = c->tags & scratchtag); c = c->next); + if (found) { + unsigned int newtagset = selmon->tagset[selmon->seltags] ^ scratchtag; + if (newtagset) { + selmon->tagset[selmon->seltags] = newtagset; + focus(NULL); + arrange(selmon); + } + if (ISVISIBLE(c)) { + focus(c); + restack(selmon); + } + } else { + selmon->tagset[selmon->seltags] |= scratchtag; + spawn(&sparg); + } +} + +void toggletag(const Arg *arg) { + unsigned int newtags; + + if (!selmon->sel) + return; + newtags = selmon->sel->tags ^ (arg->ui & TAGMASK); + if (newtags) { + selmon->sel->tags = newtags; + focus(NULL); + arrange(selmon); + } +} + +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->prevtag = selmon->pertag->curtag; + selmon->pertag->curtag = 0; + } + + /* test if the user did not select the same tag */ + if (!(newtagset & 1 << (selmon->pertag->curtag - 1))) { + selmon->pertag->prevtag = selmon->pertag->curtag; + 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]; + + if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag]) + togglebar(NULL); + + focus(NULL); + arrange(selmon); + } +} + +void freeicon(Client *c) { + if (c->icon) { + XRenderFreePicture(dpy, c->icon); + c->icon = None; + } +} + +void unfocus(Client *c, int setfocus) { + if (!c) + return; + grabbuttons(c, 0); + XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel); + if (setfocus) { + XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); + XDeleteProperty(dpy, root, netatom[NetActiveWindow]); + } +} + +void unmanage(Client *c, int destroyed) { + Monitor *m = c->mon; + XWindowChanges wc; + + detach(c); + detachstack(c); + freeicon(c); + if (!destroyed) { + wc.border_width = c->oldbw; + XGrabServer(dpy); /* avoid race conditions */ + XSetErrorHandler(xerrordummy); + XSelectInput(dpy, c->win, NoEventMask); + XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */ + XUngrabButton(dpy, AnyButton, AnyModifier, c->win); + setclientstate(c, WithdrawnState); + XSync(dpy, False); + XSetErrorHandler(xerror); + XUngrabServer(dpy); + } + free(c); + focus(NULL); + updateclientlist(); + arrange(m); +} + +void unmapnotify(XEvent *e) { + Client *c; + XUnmapEvent *ev = &e->xunmap; + + if ((c = wintoclient(ev->window))) { + if (ev->send_event) + setclientstate(c, WithdrawnState); + else + unmanage(c, 0); + } +} + +void updatebars(void) { + Monitor *m; + XSetWindowAttributes wa = { + .override_redirect = True, + .background_pixel = 0, + .border_pixel = 0, + .colormap = cmap, + .event_mask = ButtonPressMask|ExposureMask + }; + XClassHint ch = {"dwm", "dwm"}; + for (m = mons; m; m = m->next) { + if (m->barwin) + continue; + m->barwin = XCreateWindow(dpy, root, m->wx + sp, m->by + vp, m->ww - 2 * sp, bh, 0, depth, InputOutput, visual, CWOverrideRedirect|CWBackPixel|CWBorderPixel|CWColormap|CWEventMask, &wa); + XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); + XMapRaised(dpy, m->barwin); + XSetClassHint(dpy, m->barwin, &ch); + } +} + +void updatebarpos(Monitor *m) { + m->wy = m->my; + m->wh = m->mh; + if (m->showbar) { + m->wh = m->wh - vertpad - bh; + m->by = m->topbar ? m->wy : m->wy + m->wh + vertpad; + m->wy = m->topbar ? m->wy + bh + vp : m->wy; + } else + m->by = -bh - vp; +} + +void updateclientlist() { + Client *c; + Monitor *m; + + XDeleteProperty(dpy, root, netatom[NetClientList]); + for (m = mons; m; m = m->next) + for (c = m->clients; c; c = c->next) + XChangeProperty(dpy, root, netatom[NetClientList], + XA_WINDOW, 32, PropModeAppend, + (unsigned char *) &(c->win), 1); +} + +int updategeom(void) { + int dirty = 0; + +#ifdef XINERAMA + if (XineramaIsActive(dpy)) { + int i, j, n, nn; + Client *c; + Monitor *m; + XineramaScreenInfo *info = XineramaQueryScreens(dpy, &nn); + XineramaScreenInfo *unique = NULL; + + for (n = 0, m = mons; m; m = m->next, n++); + /* only consider unique geometries as separate screens */ + unique = ecalloc(nn, sizeof(XineramaScreenInfo)); + for (i = 0, j = 0; i < nn; i++) + if (isuniquegeom(unique, j, &info[i])) + memcpy(&unique[j++], &info[i], sizeof(XineramaScreenInfo)); + XFree(info); + nn = j; + + /* new monitors if nn > n */ + for (i = n; i < nn; i++) { + for (m = mons; m && m->next; m = m->next); + if (m) + m->next = createmon(); + else + mons = createmon(); + } + for (i = 0, m = mons; i < nn && m; m = m->next, i++) + if (i >= n + || unique[i].x_org != m->mx || unique[i].y_org != m->my + || unique[i].width != m->mw || unique[i].height != m->mh) + { + dirty = 1; + m->num = i; + m->mx = m->wx = unique[i].x_org; + m->my = m->wy = unique[i].y_org; + m->mw = m->ww = unique[i].width; + m->mh = m->wh = unique[i].height; + updatebarpos(m); + } + /* removed monitors if n > nn */ + for (i = nn; i < n; i++) { + for (m = mons; m && m->next; m = m->next); + while ((c = m->clients)) { + dirty = 1; + m->clients = c->next; + detachstack(c); + c->mon = mons; + attach(c); + attachbottom(c); + attachstack(c); + } + if (m == selmon) + selmon = mons; + cleanupmon(m); + } + free(unique); + } else +#endif /* XINERAMA */ + { /* default monitor setup */ + if (!mons) + mons = createmon(); + if (mons->mw != sw || mons->mh != sh) { + dirty = 1; + mons->mw = mons->ww = sw; + mons->mh = mons->wh = sh; + updatebarpos(mons); + } + } + if (dirty) { + selmon = mons; + selmon = wintomon(root); + } + return dirty; +} + +void updatenumlockmask(void) { + unsigned int i, j; + XModifierKeymap *modmap; + + numlockmask = 0; + modmap = XGetModifierMapping(dpy); + for (i = 0; i < 8; i++) + for (j = 0; j < modmap->max_keypermod; j++) + if (modmap->modifiermap[i * modmap->max_keypermod + j] + == XKeysymToKeycode(dpy, XK_Num_Lock)) + numlockmask = (1 << i); + XFreeModifiermap(modmap); +} + +void updatesizehints(Client *c) { + long msize; + XSizeHints size; + + if (!XGetWMNormalHints(dpy, c->win, &size, &msize)) + /* size is uninitialized, ensure that size.flags aren't used */ + size.flags = PSize; + if (size.flags & PBaseSize) { + c->basew = size.base_width; + c->baseh = size.base_height; + } else if (size.flags & PMinSize) { + c->basew = size.min_width; + c->baseh = size.min_height; + } else + c->basew = c->baseh = 0; + if (size.flags & PResizeInc) { + c->incw = size.width_inc; + c->inch = size.height_inc; + } else + c->incw = c->inch = 0; + if (size.flags & PMaxSize) { + c->maxw = size.max_width; + c->maxh = size.max_height; + } else + c->maxw = c->maxh = 0; + if (size.flags & PMinSize) { + c->minw = size.min_width; + c->minh = size.min_height; + } else if (size.flags & PBaseSize) { + c->minw = size.base_width; + c->minh = size.base_height; + } else + c->minw = c->minh = 0; + if (size.flags & PAspect) { + c->mina = (float)size.min_aspect.y / size.min_aspect.x; + c->maxa = (float)size.max_aspect.x / size.max_aspect.y; + } else + c->maxa = c->mina = 0.0; + c->isfixed = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh); + c->hintsvalid = 1; +} + +void updatestatus(void) { + if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) + strcpy(stext, "dwm-"VERSION); + drawbar(selmon); +} + +void updatetitle(Client *c) { + if (!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name)) + gettextprop(c->win, XA_WM_NAME, c->name, sizeof c->name); + if (c->name[0] == '\0') /* hack to mark broken clients */ + strcpy(c->name, broken); +} + +void updateicon(Client *c) { + freeicon(c); + c->icon = geticonprop(c->win, &c->icw, &c->ich); +} + +void updatewindowtype(Client *c) { + Atom state = getatomprop(c, netatom[NetWMState]); + Atom wtype = getatomprop(c, netatom[NetWMWindowType]); + + if (state == netatom[NetWMFullscreen]) + setfullscreen(c, 1); + if (state == netatom[NetWMSticky]) { + setsticky(c, 1); + } + if (wtype == netatom[NetWMWindowTypeDialog]) + c->isfloating = 1; +} + +void updatewmhints(Client *c) { + XWMHints *wmh; + + if ((wmh = XGetWMHints(dpy, c->win))) { + if (c == selmon->sel && wmh->flags & XUrgencyHint) { + wmh->flags &= ~XUrgencyHint; + XSetWMHints(dpy, c->win, wmh); + } else + c->isurgent = (wmh->flags & XUrgencyHint) ? 1 : 0; + if (wmh->flags & InputHint) + c->neverfocus = !wmh->input; + else + c->neverfocus = 0; + XFree(wmh); + } +} + +void view(const Arg *arg) { + int i; + unsigned int tmptag; + + 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; + selmon->pertag->prevtag = selmon->pertag->curtag; + + if (arg->ui == ~0) + selmon->pertag->curtag = 0; + else { + for (i = 0; !(arg->ui & 1 << i); i++) ; + selmon->pertag->curtag = i + 1; + } + } else { + tmptag = selmon->pertag->prevtag; + selmon->pertag->prevtag = selmon->pertag->curtag; + selmon->pertag->curtag = tmptag; + } + + 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]; + + if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag]) + togglebar(NULL); + + focus(NULL); + arrange(selmon); +} + +void viewnext(const Arg *arg) { + view(&(const Arg){.ui = nexttag()}); +} + +void viewprev(const Arg *arg) { + view(&(const Arg){.ui = prevtag()}); +} + +void warp(const Client *c) { + int x, y; + + if (!c) { + XWarpPointer(dpy, None, root, 0, 0, 0, 0, selmon->wx + selmon->ww / 2, selmon->wy + selmon->wh / 2); + return; + } + + if (!getrootptr(&x, &y) || + (x > c->x - c->bw && + y > c->y - c->bw && + x < c->x + c->w + c->bw*2 && + y < c->y + c->h + c->bw*2) || + (y > c->mon->by && y < c->mon->by + bh) || + (c->mon->topbar && !y)) + return; + + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w / 2, c->h / 2); +} + +Client *wintoclient(Window w) { + Client *c; + Monitor *m; + + for (m = mons; m; m = m->next) + for (c = m->clients; c; c = c->next) + if (c->win == w) + return c; + return NULL; +} + +Monitor *wintomon(Window w) { + int x, y; + Client *c; + Monitor *m; + + if (w == root && getrootptr(&x, &y)) + return recttomon(x, y, 1, 1); + for (m = mons; m; m = m->next) + if (w == m->barwin) + return m; + if ((c = wintoclient(w))) + return c->mon; + return selmon; +} + +/* There's no way to check accesses to destroyed windows, thus those cases are + * ignored (especially on UnmapNotify's). Other types of errors call Xlibs + * default error handler, which may call exit. */ +int xerror(Display *dpy, XErrorEvent *ee) { + if (ee->error_code == BadWindow + || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch) + || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable) + || (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable) + || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable) + || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch) + || (ee->request_code == X_GrabButton && ee->error_code == BadAccess) + || (ee->request_code == X_GrabKey && ee->error_code == BadAccess) + || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable)) + return 0; + fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n", + ee->request_code, ee->error_code); + return xerrorxlib(dpy, ee); /* may call exit */ +} + +int xerrordummy(Display *dpy, XErrorEvent *ee) { + return 0; +} + +/* Startup Error handler to check if another window manager + * is already running. */ +int xerrorstart(Display *dpy, XErrorEvent *ee) { + die("dwm: another window manager is already running"); + return -1; +} + +void xinitvisual() { + XVisualInfo *infos; + XRenderPictFormat *fmt; + int nitems; + int i; + + XVisualInfo tpl = { + .screen = screen, + .depth = 32, + .class = TrueColor + }; + long masks = VisualScreenMask | VisualDepthMask | VisualClassMask; + + infos = XGetVisualInfo(dpy, masks, &tpl, &nitems); + visual = NULL; + for(i = 0; i < nitems; i ++) { + fmt = XRenderFindVisualFormat(dpy, infos[i].visual); + if (fmt->type == PictTypeDirect && fmt->direct.alphaMask) { + visual = infos[i].visual; + depth = infos[i].depth; + cmap = XCreateColormap(dpy, root, visual, AllocNone); + useargb = 1; + break; + } + } + + XFree(infos); + + if (! visual) { + visual = DefaultVisual(dpy, screen); + depth = DefaultDepth(dpy, screen); + cmap = DefaultColormap(dpy, screen); + } +} + +void zoom(const Arg *arg) { + Client *c = selmon->sel; + + if (!selmon->lt[selmon->sellt]->arrange || !c || c->isfloating) + return; + if (c == nexttiled(selmon->clients) && !(c = nexttiled(c->next))) + return; + pop(c); +} + +int main(int argc, char *argv[]) { + if (argc == 2 && !strcmp("-v", argv[1])) + die("dwm-"VERSION); + else if (argc != 1) + die("usage: dwm [-v]"); + if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) + fputs("warning: no locale support\n", stderr); + if (!(dpy = XOpenDisplay(NULL))) + die("dwm: cannot open display"); + checkotherwm(); + setup(); +#ifdef __OpenBSD__ + if (pledge("stdio rpath proc exec", NULL) == -1) + die("pledge"); +#endif /* __OpenBSD__ */ + scan(); + runAutostart(); + run(); + if(restart) execvp(argv[0], argv); + cleanup(); + XCloseDisplay(dpy); + return EXIT_SUCCESS; +} diff --git a/dwm.png b/dwm.png new file mode 100644 index 0000000000000000000000000000000000000000..507f5dad9b45f1a004eb127f7e1833c962fd4ee8 GIT binary patch literal 1160 zcmeAS@N?(olHy`uVBq!ia0y~yVEh7PU*KQ^k{{b$6pC<*tp*KbK%zx=5wprWj-XP z?OQ9yxZCRNnYSRR?~nQRPTBrA`jGtR)3OXqEQ1vL0iFJ(IBi~7KgZ5kTL!alCj0L% zslUtjy=MCMzjx2Xte5*ZC${OIg>m_6_m-G2{)XP#nKOT1Rpz_#aNhe*+wPw~v;3;^ zyvNh62X~^H6UJ}x!p8W1c>jaYw?)sMUuEXI^)UM08aW2*+o~_jjXzg@<%pYVTpzhl l^~lesd<=|DgBb5;2wE-WVLxwsHn0?7@O1TaS?83{1OR(**ysQN literal 0 HcmV?d00001 diff --git a/patches/dwm-adjacenttag-6.2.diff b/patches/dwm-adjacenttag-6.2.diff new file mode 100644 index 000000000..6121f65e5 --- /dev/null +++ b/patches/dwm-adjacenttag-6.2.diff @@ -0,0 +1,126 @@ +diff -up a/config.def.h b/config.def.h +--- a/config.def.h 2021-10-02 13:57:18.011307099 +0100 ++++ b/config.def.h 2021-10-02 13:58:07.812080253 +0100 +@@ -84,6 +84,10 @@ static Key keys[] = { + { MODKEY, XK_period, focusmon, {.i = +1 } }, + { MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } }, + { MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } }, ++ { MODKEY, XK_Right, viewnext, {0} }, ++ { MODKEY, XK_Left, viewprev, {0} }, ++ { MODKEY|ShiftMask, XK_Right, tagtonext, {0} }, ++ { MODKEY|ShiftMask, XK_Left, tagtoprev, {0} }, + TAGKEYS( XK_1, 0) + TAGKEYS( XK_2, 1) + TAGKEYS( XK_3, 2) +diff -up a/dwm.c b/dwm.c +--- a/dwm.c 2021-10-02 13:57:18.011307099 +0100 ++++ b/dwm.c 2021-10-02 14:21:17.063622953 +0100 +@@ -183,8 +183,10 @@ static void maprequest(XEvent *e); + static void monocle(Monitor *m); + static void motionnotify(XEvent *e); + static void movemouse(const Arg *arg); ++static unsigned int nexttag(void); + static Client *nexttiled(Client *c); + static void pop(Client *); ++static unsigned int prevtag(void); + static void propertynotify(XEvent *e); + static void quit(const Arg *arg); + static Monitor *recttomon(int x, int y, int w, int h); +@@ -208,6 +210,8 @@ static void sigchld(int unused); + static void spawn(const Arg *arg); + static void tag(const Arg *arg); + static void tagmon(const Arg *arg); ++static void tagtonext(const Arg *arg); ++static void tagtoprev(const Arg *arg); + static void tile(Monitor *); + static void togglebar(const Arg *arg); + static void togglefloating(const Arg *arg); +@@ -227,6 +231,8 @@ static void updatetitle(Client *c); + static void updatewindowtype(Client *c); + static void updatewmhints(Client *c); + static void view(const Arg *arg); ++static void viewnext(const Arg *arg); ++static void viewprev(const Arg *arg); + static Client *wintoclient(Window w); + static Monitor *wintomon(Window w); + static int xerror(Display *dpy, XErrorEvent *ee); +@@ -1192,6 +1198,13 @@ movemouse(const Arg *arg) + } + } + ++unsigned int ++nexttag(void) ++{ ++ unsigned int seltag = selmon->tagset[selmon->seltags]; ++ return seltag == (1 << (LENGTH(tags) - 1)) ? 1 : seltag << 1; ++} ++ + Client * + nexttiled(Client *c) + { +@@ -1208,6 +1221,13 @@ pop(Client *c) + arrange(c->mon); + } + ++unsigned int ++prevtag(void) ++{ ++ unsigned int seltag = selmon->tagset[selmon->seltags]; ++ return seltag == 1 ? (1 << (LENGTH(tags) - 1)) : seltag >> 1; ++} ++ + void + propertynotify(XEvent *e) + { +@@ -1671,6 +1691,32 @@ tagmon(const Arg *arg) + } + + void ++tagtonext(const Arg *arg) ++{ ++ unsigned int tmp; ++ ++ if (selmon->sel == NULL) ++ return; ++ ++ tmp = nexttag(); ++ tag(&(const Arg){.ui = tmp }); ++ view(&(const Arg){.ui = tmp }); ++} ++ ++void ++tagtoprev(const Arg *arg) ++{ ++ unsigned int tmp; ++ ++ if (selmon->sel == NULL) ++ return; ++ ++ tmp = prevtag(); ++ tag(&(const Arg){.ui = tmp }); ++ view(&(const Arg){.ui = tmp }); ++} ++ ++void + tile(Monitor *m) + { + unsigned int i, n, h, mw, my, ty; +@@ -2044,6 +2090,18 @@ view(const Arg *arg) + arrange(selmon); + } + ++void ++viewnext(const Arg *arg) ++{ ++ view(&(const Arg){.ui = nexttag()}); ++} ++ ++void ++viewprev(const Arg *arg) ++{ ++ view(&(const Arg){.ui = prevtag()}); ++} ++ + Client * + wintoclient(Window w) + { diff --git a/patches/dwm-alpha-20230401-348f655.diff b/patches/dwm-alpha-20230401-348f655.diff new file mode 100644 index 000000000..c948f0419 --- /dev/null +++ b/patches/dwm-alpha-20230401-348f655.diff @@ -0,0 +1,288 @@ +From ad5887df95fda706291c81ee143d0786a1717b12 Mon Sep 17 00:00:00 2001 +From: getimiskon +Date: Sat, 1 Apr 2023 16:22:01 +0300 +Subject: [PATCH] Allow dwm to have translucent bars, while keeping all the + text on it opaque, just like the alpha-patch for st. Updated for 348f655. + +--- + config.def.h | 7 +++++++ + config.mk | 2 +- + drw.c | 26 ++++++++++++----------- + drw.h | 9 +++++--- + dwm.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++------ + 5 files changed, 81 insertions(+), 22 deletions(-) + +diff --git a/config.def.h b/config.def.h +index 9efa774..8b3789a 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -12,11 +12,18 @@ static const char col_gray2[] = "#444444"; + static const char col_gray3[] = "#bbbbbb"; + static const char col_gray4[] = "#eeeeee"; + static const char col_cyan[] = "#005577"; ++static const unsigned int baralpha = 0xd0; ++static const unsigned int borderalpha = OPAQUE; + static const char *colors[][3] = { + /* fg bg border */ + [SchemeNorm] = { col_gray3, col_gray1, col_gray2 }, + [SchemeSel] = { col_gray4, col_cyan, col_cyan }, + }; ++static const unsigned int alphas[][3] = { ++ /* fg bg border*/ ++ [SchemeNorm] = { OPAQUE, baralpha, borderalpha }, ++ [SchemeSel] = { OPAQUE, baralpha, borderalpha }, ++}; + + /* tagging */ + static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; +diff --git a/config.mk b/config.mk +index ba64d3d..d609c42 100644 +--- a/config.mk ++++ b/config.mk +@@ -23,7 +23,7 @@ FREETYPEINC = /usr/include/freetype2 + + # includes and libs + INCS = -I${X11INC} -I${FREETYPEINC} +-LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} ++LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} -lXrender + + # flags + CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} +diff --git a/drw.c b/drw.c +index a58a2b4..d18e8d8 100644 +--- a/drw.c ++++ b/drw.c +@@ -61,7 +61,7 @@ utf8decode(const char *c, long *u, size_t clen) + } + + Drw * +-drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h) ++drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h, Visual *visual, unsigned int depth, Colormap cmap) + { + Drw *drw = ecalloc(1, sizeof(Drw)); + +@@ -70,8 +70,11 @@ drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h + 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); ++ drw->visual = visual; ++ drw->depth = depth; ++ drw->cmap = cmap; ++ drw->drawable = XCreatePixmap(dpy, root, w, h, depth); ++ drw->gc = XCreateGC(dpy, drw->drawable, 0, NULL); + XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter); + + return drw; +@@ -87,7 +90,7 @@ drw_resize(Drw *drw, unsigned int w, unsigned int h) + 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)); ++ drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, drw->depth); + } + + void +@@ -181,21 +184,22 @@ drw_fontset_free(Fnt *font) + } + + void +-drw_clr_create(Drw *drw, Clr *dest, const char *clrname) ++drw_clr_create(Drw *drw, Clr *dest, const char *clrname, unsigned int alpha) + { + if (!drw || !dest || !clrname) + return; + +- if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen), +- DefaultColormap(drw->dpy, drw->screen), ++ if (!XftColorAllocName(drw->dpy, drw->visual, drw->cmap, + clrname, dest)) + die("error, cannot allocate color '%s'", clrname); ++ ++ dest->pixel = (dest->pixel & 0x00ffffffU) | (alpha << 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, const char *clrnames[], size_t clrcount) ++drw_scm_create(Drw *drw, const char *clrnames[], const unsigned int alphas[], size_t clrcount) + { + size_t i; + Clr *ret; +@@ -205,7 +209,7 @@ drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount) + return NULL; + + for (i = 0; i < clrcount; i++) +- drw_clr_create(drw, &ret[i], clrnames[i]); ++ drw_clr_create(drw, &ret[i], clrnames[i], alphas[i]); + return ret; + } + +@@ -263,9 +267,7 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp + } 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)); ++ d = XftDrawCreate(drw->dpy, drw->drawable, drw->visual, drw->cmap); + x += lpad; + w -= lpad; + } +diff --git a/drw.h b/drw.h +index 6471431..2143533 100644 +--- a/drw.h ++++ b/drw.h +@@ -20,6 +20,9 @@ typedef struct { + Display *dpy; + int screen; + Window root; ++ Visual *visual; ++ unsigned int depth; ++ Colormap cmap; + Drawable drawable; + GC gc; + Clr *scheme; +@@ -27,7 +30,7 @@ typedef struct { + } Drw; + + /* Drawable abstraction */ +-Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h); ++Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h, Visual *visual, unsigned int depth, Colormap cmap); + void drw_resize(Drw *drw, unsigned int w, unsigned int h); + void drw_free(Drw *drw); + +@@ -39,8 +42,8 @@ unsigned int drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int + void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h); + + /* Colorscheme abstraction */ +-void drw_clr_create(Drw *drw, Clr *dest, const char *clrname); +-Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount); ++void drw_clr_create(Drw *drw, Clr *dest, const char *clrname, unsigned int alpha); ++Clr *drw_scm_create(Drw *drw, const char *clrnames[], const unsigned int alphas[], size_t clrcount); + + /* Cursor abstraction */ + Cur *drw_cur_create(Drw *drw, int shape); +diff --git a/dwm.c b/dwm.c +index c2bd871..3b34de8 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -56,6 +56,7 @@ + #define HEIGHT(X) ((X)->h + 2 * (X)->bw) + #define TAGMASK ((1 << LENGTH(tags)) - 1) + #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) ++#define OPAQUE 0xffU + + /* enums */ + enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ +@@ -232,6 +233,7 @@ 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 xinitvisual(); + static void zoom(const Arg *arg); + + /* variables */ +@@ -268,6 +270,11 @@ static Drw *drw; + static Monitor *mons, *selmon; + static Window root, wmcheckwin; + ++static int useargb = 0; ++static Visual *visual; ++static int depth; ++static Colormap cmap; ++ + /* configuration, allows nested code to access above variables */ + #include "config.h" + +@@ -1558,7 +1565,8 @@ setup(void) + sw = DisplayWidth(dpy, screen); + sh = DisplayHeight(dpy, screen); + root = RootWindow(dpy, screen); +- drw = drw_create(dpy, screen, root, sw, sh); ++ xinitvisual(); ++ drw = drw_create(dpy, screen, root, sw, sh, visual, depth, cmap); + if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) + die("no fonts could be loaded."); + lrpad = drw->fonts->h; +@@ -1586,7 +1594,7 @@ setup(void) + /* init appearance */ + scheme = ecalloc(LENGTH(colors), sizeof(Clr *)); + for (i = 0; i < LENGTH(colors); i++) +- scheme[i] = drw_scm_create(drw, colors[i], 3); ++ scheme[i] = drw_scm_create(drw, colors[i], alphas[i], 3); + /* init bars */ + updatebars(); + updatestatus(); +@@ -1813,16 +1821,18 @@ updatebars(void) + Monitor *m; + XSetWindowAttributes wa = { + .override_redirect = True, +- .background_pixmap = ParentRelative, ++ .background_pixel = 0, ++ .border_pixel = 0, ++ .colormap = cmap, + .event_mask = ButtonPressMask|ExposureMask + }; + XClassHint ch = {"dwm", "dwm"}; + for (m = mons; m; m = m->next) { + if (m->barwin) + continue; +- m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen), +- CopyFromParent, DefaultVisual(dpy, screen), +- CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); ++ m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, depth, ++ InputOutput, visual, ++ CWOverrideRedirect|CWBackPixel|CWBorderPixel|CWColormap|CWEventMask, &wa); + XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); + XMapRaised(dpy, m->barwin); + XSetClassHint(dpy, m->barwin, &ch); +@@ -2120,6 +2130,43 @@ xerrorstart(Display *dpy, XErrorEvent *ee) + return -1; + } + ++void ++xinitvisual() ++{ ++ XVisualInfo *infos; ++ XRenderPictFormat *fmt; ++ int nitems; ++ int i; ++ ++ XVisualInfo tpl = { ++ .screen = screen, ++ .depth = 32, ++ .class = TrueColor ++ }; ++ long masks = VisualScreenMask | VisualDepthMask | VisualClassMask; ++ ++ infos = XGetVisualInfo(dpy, masks, &tpl, &nitems); ++ visual = NULL; ++ for(i = 0; i < nitems; i ++) { ++ fmt = XRenderFindVisualFormat(dpy, infos[i].visual); ++ if (fmt->type == PictTypeDirect && fmt->direct.alphaMask) { ++ visual = infos[i].visual; ++ depth = infos[i].depth; ++ cmap = XCreateColormap(dpy, root, visual, AllocNone); ++ useargb = 1; ++ break; ++ } ++ } ++ ++ XFree(infos); ++ ++ if (! visual) { ++ visual = DefaultVisual(dpy, screen); ++ depth = DefaultDepth(dpy, screen); ++ cmap = DefaultColormap(dpy, screen); ++ } ++} ++ + void + zoom(const Arg *arg) + { +-- +2.40.0 + diff --git a/patches/dwm-alwayscenter-20200625-f04cac6.diff b/patches/dwm-alwayscenter-20200625-f04cac6.diff new file mode 100644 index 000000000..03ea9ef2a --- /dev/null +++ b/patches/dwm-alwayscenter-20200625-f04cac6.diff @@ -0,0 +1,12 @@ +diff -up dwm/dwm.c dwmmod/dwm.c +--- dwm/dwm.c 2020-06-25 00:21:30.383692180 -0300 ++++ dwmmod/dwm.c 2020-06-25 00:20:35.643692330 -0300 +@@ -1057,6 +1057,8 @@ manage(Window w, XWindowAttributes *wa) + updatewindowtype(c); + updatesizehints(c); + updatewmhints(c); ++ c->x = c->mon->mx + (c->mon->mw - WIDTH(c)) / 2; ++ c->y = c->mon->my + (c->mon->mh - HEIGHT(c)) / 2; + XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask); + grabbuttons(c, 0); + if (!c->isfloating) diff --git a/patches/dwm-attachbottom-6.3.diff b/patches/dwm-attachbottom-6.3.diff new file mode 100644 index 000000000..c3955f9b1 --- /dev/null +++ b/patches/dwm-attachbottom-6.3.diff @@ -0,0 +1,54 @@ +diff -up dwm-6.3/dwm.c dwm-6.3-attachbottom/dwm.c +--- dwm-6.3/dwm.c 2022-01-07 12:42:18.000000000 +0100 ++++ dwm-6.3-attachbottom/dwm.c 2022-08-17 22:14:41.813809073 +0200 +@@ -147,6 +147,7 @@ static int applysizehints(Client *c, int + static void arrange(Monitor *m); + static void arrangemon(Monitor *m); + static void attach(Client *c); ++static void attachbottom(Client *c); + static void attachstack(Client *c); + static void buttonpress(XEvent *e); + static void checkotherwm(void); +@@ -408,6 +409,15 @@ attach(Client *c) + } + + void ++attachbottom(Client *c) ++{ ++ Client **tc; ++ c->next = NULL; ++ for (tc = &c->mon->clients; *tc; tc = &(*tc)->next); ++ *tc = c; ++} ++ ++void + attachstack(Client *c) + { + c->snext = c->mon->stack; +@@ -1066,7 +1076,7 @@ manage(Window w, XWindowAttributes *wa) + c->isfloating = c->oldstate = trans != None || c->isfixed; + if (c->isfloating) + XRaiseWindow(dpy, c->win); +- attach(c); ++ attachbottom(c); + attachstack(c); + XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, + (unsigned char *) &(c->win), 1); +@@ -1421,7 +1431,7 @@ sendmon(Client *c, Monitor *m) + detachstack(c); + c->mon = m; + c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ +- attach(c); ++ attachbottom(c); + attachstack(c); + focus(NULL); + arrange(NULL); +@@ -1903,7 +1913,7 @@ updategeom(void) + m->clients = c->next; + detachstack(c); + c->mon = mons; +- attach(c); ++ attachbottom(c); + attachstack(c); + } + if (m == selmon) diff --git a/patches/dwm-autostart-20161205-bb3bd6f.diff b/patches/dwm-autostart-20161205-bb3bd6f.diff new file mode 100644 index 000000000..6f11eaf28 --- /dev/null +++ b/patches/dwm-autostart-20161205-bb3bd6f.diff @@ -0,0 +1,39 @@ +commit 5918623c5bd7fda155bf9dc3d33890c4ae1722d0 +Author: Simon Bremer +Date: Thu Dec 22 17:31:07 2016 +0100 + + Applied and fixed autostart patch for previous version; + +diff --git a/dwm.c b/dwm.c +index d27cb67..066ed71 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -194,6 +194,7 @@ static void resizeclient(Client *c, int x, int y, int w, int h); + static void resizemouse(const Arg *arg); + static void restack(Monitor *m); + static void run(void); ++static void runAutostart(void); + static void scan(void); + static int sendevent(Client *c, Atom proto); + static void sendmon(Client *c, Monitor *m); +@@ -1386,6 +1387,12 @@ run(void) + } + + void ++runAutostart(void) { ++ system("cd ~/.dwm; ./autostart_blocking.sh"); ++ system("cd ~/.dwm; ./autostart.sh &"); ++} ++ ++void + scan(void) + { + unsigned int i, num; +@@ -2145,6 +2152,7 @@ main(int argc, char *argv[]) + checkotherwm(); + setup(); + scan(); ++ runAutostart(); + run(); + cleanup(); + XCloseDisplay(dpy); diff --git a/patches/dwm-barpadding-20211020-a786211.diff b/patches/dwm-barpadding-20211020-a786211.diff new file mode 100644 index 000000000..784218190 --- /dev/null +++ b/patches/dwm-barpadding-20211020-a786211.diff @@ -0,0 +1,118 @@ +From a3cfb215f7f647d83d67e33df8f33a73e43bd65f Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Wed, 20 Oct 2021 09:14:07 +0200 +Subject: [PATCH] barpadding: adds space between the statusbar and the edge of + the screen + +--- + config.def.h | 2 ++ + dwm.c | 25 +++++++++++++++---------- + 2 files changed, 17 insertions(+), 10 deletions(-) + +diff --git a/config.def.h b/config.def.h +index a2ac963..f0b739f 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -5,6 +5,8 @@ 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 vertpad = 10; /* vertical padding of bar */ ++static const int sidepad = 10; /* horizontal padding of bar */ + static const char *fonts[] = { "monospace:size=10" }; + static const char dmenufont[] = "monospace:size=10"; + static const char col_gray1[] = "#222222"; +diff --git a/dwm.c b/dwm.c +index 5e4d494..df6d0d7 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -242,6 +242,8 @@ static int screen; + static int sw, sh; /* X display screen geometry width, height */ + static int bh, blw = 0; /* bar geometry */ + static int lrpad; /* sum of left and right padding for text */ ++static int vp; /* vertical padding for bar */ ++static int sp; /* side padding for bar */ + static int (*xerrorxlib)(Display *, XErrorEvent *); + static unsigned int numlockmask = 0; + static void (*handler[LASTEvent]) (XEvent *) = { +@@ -568,7 +570,7 @@ configurenotify(XEvent *e) + for (c = m->clients; c; c = c->next) + if (c->isfullscreen) + resizeclient(c, m->mx, m->my, m->mw, m->mh); +- XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); ++ XMoveResizeWindow(dpy, m->barwin, m->wx + sp, m->by + vp, m->ww - 2 * sp, bh); + } + focus(NULL); + arrange(NULL); +@@ -706,7 +708,7 @@ drawbar(Monitor *m) + if (m == selmon) { /* status is only drawn on selected monitor */ + drw_setscheme(drw, scheme[SchemeNorm]); + tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ +- drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0); ++ drw_text(drw, m->ww - tw - 2 * sp, 0, tw, bh, 0, stext, 0); + } + + for (c = m->clients; c; c = c->next) { +@@ -732,12 +734,12 @@ drawbar(Monitor *m) + if ((w = m->ww - tw - x) > bh) { + if (m->sel) { + drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); +- drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); ++ drw_text(drw, x, 0, w - 2 * sp, bh, lrpad / 2, m->sel->name, 0); + if (m->sel->isfloating) + drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0); + } else { + drw_setscheme(drw, scheme[SchemeNorm]); +- drw_rect(drw, x, 0, w, bh, 1, 1); ++ drw_rect(drw, x, 0, w - 2 * sp, bh, 1, 1); + } + } + drw_map(drw, m->barwin, 0, 0, m->ww, bh); +@@ -1547,7 +1549,10 @@ setup(void) + die("no fonts could be loaded."); + lrpad = drw->fonts->h; + bh = drw->fonts->h + 2; ++ sp = sidepad; ++ vp = (topbar == 1) ? vertpad : - vertpad; + updategeom(); ++ + /* init atoms */ + utf8string = XInternAtom(dpy, "UTF8_STRING", False); + wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); +@@ -1704,7 +1709,7 @@ togglebar(const Arg *arg) + { + selmon->showbar = !selmon->showbar; + updatebarpos(selmon); +- XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); ++ XMoveResizeWindow(dpy, selmon->barwin, selmon->wx + sp, selmon->by + vp, selmon->ww - 2 * sp, bh); + arrange(selmon); + } + +@@ -1814,7 +1819,7 @@ updatebars(void) + for (m = mons; m; m = m->next) { + if (m->barwin) + continue; +- m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen), ++ m->barwin = XCreateWindow(dpy, root, m->wx + sp, m->by + vp, m->ww - 2 * sp, bh, 0, DefaultDepth(dpy, screen), + CopyFromParent, DefaultVisual(dpy, screen), + CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); + XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); +@@ -1829,11 +1834,11 @@ updatebarpos(Monitor *m) + m->wy = m->my; + m->wh = m->mh; + if (m->showbar) { +- m->wh -= bh; +- m->by = m->topbar ? m->wy : m->wy + m->wh; +- m->wy = m->topbar ? m->wy + bh : m->wy; ++ m->wh = m->wh - vertpad - bh; ++ m->by = m->topbar ? m->wy : m->wy + m->wh + vertpad; ++ m->wy = m->topbar ? m->wy + bh + vp : m->wy; + } else +- m->by = -bh; ++ m->by = -bh - vp; + } + + void +-- +2.33.0 + diff --git a/patches/dwm-centeredmaster-6.1.diff b/patches/dwm-centeredmaster-6.1.diff new file mode 100644 index 000000000..692689279 --- /dev/null +++ b/patches/dwm-centeredmaster-6.1.diff @@ -0,0 +1,142 @@ +diff --git a/config.def.h b/config.def.h +index 7054c06..527b214 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -39,6 +39,8 @@ static const Layout layouts[] = { + { "[]=", tile }, /* first entry is default */ + { "><>", NULL }, /* no layout function means floating behavior */ + { "[M]", monocle }, ++ { "|M|", centeredmaster }, ++ { ">M>", centeredfloatingmaster }, + }; + + /* key definitions */ +@@ -74,6 +76,8 @@ static Key keys[] = { + { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, + { MODKEY, XK_f, setlayout, {.v = &layouts[1]} }, + { MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, ++ { MODKEY, XK_u, setlayout, {.v = &layouts[3]} }, ++ { MODKEY, XK_o, setlayout, {.v = &layouts[4]} }, + { MODKEY, XK_space, setlayout, {0} }, + { MODKEY|ShiftMask, XK_space, togglefloating, {0} }, + { MODKEY, XK_0, view, {.ui = ~0 } }, +diff --git a/dwm.c b/dwm.c +index 0362114..1e81412 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -233,6 +233,8 @@ 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); ++static void centeredmaster(Monitor *m); ++static void centeredfloatingmaster(Monitor *m); + + /* variables */ + static const char broken[] = "broken"; +@@ -2139,3 +2141,106 @@ main(int argc, char *argv[]) + XCloseDisplay(dpy); + return EXIT_SUCCESS; + } ++ ++void ++centeredmaster(Monitor *m) ++{ ++ unsigned int i, n, h, mw, mx, my, oty, ety, tw; ++ Client *c; ++ ++ /* count number of clients in the selected monitor */ ++ for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); ++ if (n == 0) ++ return; ++ ++ /* initialize areas */ ++ mw = m->ww; ++ mx = 0; ++ my = 0; ++ tw = mw; ++ ++ if (n > m->nmaster) { ++ /* go mfact box in the center if more than nmaster clients */ ++ mw = m->nmaster ? m->ww * m->mfact : 0; ++ tw = m->ww - mw; ++ ++ if (n - m->nmaster > 1) { ++ /* only one client */ ++ mx = (m->ww - mw) / 2; ++ tw = (m->ww - mw) / 2; ++ } ++ } ++ ++ oty = 0; ++ ety = 0; ++ for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) ++ if (i < m->nmaster) { ++ /* nmaster clients are stacked vertically, in the center ++ * of the screen */ ++ h = (m->wh - my) / (MIN(n, m->nmaster) - i); ++ resize(c, m->wx + mx, m->wy + my, mw - (2*c->bw), ++ h - (2*c->bw), 0); ++ my += HEIGHT(c); ++ } else { ++ /* stack clients are stacked vertically */ ++ if ((i - m->nmaster) % 2 ) { ++ h = (m->wh - ety) / ( (1 + n - i) / 2); ++ resize(c, m->wx, m->wy + ety, tw - (2*c->bw), ++ h - (2*c->bw), 0); ++ ety += HEIGHT(c); ++ } else { ++ h = (m->wh - oty) / ((1 + n - i) / 2); ++ resize(c, m->wx + mx + mw, m->wy + oty, ++ tw - (2*c->bw), h - (2*c->bw), 0); ++ oty += HEIGHT(c); ++ } ++ } ++} ++ ++void ++centeredfloatingmaster(Monitor *m) ++{ ++ unsigned int i, n, w, mh, mw, mx, mxo, my, myo, tx; ++ Client *c; ++ ++ /* count number of clients in the selected monitor */ ++ for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); ++ if (n == 0) ++ return; ++ ++ /* initialize nmaster area */ ++ if (n > m->nmaster) { ++ /* go mfact box in the center if more than nmaster clients */ ++ if (m->ww > m->wh) { ++ mw = m->nmaster ? m->ww * m->mfact : 0; ++ mh = m->nmaster ? m->wh * 0.9 : 0; ++ } else { ++ mh = m->nmaster ? m->wh * m->mfact : 0; ++ mw = m->nmaster ? m->ww * 0.9 : 0; ++ } ++ mx = mxo = (m->ww - mw) / 2; ++ my = myo = (m->wh - mh) / 2; ++ } else { ++ /* go fullscreen if all clients are in the master area */ ++ mh = m->wh; ++ mw = m->ww; ++ mx = mxo = 0; ++ my = myo = 0; ++ } ++ ++ for(i = tx = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) ++ if (i < m->nmaster) { ++ /* nmaster clients are stacked horizontally, in the center ++ * of the screen */ ++ w = (mw + mxo - mx) / (MIN(n, m->nmaster) - i); ++ resize(c, m->wx + mx, m->wy + my, w - (2*c->bw), ++ mh - (2*c->bw), 0); ++ mx += WIDTH(c); ++ } else { ++ /* stack clients are stacked horizontally */ ++ w = (m->ww - tx) / (n - i); ++ resize(c, m->wx + tx, m->wy, w - (2*c->bw), ++ m->wh - (2*c->bw), 0); ++ tx += WIDTH(c); ++ } ++} diff --git a/patches/dwm-clientindicators-6.2.diff b/patches/dwm-clientindicators-6.2.diff new file mode 100644 index 000000000..f2e9afb84 --- /dev/null +++ b/patches/dwm-clientindicators-6.2.diff @@ -0,0 +1,48 @@ +From 8c72f9ea7c9cd8d254b52a4f7059113c41483597 Mon Sep 17 00:00:00 2001 +From: Miles Alan +Date: Mon, 17 Aug 2020 20:33:45 -0500 +Subject: [PATCH] Draws a dot indicator overlayed on each tag icon for each + client. The selected client is drawn as a larger horizontal line. + +--- + dwm.c | 14 ++++++++++---- + 1 file changed, 10 insertions(+), 4 deletions(-) + +diff --git a/dwm.c b/dwm.c +index 4465af1..e0ca438 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -695,6 +695,7 @@ dirtomon(int dir) + void + drawbar(Monitor *m) + { ++ int indn; + int x, w, sw = 0; + int boxs = drw->fonts->h / 9; + int boxw = drw->fonts->h / 6 + 2; +@@ -715,13 +716,18 @@ drawbar(Monitor *m) + } + x = 0; + for (i = 0; i < LENGTH(tags); i++) { ++ indn = 0; + w = TEXTW(tags[i]); + drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]); + drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i); +- if (occ & 1 << i) +- drw_rect(drw, x + boxs, boxs, boxw, boxw, +- m == selmon && selmon->sel && selmon->sel->tags & 1 << i, +- urg & 1 << i); ++ ++ for (c = m->clients; c; c = c->next) { ++ if (c->tags & (1 << i)) { ++ drw_rect(drw, x, 1 + (indn * 2), selmon->sel == c ? 6 : 1, 1, 1, urg & 1 << i); ++ indn++; ++ } ++ } ++ + x += w; + } + w = blw = TEXTW(m->ltsymbol); +-- +2.25.4 + diff --git a/patches/dwm-combo-6.1.diff b/patches/dwm-combo-6.1.diff new file mode 100644 index 000000000..32017db77 --- /dev/null +++ b/patches/dwm-combo-6.1.diff @@ -0,0 +1,75 @@ +diff --git a/dwm.c b/dwm.c +index 0362114..40b7a99 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -234,6 +234,11 @@ static int xerrordummy(Display *dpy, XErrorEvent *ee); + static int xerrorstart(Display *dpy, XErrorEvent *ee); + static void zoom(const Arg *arg); + ++static void keyrelease(XEvent *e); ++static void combotag(const Arg *arg); ++static void comboview(const Arg *arg); ++ ++ + /* variables */ + static const char broken[] = "broken"; + static char stext[256]; +@@ -244,6 +249,7 @@ static int (*xerrorxlib)(Display *, XErrorEvent *); + static unsigned int numlockmask = 0; + static void (*handler[LASTEvent]) (XEvent *) = { + [ButtonPress] = buttonpress, ++ [ButtonRelease] = keyrelease, + [ClientMessage] = clientmessage, + [ConfigureRequest] = configurerequest, + [ConfigureNotify] = configurenotify, +@@ -251,6 +257,7 @@ static void (*handler[LASTEvent]) (XEvent *) = { + [EnterNotify] = enternotify, + [Expose] = expose, + [FocusIn] = focusin, ++ [KeyRelease] = keyrelease, + [KeyPress] = keypress, + [MappingNotify] = mappingnotify, + [MapRequest] = maprequest, +@@ -274,6 +281,42 @@ static Window root; + struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; + + /* function implementations */ ++static int combo = 0; ++ ++void ++keyrelease(XEvent *e) { ++ combo = 0; ++} ++ ++void ++combotag(const Arg *arg) { ++ if(selmon->sel && arg->ui & TAGMASK) { ++ if (combo) { ++ selmon->sel->tags |= arg->ui & TAGMASK; ++ } else { ++ combo = 1; ++ selmon->sel->tags = arg->ui & TAGMASK; ++ } ++ focus(NULL); ++ arrange(selmon); ++ } ++} ++ ++void ++comboview(const Arg *arg) { ++ unsigned newtags = arg->ui & TAGMASK; ++ if (combo) { ++ selmon->tagset[selmon->seltags] |= newtags; ++ } else { ++ selmon->seltags ^= 1; /*toggle tagset*/ ++ combo = 1; ++ if (newtags) ++ selmon->tagset[selmon->seltags] = newtags; ++ } ++ focus(NULL); ++ arrange(selmon); ++} ++ + void + applyrules(Client *c) + { diff --git a/patches/dwm-cyclelayouts-20180524-6.2.diff b/patches/dwm-cyclelayouts-20180524-6.2.diff new file mode 100644 index 000000000..8079028bd --- /dev/null +++ b/patches/dwm-cyclelayouts-20180524-6.2.diff @@ -0,0 +1,93 @@ +From a09e766a4342f580582082a92b2de65f33208eb4 Mon Sep 17 00:00:00 2001 +From: Christopher Drelich +Date: Thu, 24 May 2018 00:56:56 -0400 +Subject: [PATCH] Function to cycle through available layouts. + +MOD-CTRL-, and MOD-CTRL-. +cycle backwards and forwards through available layouts. +Probably only useful if you have a lot of additional layouts. +The NULL, NULL layout should always be the last layout in your list, +in order to guarantee consistent behavior. +--- + config.def.h | 3 +++ + dwm.1 | 6 ++++++ + dwm.c | 18 ++++++++++++++++++ + 3 files changed, 27 insertions(+) + +diff --git a/config.def.h b/config.def.h +index a9ac303..153b880 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -41,6 +41,7 @@ static const Layout layouts[] = { + { "[]=", tile }, /* first entry is default */ + { "><>", NULL }, /* no layout function means floating behavior */ + { "[M]", monocle }, ++ { NULL, NULL }, + }; + + /* key definitions */ +@@ -76,6 +77,8 @@ static Key keys[] = { + { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, + { MODKEY, XK_f, setlayout, {.v = &layouts[1]} }, + { MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, ++ { MODKEY|ControlMask, XK_comma, cyclelayout, {.i = -1 } }, ++ { MODKEY|ControlMask, XK_period, cyclelayout, {.i = +1 } }, + { MODKEY, XK_space, setlayout, {0} }, + { MODKEY|ShiftMask, XK_space, togglefloating, {0} }, + { MODKEY, XK_0, view, {.ui = ~0 } }, +diff --git a/dwm.1 b/dwm.1 +index 13b3729..165891b 100644 +--- a/dwm.1 ++++ b/dwm.1 +@@ -92,6 +92,12 @@ Sets monocle layout. + .B Mod1\-space + Toggles between current and previous layout. + .TP ++.B Mod1\-Control\-, ++Cycles backwards in layout list. ++.TP ++.B Mod1\-Control\-. ++Cycles forwards in layout list. ++.TP + .B Mod1\-j + Focus next window. + .TP +diff --git a/dwm.c b/dwm.c +index bb95e26..db73000 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -157,6 +157,7 @@ static void configure(Client *c); + static void configurenotify(XEvent *e); + static void configurerequest(XEvent *e); + static Monitor *createmon(void); ++static void cyclelayout(const Arg *arg); + static void destroynotify(XEvent *e); + static void detach(Client *c); + static void detachstack(Client *c); +@@ -645,6 +646,23 @@ createmon(void) + } + + void ++cyclelayout(const Arg *arg) { ++ Layout *l; ++ for(l = (Layout *)layouts; l != selmon->lt[selmon->sellt]; l++); ++ if(arg->i > 0) { ++ if(l->symbol && (l + 1)->symbol) ++ setlayout(&((Arg) { .v = (l + 1) })); ++ else ++ setlayout(&((Arg) { .v = layouts })); ++ } else { ++ if(l != layouts && (l - 1)->symbol) ++ setlayout(&((Arg) { .v = (l - 1) })); ++ else ++ setlayout(&((Arg) { .v = &layouts[LENGTH(layouts) - 2] })); ++ } ++} ++ ++void + destroynotify(XEvent *e) + { + Client *c; +-- +2.7.4 + diff --git a/patches/dwm-fibonacci-20200418-c82db69.diff b/patches/dwm-fibonacci-20200418-c82db69.diff new file mode 100644 index 000000000..81bba7a4d --- /dev/null +++ b/patches/dwm-fibonacci-20200418-c82db69.diff @@ -0,0 +1,114 @@ +From ec9f55b6005cfa3b025b3d700c61af3ce539d057 Mon Sep 17 00:00:00 2001 +From: Niki Yoshiuchi +Date: Sat, 18 Apr 2020 09:55:26 -0700 +Subject: [PATCH] Adding the fibonacci layout patch + +--- + config.def.h | 5 ++++ + fibonacci.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 71 insertions(+) + create mode 100644 fibonacci.c + +diff --git a/config.def.h b/config.def.h +index 1c0b587..5708487 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -36,11 +36,14 @@ static const float mfact = 0.55; /* factor of master area size [0.05..0.95] + static const int nmaster = 1; /* number of clients in master area */ + static const int resizehints = 1; /* 1 means respect size hints in tiled resizals */ + ++#include "fibonacci.c" + static const Layout layouts[] = { + /* symbol arrange function */ + { "[]=", tile }, /* first entry is default */ + { "><>", NULL }, /* no layout function means floating behavior */ + { "[M]", monocle }, ++ { "[@]", spiral }, ++ { "[\\]", dwindle }, + }; + + /* key definitions */ +@@ -76,6 +79,8 @@ static Key keys[] = { + { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, + { MODKEY, XK_f, setlayout, {.v = &layouts[1]} }, + { MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, ++ { MODKEY, XK_r, setlayout, {.v = &layouts[3]} }, ++ { MODKEY|ShiftMask, XK_r, setlayout, {.v = &layouts[4]} }, + { MODKEY, XK_space, setlayout, {0} }, + { MODKEY|ShiftMask, XK_space, togglefloating, {0} }, + { MODKEY, XK_0, view, {.ui = ~0 } }, +diff --git a/fibonacci.c b/fibonacci.c +new file mode 100644 +index 0000000..fce0a57 +--- /dev/null ++++ b/fibonacci.c +@@ -0,0 +1,66 @@ ++void ++fibonacci(Monitor *mon, int s) { ++ unsigned int i, n, nx, ny, nw, nh; ++ Client *c; ++ ++ for(n = 0, c = nexttiled(mon->clients); c; c = nexttiled(c->next), n++); ++ if(n == 0) ++ return; ++ ++ nx = mon->wx; ++ ny = 0; ++ nw = mon->ww; ++ nh = mon->wh; ++ ++ for(i = 0, c = nexttiled(mon->clients); c; c = nexttiled(c->next)) { ++ if((i % 2 && nh / 2 > 2 * c->bw) ++ || (!(i % 2) && nw / 2 > 2 * c->bw)) { ++ if(i < n - 1) { ++ if(i % 2) ++ nh /= 2; ++ else ++ nw /= 2; ++ if((i % 4) == 2 && !s) ++ nx += nw; ++ else if((i % 4) == 3 && !s) ++ ny += nh; ++ } ++ if((i % 4) == 0) { ++ if(s) ++ ny += nh; ++ else ++ ny -= nh; ++ } ++ else if((i % 4) == 1) ++ nx += nw; ++ else if((i % 4) == 2) ++ ny += nh; ++ else if((i % 4) == 3) { ++ if(s) ++ nx += nw; ++ else ++ nx -= nw; ++ } ++ if(i == 0) ++ { ++ if(n != 1) ++ nw = mon->ww * mon->mfact; ++ ny = mon->wy; ++ } ++ else if(i == 1) ++ nw = mon->ww - nw; ++ i++; ++ } ++ resize(c, nx, ny, nw - 2 * c->bw, nh - 2 * c->bw, False); ++ } ++} ++ ++void ++dwindle(Monitor *mon) { ++ fibonacci(mon, 1); ++} ++ ++void ++spiral(Monitor *mon) { ++ fibonacci(mon, 0); ++} +-- +2.20.1 + diff --git a/patches/dwm-focusmaster-return-6.2.diff b/patches/dwm-focusmaster-return-6.2.diff new file mode 100644 index 000000000..975af081f --- /dev/null +++ b/patches/dwm-focusmaster-return-6.2.diff @@ -0,0 +1,90 @@ +From 8f662e7a556f94bda83ec724fb036e15b2badaac Mon Sep 17 00:00:00 2001 +From: Jack Bird +Date: Fri, 27 Aug 2021 01:14:44 +0100 +Subject: [PATCH] 6.2 focusmaster return + +--- + dwm.c | 39 +++++++++++++++++++++++++++++++++++++++ + 1 file changed, 39 insertions(+) + +diff --git a/dwm.c b/dwm.c +index 4465af1..5219cbd 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -127,6 +127,7 @@ struct Monitor { + Client *clients; + Client *sel; + Client *stack; ++ Client *tagmarked[32]; + Monitor *next; + Window barwin; + const Layout *lt[2]; +@@ -167,6 +168,7 @@ static void enternotify(XEvent *e); + static void expose(XEvent *e); + static void focus(Client *c); + static void focusin(XEvent *e); ++static void focusmaster(const Arg *arg); + static void focusmon(const Arg *arg); + static void focusstack(const Arg *arg); + static int getrootptr(int *x, int *y); +@@ -659,6 +661,10 @@ detach(Client *c) + { + Client **tc; + ++ for (int i = 1; i < LENGTH(tags); i++) ++ if (c == c->mon->tagmarked[i]) ++ c->mon->tagmarked[i] = NULL; ++ + for (tc = &c->mon->clients; *tc && *tc != c; tc = &(*tc)->next); + *tc = c->next; + } +@@ -815,6 +821,34 @@ focusin(XEvent *e) + setfocus(selmon->sel); + } + ++void ++focusmaster(const Arg *arg) ++{ ++ Client *master; ++ ++ if (selmon->nmaster > 1) ++ return; ++ if (!selmon->sel || (selmon->sel->isfullscreen && lockfullscreen)) ++ return; ++ ++ master = nexttiled(selmon->clients); ++ ++ if (!master) ++ return; ++ ++ int i; ++ for (i = 0; !(selmon->tagset[selmon->seltags] & 1 << i); i++); ++ i++; ++ ++ if (selmon->sel == master) { ++ if (selmon->tagmarked[i] && ISVISIBLE(selmon->tagmarked[i])) ++ focus(selmon->tagmarked[i]); ++ } else { ++ selmon->tagmarked[i] = selmon->sel; ++ focus(master); ++ } ++} ++ + void + focusmon(const Arg *arg) + { +@@ -1202,6 +1236,11 @@ nexttiled(Client *c) + void + pop(Client *c) + { ++ int i; ++ for (i = 0; !(selmon->tagset[selmon->seltags] & 1 << i); i++); ++ i++; ++ ++ c->mon->tagmarked[i] = nexttiled(c->mon->clients); + detach(c); + attach(c); + focus(c); +-- +2.33.0 + diff --git a/patches/dwm-fullscreen-6.2.diff b/patches/dwm-fullscreen-6.2.diff new file mode 100644 index 000000000..36e314055 --- /dev/null +++ b/patches/dwm-fullscreen-6.2.diff @@ -0,0 +1,56 @@ +From 54719285bd1a984e2efce6e8a8eab184fec11abf Mon Sep 17 00:00:00 2001 +From: Sermak +Date: Mon, 8 Jul 2019 01:06:44 +0200 +Subject: [PATCH] Simulate toggleable fullscreen mode + +--- + config.def.h | 1 + + dwm.c | 14 ++++++++++++++ + 2 files changed, 15 insertions(+) + +diff --git a/config.def.h b/config.def.h +index 1c0b587..f774cc5 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -76,6 +76,7 @@ static Key keys[] = { + { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, + { MODKEY, XK_f, setlayout, {.v = &layouts[1]} }, + { MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, ++ { MODKEY|ShiftMask, XK_f, fullscreen, {0} }, + { MODKEY, XK_space, setlayout, {0} }, + { MODKEY|ShiftMask, XK_space, togglefloating, {0} }, + { MODKEY, XK_0, view, {.ui = ~0 } }, +diff --git a/dwm.c b/dwm.c +index 4465af1..04b1e06 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -199,6 +199,7 @@ 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 fullscreen(const Arg *arg); + static void setlayout(const Arg *arg); + static void setmfact(const Arg *arg); + static void setup(void); +@@ -1497,6 +1498,19 @@ setfullscreen(Client *c, int fullscreen) + } + } + ++Layout *last_layout; ++void ++fullscreen(const Arg *arg) ++{ ++ if (selmon->showbar) { ++ for(last_layout = (Layout *)layouts; last_layout != selmon->lt[selmon->sellt]; last_layout++); ++ setlayout(&((Arg) { .v = &layouts[2] })); ++ } else { ++ setlayout(&((Arg) { .v = last_layout })); ++ } ++ togglebar(arg); ++} ++ + void + setlayout(const Arg *arg) + { +-- +2.22.0 diff --git a/patches/dwm-gridmode-20170909-ceac8c9.diff b/patches/dwm-gridmode-20170909-ceac8c9.diff new file mode 100644 index 000000000..feec67b03 --- /dev/null +++ b/patches/dwm-gridmode-20170909-ceac8c9.diff @@ -0,0 +1,73 @@ +From b04bb473cf9818277d33a591f7fe2dfae96afaaf Mon Sep 17 00:00:00 2001 +From: Joshua Haase +Date: Mon, 15 Aug 2016 17:06:18 -0500 +Subject: [PATCH] Apply modified gridmode patch. + +--- + config.def.h | 3 +++ + layouts.c | 27 +++++++++++++++++++++++++++ + 2 files changed, 30 insertions(+) + create mode 100644 layouts.c + +diff --git a/config.def.h b/config.def.h +index a9ac303..30b7c4a 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -36,11 +36,13 @@ static const float mfact = 0.55; /* factor of master area size [0.05..0.95] + static const int nmaster = 1; /* number of clients in master area */ + static const int resizehints = 1; /* 1 means respect size hints in tiled resizals */ + ++#include "layouts.c" + static const Layout layouts[] = { + /* symbol arrange function */ + { "[]=", tile }, /* first entry is default */ + { "><>", NULL }, /* no layout function means floating behavior */ + { "[M]", monocle }, ++ { "HHH", grid }, + }; + + /* key definitions */ +@@ -76,6 +78,7 @@ static Key keys[] = { + { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, + { MODKEY, XK_f, setlayout, {.v = &layouts[1]} }, + { MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, ++ { MODKEY, XK_g, setlayout, {.v = &layouts[3]} }, + { MODKEY, XK_space, setlayout, {0} }, + { MODKEY|ShiftMask, XK_space, togglefloating, {0} }, + { MODKEY, XK_0, view, {.ui = ~0 } }, +diff --git a/layouts.c b/layouts.c +new file mode 100644 +index 0000000..d26acf3 +--- /dev/null ++++ b/layouts.c +@@ -0,0 +1,27 @@ ++void ++grid(Monitor *m) { ++ unsigned int i, n, cx, cy, cw, ch, aw, ah, cols, rows; ++ Client *c; ++ ++ for(n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next)) ++ n++; ++ ++ /* grid dimensions */ ++ for(rows = 0; rows <= n/2; rows++) ++ if(rows*rows >= n) ++ break; ++ cols = (rows && (rows - 1) * rows >= n) ? rows - 1 : rows; ++ ++ /* window geoms (cell height/width) */ ++ ch = m->wh / (rows ? rows : 1); ++ cw = m->ww / (cols ? cols : 1); ++ for(i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next)) { ++ cx = m->wx + (i / rows) * cw; ++ cy = m->wy + (i % rows) * ch; ++ /* adjust height/width of last row/column's windows */ ++ ah = ((i + 1) % rows == 0) ? m->wh - ch * rows : 0; ++ aw = (i >= rows * (cols - 1)) ? m->ww - cw * cols : 0; ++ resize(c, cx, cy, cw - 2 * c->bw + aw, ch - 2 * c->bw + ah, False); ++ i++; ++ } ++} +-- +2.14.1 + diff --git a/patches/dwm-keychord-6.2.diff b/patches/dwm-keychord-6.2.diff new file mode 100644 index 000000000..98ac4a8fa --- /dev/null +++ b/patches/dwm-keychord-6.2.diff @@ -0,0 +1,214 @@ +From af959703381f2c216624eff7795f59156b05c2a0 Mon Sep 17 00:00:00 2001 +From: Hai Nguyen +Date: Wed, 19 Jan 2022 04:38:20 -0500 +Subject: [PATCH] implement keychord using array and pointer instead of heap + allocation + +--- + config.def.h | 63 +++++++++++++++++++++++---------------------- + dwm.c | 72 ++++++++++++++++++++++++++++++++++++++++------------ + 2 files changed, 88 insertions(+), 47 deletions(-) + +diff --git a/config.def.h b/config.def.h +index a2ac963..7cc8ddd 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -46,11 +46,11 @@ static const Layout layouts[] = { + + /* 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 TAGKEYS(KEY,TAG) \ ++ &((Keychord){1, {{MODKEY, KEY}}, view, {.ui = 1 << TAG} }), \ ++ &((Keychord){1, {{MODKEY|ControlMask, KEY}}, toggleview, {.ui = 1 << TAG} }), \ ++ &((Keychord){1, {{MODKEY|ShiftMask, KEY}}, tag, {.ui = 1 << TAG} }), \ ++ &((Keychord){1, {{MODKEY|ControlMask|ShiftMask, KEY}}, toggletag, {.ui = 1 << TAG} }), + + /* helper for spawning shell commands in the pre dwm-5.0 fashion */ + #define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } } +@@ -60,31 +60,32 @@ static char dmenumon[2] = "0"; /* component of dmenucmd, manipulated in spawn() + static const char *dmenucmd[] = { "dmenu_run", "-m", dmenumon, "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL }; + static const char *termcmd[] = { "st", NULL }; + +-static Key keys[] = { +- /* modifier key function argument */ +- { MODKEY, XK_p, spawn, {.v = dmenucmd } }, +- { MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } }, +- { MODKEY, XK_b, togglebar, {0} }, +- { MODKEY, XK_j, focusstack, {.i = +1 } }, +- { MODKEY, XK_k, focusstack, {.i = -1 } }, +- { MODKEY, XK_i, incnmaster, {.i = +1 } }, +- { MODKEY, XK_d, incnmaster, {.i = -1 } }, +- { MODKEY, XK_h, setmfact, {.f = -0.05} }, +- { MODKEY, XK_l, setmfact, {.f = +0.05} }, +- { MODKEY, XK_Return, zoom, {0} }, +- { MODKEY, XK_Tab, view, {0} }, +- { MODKEY|ShiftMask, XK_c, killclient, {0} }, +- { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, +- { MODKEY, XK_f, setlayout, {.v = &layouts[1]} }, +- { MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, +- { MODKEY, XK_space, setlayout, {0} }, +- { MODKEY|ShiftMask, XK_space, togglefloating, {0} }, +- { MODKEY, XK_0, view, {.ui = ~0 } }, +- { MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } }, +- { MODKEY, XK_comma, focusmon, {.i = -1 } }, +- { MODKEY, XK_period, focusmon, {.i = +1 } }, +- { MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } }, +- { MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } }, ++static Keychord *keychords[] = { ++ /* Keys function argument */ ++ &((Keychord){1, {{MODKEY, XK_p}}, spawn, {.v = dmenucmd } }), ++ &((Keychord){1, {{MODKEY|ShiftMask, XK_Return}}, spawn, {.v = termcmd } }), ++ &((Keychord){2, {{MODKEY, XK_e}, {MODKEY, XK_e}}, spawn, {.v = termcmd } }), ++ &((Keychord){1, {{MODKEY, XK_b}}, togglebar, {0} }), ++ &((Keychord){1, {{MODKEY, XK_j}}, focusstack, {.i = +1 } }), ++ &((Keychord){1, {{MODKEY, XK_k}}, focusstack, {.i = -1 } }), ++ &((Keychord){1, {{MODKEY, XK_i}}, incnmaster, {.i = +1 } }), ++ &((Keychord){1, {{MODKEY, XK_d}}, incnmaster, {.i = -1 } }), ++ &((Keychord){1, {{MODKEY, XK_h}}, setmfact, {.f = -0.05} }), ++ &((Keychord){1, {{MODKEY, XK_l}}, setmfact, {.f = +0.05} }), ++ &((Keychord){1, {{MODKEY, XK_Return}}, zoom, {0} }), ++ &((Keychord){1, {{MODKEY, XK_Tab}}, view, {0} }), ++ &((Keychord){1, {{MODKEY|ShiftMask, XK_c}}, killclient, {0} }), ++ &((Keychord){1, {{MODKEY, XK_t}}, setlayout, {.v = &layouts[0]} }), ++ &((Keychord){1, {{MODKEY, XK_f}}, setlayout, {.v = &layouts[1]} }), ++ &((Keychord){1, {{MODKEY, XK_m}}, setlayout, {.v = &layouts[2]} }), ++ &((Keychord){1, {{MODKEY, XK_space}}, setlayout, {0} }), ++ &((Keychord){1, {{MODKEY|ShiftMask, XK_space}}, togglefloating, {0} }), ++ &((Keychord){1, {{MODKEY, XK_0}}, view, {.ui = ~0 } }), ++ &((Keychord){1, {{MODKEY|ShiftMask, XK_0}}, tag, {.ui = ~0 } }), ++ &((Keychord){1, {{MODKEY, XK_comma}}, focusmon, {.i = -1 } }), ++ &((Keychord){1, {{MODKEY, XK_period}}, focusmon, {.i = +1 } }), ++ &((Keychord){1, {{MODKEY|ShiftMask, XK_comma}}, tagmon, {.i = -1 } }), ++ &((Keychord){1, {{MODKEY|ShiftMask, XK_period}}, tagmon, {.i = +1 } }), + TAGKEYS( XK_1, 0) + TAGKEYS( XK_2, 1) + TAGKEYS( XK_3, 2) +@@ -94,7 +95,7 @@ static Key keys[] = { + TAGKEYS( XK_7, 6) + TAGKEYS( XK_8, 7) + TAGKEYS( XK_9, 8) +- { MODKEY|ShiftMask, XK_q, quit, {0} }, ++ &((Keychord){1, {{MODKEY|ShiftMask, XK_q}}, quit, {0} }), + }; + + /* button definitions */ +diff --git a/dwm.c b/dwm.c +index a96f33c..f9777bd 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -102,9 +102,14 @@ struct Client { + typedef struct { + unsigned int mod; + KeySym keysym; ++} Key; ++ ++typedef struct { ++ unsigned int n; ++ const Key keys[5]; + void (*func)(const Arg *); + const Arg arg; +-} Key; ++} Keychord; + + typedef struct { + const char *symbol; +@@ -268,6 +273,7 @@ static Display *dpy; + static Drw *drw; + static Monitor *mons, *selmon; + static Window root, wmcheckwin; ++unsigned int currentkey = 0; + + /* configuration, allows nested code to access above variables */ + #include "config.h" +@@ -954,16 +960,17 @@ grabkeys(void) + { + updatenumlockmask(); + { +- unsigned int i, j; ++ unsigned int i, k; + unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; + KeyCode code; +- + XUngrabKey(dpy, AnyKey, AnyModifier, root); +- for (i = 0; i < LENGTH(keys); i++) +- if ((code = XKeysymToKeycode(dpy, keys[i].keysym))) +- for (j = 0; j < LENGTH(modifiers); j++) +- XGrabKey(dpy, code, keys[i].mod | modifiers[j], root, +- True, GrabModeAsync, GrabModeAsync); ++ for (i = 0; i < LENGTH(keychords); i++) ++ if ((code = XKeysymToKeycode(dpy, keychords[i]->keys[currentkey].keysym))) ++ for (k = 0; k < LENGTH(modifiers); k++) ++ XGrabKey(dpy, code, keychords[i]->keys[currentkey].mod | modifiers[k], root, ++ True, GrabModeAsync, GrabModeAsync); ++ if(currentkey > 0) ++ XGrabKey(dpy, XKeysymToKeycode(dpy, XK_Escape), AnyModifier, root, True, GrabModeAsync, GrabModeAsync); + } + } + +@@ -989,17 +996,50 @@ isuniquegeom(XineramaScreenInfo *unique, size_t n, XineramaScreenInfo *info) + void + keypress(XEvent *e) + { +- unsigned int i; ++ XEvent event = *e; ++ unsigned int ran = 0; + KeySym keysym; + XKeyEvent *ev; + +- ev = &e->xkey; +- keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0); +- for (i = 0; i < LENGTH(keys); i++) +- if (keysym == keys[i].keysym +- && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state) +- && keys[i].func) +- keys[i].func(&(keys[i].arg)); ++ Keychord *arr1[sizeof(keychords) / sizeof(Keychord*)]; ++ Keychord *arr2[sizeof(keychords) / sizeof(Keychord*)]; ++ memcpy(arr1, keychords, sizeof(keychords)); ++ Keychord **rpointer = arr1; ++ Keychord **wpointer = arr2; ++ ++ size_t r = sizeof(keychords)/ sizeof(Keychord*); ++ ++ while(1){ ++ ev = &event.xkey; ++ keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0); ++ size_t w = 0; ++ for (int i = 0; i < r; i++){ ++ if(keysym == (*(rpointer + i))->keys[currentkey].keysym ++ && CLEANMASK((*(rpointer + i))->keys[currentkey].mod) == CLEANMASK(ev->state) ++ && (*(rpointer + i))->func){ ++ if((*(rpointer + i))->n == currentkey +1){ ++ (*(rpointer + i))->func(&((*(rpointer + i))->arg)); ++ ran = 1; ++ }else{ ++ *(wpointer + w) = *(rpointer + i); ++ w++; ++ } ++ } ++ } ++ currentkey++; ++ if(w == 0 || ran == 1) ++ break; ++ grabkeys(); ++ while (running && !XNextEvent(dpy, &event) && !ran) ++ if(event.type == KeyPress) ++ break; ++ r = w; ++ Keychord **holder = rpointer; ++ rpointer = wpointer; ++ wpointer = holder; ++ } ++ currentkey = 0; ++ grabkeys(); + } + + void +-- +2.34.1 + diff --git a/patches/dwm-movestack-20211115-a786211.diff b/patches/dwm-movestack-20211115-a786211.diff new file mode 100644 index 000000000..134abb88b --- /dev/null +++ b/patches/dwm-movestack-20211115-a786211.diff @@ -0,0 +1,95 @@ +From 9a4037dc0ef56f91c009317e78e9e3790dafbb58 Mon Sep 17 00:00:00 2001 +From: BrunoCooper17 +Date: Mon, 15 Nov 2021 14:04:53 -0600 +Subject: [PATCH] MoveStack patch + +This plugin allows you to move clients around in the stack and swap them +with the master. It emulates the behavior off mod+shift+j and mod+shift+k +in Xmonad. movestack(+1) will swap the client with the current focus with +the next client. movestack(-1) will swap the client with the current focus +with the previous client. +--- + config.def.h | 3 +++ + movestack.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 51 insertions(+) + create mode 100644 movestack.c + +diff --git a/config.def.h b/config.def.h +index a2ac963..33efa5b 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -60,6 +60,7 @@ static char dmenumon[2] = "0"; /* component of dmenucmd, manipulated in spawn() + static const char *dmenucmd[] = { "dmenu_run", "-m", dmenumon, "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL }; + static const char *termcmd[] = { "st", NULL }; + ++#include "movestack.c" + static Key keys[] = { + /* modifier key function argument */ + { MODKEY, XK_p, spawn, {.v = dmenucmd } }, +@@ -71,6 +72,8 @@ static Key keys[] = { + { MODKEY, XK_d, incnmaster, {.i = -1 } }, + { MODKEY, XK_h, setmfact, {.f = -0.05} }, + { MODKEY, XK_l, setmfact, {.f = +0.05} }, ++ { MODKEY|ShiftMask, XK_j, movestack, {.i = +1 } }, ++ { MODKEY|ShiftMask, XK_k, movestack, {.i = -1 } }, + { MODKEY, XK_Return, zoom, {0} }, + { MODKEY, XK_Tab, view, {0} }, + { MODKEY|ShiftMask, XK_c, killclient, {0} }, +diff --git a/movestack.c b/movestack.c +new file mode 100644 +index 0000000..520f4ae +--- /dev/null ++++ b/movestack.c +@@ -0,0 +1,48 @@ ++void ++movestack(const Arg *arg) { ++ Client *c = NULL, *p = NULL, *pc = NULL, *i; ++ ++ if(arg->i > 0) { ++ /* find the client after selmon->sel */ ++ for(c = selmon->sel->next; c && (!ISVISIBLE(c) || c->isfloating); c = c->next); ++ if(!c) ++ for(c = selmon->clients; c && (!ISVISIBLE(c) || c->isfloating); c = c->next); ++ ++ } ++ else { ++ /* find the client before selmon->sel */ ++ for(i = selmon->clients; i != selmon->sel; i = i->next) ++ if(ISVISIBLE(i) && !i->isfloating) ++ c = i; ++ if(!c) ++ for(; i; i = i->next) ++ if(ISVISIBLE(i) && !i->isfloating) ++ c = i; ++ } ++ /* find the client before selmon->sel and c */ ++ for(i = selmon->clients; i && (!p || !pc); i = i->next) { ++ if(i->next == selmon->sel) ++ p = i; ++ if(i->next == c) ++ pc = i; ++ } ++ ++ /* swap c and selmon->sel selmon->clients in the selmon->clients list */ ++ if(c && c != selmon->sel) { ++ Client *temp = selmon->sel->next==c?selmon->sel:selmon->sel->next; ++ selmon->sel->next = c->next==selmon->sel?c:c->next; ++ c->next = temp; ++ ++ if(p && p != c) ++ p->next = c; ++ if(pc && pc != selmon->sel) ++ pc->next = selmon->sel; ++ ++ if(selmon->sel == selmon->clients) ++ selmon->clients = c; ++ else if(c == selmon->clients) ++ selmon->clients = selmon->sel; ++ ++ arrange(selmon); ++ } ++} +\ No newline at end of file +-- +2.33.1 + diff --git a/patches/dwm-pertag-20200914-61bb8b2.diff b/patches/dwm-pertag-20200914-61bb8b2.diff new file mode 100644 index 000000000..c8d7fbcb3 --- /dev/null +++ b/patches/dwm-pertag-20200914-61bb8b2.diff @@ -0,0 +1,177 @@ +diff --git a/dwm.c b/dwm.c +index 664c527..ac8e4ec 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -111,6 +111,7 @@ typedef struct { + void (*arrange)(Monitor *); + } Layout; + ++typedef struct Pertag Pertag; + struct Monitor { + char ltsymbol[16]; + float mfact; +@@ -130,6 +131,7 @@ struct Monitor { + Monitor *next; + Window barwin; + const Layout *lt[2]; ++ Pertag *pertag; + }; + + typedef struct { +@@ -272,6 +274,15 @@ static Window root, wmcheckwin; + /* configuration, allows nested code to access above variables */ + #include "config.h" + ++struct Pertag { ++ unsigned int curtag, prevtag; /* current and previous tag */ ++ int nmasters[LENGTH(tags) + 1]; /* number of windows in master area */ ++ float mfacts[LENGTH(tags) + 1]; /* mfacts per tag */ ++ unsigned int sellts[LENGTH(tags) + 1]; /* selected layouts */ ++ const Layout *ltidxs[LENGTH(tags) + 1][2]; /* matrix of tags and layouts indexes */ ++ int showbars[LENGTH(tags) + 1]; /* display bar for the current tag */ ++}; ++ + /* compile-time check if all tags fit into an unsigned int bit array. */ + struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; + +@@ -632,6 +643,7 @@ Monitor * + createmon(void) + { + Monitor *m; ++ unsigned int i; + + m = ecalloc(1, sizeof(Monitor)); + m->tagset[0] = m->tagset[1] = 1; +@@ -642,6 +654,20 @@ createmon(void) + m->lt[0] = &layouts[0]; + m->lt[1] = &layouts[1 % LENGTH(layouts)]; + strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); ++ m->pertag = ecalloc(1, sizeof(Pertag)); ++ m->pertag->curtag = m->pertag->prevtag = 1; ++ ++ for (i = 0; i <= LENGTH(tags); i++) { ++ m->pertag->nmasters[i] = m->nmaster; ++ m->pertag->mfacts[i] = m->mfact; ++ ++ m->pertag->ltidxs[i][0] = m->lt[0]; ++ m->pertag->ltidxs[i][1] = m->lt[1]; ++ m->pertag->sellts[i] = m->sellt; ++ ++ m->pertag->showbars[i] = m->showbar; ++ } ++ + return m; + } + +@@ -967,7 +993,7 @@ grabkeys(void) + void + incnmaster(const Arg *arg) + { +- selmon->nmaster = MAX(selmon->nmaster + arg->i, 0); ++ selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag] = MAX(selmon->nmaster + arg->i, 0); + arrange(selmon); + } + +@@ -1502,9 +1528,9 @@ void + setlayout(const Arg *arg) + { + if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) +- selmon->sellt ^= 1; ++ selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag] ^= 1; + if (arg && arg->v) +- selmon->lt[selmon->sellt] = (Layout *)arg->v; ++ selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt] = (Layout *)arg->v; + strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol); + if (selmon->sel) + arrange(selmon); +@@ -1523,7 +1549,7 @@ setmfact(const Arg *arg) + f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0; + if (f < 0.05 || f > 0.95) + return; +- selmon->mfact = f; ++ selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag] = f; + arrange(selmon); + } + +@@ -1702,7 +1728,7 @@ tile(Monitor *m) + void + togglebar(const Arg *arg) + { +- selmon->showbar = !selmon->showbar; ++ selmon->showbar = selmon->pertag->showbars[selmon->pertag->curtag] = !selmon->showbar; + updatebarpos(selmon); + XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); + arrange(selmon); +@@ -1741,9 +1767,33 @@ 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->prevtag = selmon->pertag->curtag; ++ selmon->pertag->curtag = 0; ++ } ++ ++ /* test if the user did not select the same tag */ ++ if (!(newtagset & 1 << (selmon->pertag->curtag - 1))) { ++ selmon->pertag->prevtag = selmon->pertag->curtag; ++ 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]; ++ ++ if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag]) ++ togglebar(NULL); ++ + focus(NULL); + arrange(selmon); + } +@@ -2038,11 +2088,37 @@ updatewmhints(Client *c) + void + view(const Arg *arg) + { ++ int i; ++ unsigned int tmptag; ++ + if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) + return; + selmon->seltags ^= 1; /* toggle sel tagset */ +- if (arg->ui & TAGMASK) ++ if (arg->ui & TAGMASK) { + selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; ++ selmon->pertag->prevtag = selmon->pertag->curtag; ++ ++ if (arg->ui == ~0) ++ selmon->pertag->curtag = 0; ++ else { ++ for (i = 0; !(arg->ui & 1 << i); i++) ; ++ selmon->pertag->curtag = i + 1; ++ } ++ } else { ++ tmptag = selmon->pertag->prevtag; ++ selmon->pertag->prevtag = selmon->pertag->curtag; ++ selmon->pertag->curtag = tmptag; ++ } ++ ++ 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]; ++ ++ if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag]) ++ togglebar(NULL); ++ + focus(NULL); + arrange(selmon); + } diff --git a/patches/dwm-restartsig-20180523-6.2.diff b/patches/dwm-restartsig-20180523-6.2.diff new file mode 100644 index 000000000..f1f86808e --- /dev/null +++ b/patches/dwm-restartsig-20180523-6.2.diff @@ -0,0 +1,139 @@ +From 2991f37f0aaf44b9f9b11e7893ff0af8eb88f649 Mon Sep 17 00:00:00 2001 +From: Christopher Drelich +Date: Wed, 23 May 2018 22:50:38 -0400 +Subject: [PATCH] Modifies quit to handle restarts and adds SIGHUP and SIGTERM + handlers. + +Modified quit() to restart if it receives arg .i = 1 +MOD+CTRL+SHIFT+Q was added to confid.def.h to do just that. + +Signal handlers were handled for SIGHUP and SIGTERM. +If dwm receives these signals it calls quit() with +arg .i = to 1 or 0, respectively. + +To restart dwm: +MOD+CTRL+SHIFT+Q +or +kill -HUP dwmpid + +To quit dwm cleanly: +MOD+SHIFT+Q +or +kill -TERM dwmpid +--- + config.def.h | 1 + + dwm.1 | 10 ++++++++++ + dwm.c | 22 ++++++++++++++++++++++ + 3 files changed, 33 insertions(+) + +diff --git a/config.def.h b/config.def.h +index a9ac303..e559429 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -94,6 +94,7 @@ static Key keys[] = { + TAGKEYS( XK_8, 7) + TAGKEYS( XK_9, 8) + { MODKEY|ShiftMask, XK_q, quit, {0} }, ++ { MODKEY|ControlMask|ShiftMask, XK_q, quit, {1} }, + }; + + /* button definitions */ +diff --git a/dwm.1 b/dwm.1 +index 13b3729..36a331c 100644 +--- a/dwm.1 ++++ b/dwm.1 +@@ -142,6 +142,9 @@ Add/remove all windows with nth tag to/from the view. + .TP + .B Mod1\-Shift\-q + Quit dwm. ++.TP ++.B Mod1\-Control\-Shift\-q ++Restart dwm. + .SS Mouse commands + .TP + .B Mod1\-Button1 +@@ -155,6 +158,13 @@ Resize focused window while dragging. Tiled windows will be toggled to the float + .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 SIGNALS ++.TP ++.B SIGHUP - 1 ++Restart the dwm process. ++.TP ++.B SIGTERM - 15 ++Cleanly terminate the dwm process. + .SH SEE ALSO + .BR dmenu (1), + .BR st (1) +diff --git a/dwm.c b/dwm.c +index bb95e26..286eecd 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -205,6 +205,8 @@ static void setup(void); + static void seturgent(Client *c, int urg); + static void showhide(Client *c); + static void sigchld(int unused); ++static void sighup(int unused); ++static void sigterm(int unused); + static void spawn(const Arg *arg); + static void tag(const Arg *arg); + static void tagmon(const Arg *arg); +@@ -260,6 +262,7 @@ static void (*handler[LASTEvent]) (XEvent *) = { + [UnmapNotify] = unmapnotify + }; + static Atom wmatom[WMLast], netatom[NetLast]; ++static int restart = 0; + static int running = 1; + static Cur *cursor[CurLast]; + static Clr **scheme; +@@ -1248,6 +1251,7 @@ propertynotify(XEvent *e) + void + quit(const Arg *arg) + { ++ if(arg->i) restart = 1; + running = 0; + } + +@@ -1536,6 +1540,9 @@ setup(void) + /* clean up any zombies immediately */ + sigchld(0); + ++ signal(SIGHUP, sighup); ++ signal(SIGTERM, sigterm); ++ + /* init screen */ + screen = DefaultScreen(dpy); + sw = DisplayWidth(dpy, screen); +@@ -1637,6 +1644,20 @@ sigchld(int unused) + } + + void ++sighup(int unused) ++{ ++ Arg a = {.i = 1}; ++ quit(&a); ++} ++ ++void ++sigterm(int unused) ++{ ++ Arg a = {.i = 0}; ++ quit(&a); ++} ++ ++void + spawn(const Arg *arg) + { + if (arg->v == dmenucmd) +@@ -2139,6 +2160,7 @@ main(int argc, char *argv[]) + setup(); + scan(); + run(); ++ if(restart) execvp(argv[0], argv); + cleanup(); + XCloseDisplay(dpy); + return EXIT_SUCCESS; +-- +2.7.4 + diff --git a/patches/dwm-rmaster-6.2.diff b/patches/dwm-rmaster-6.2.diff new file mode 100644 index 000000000..3a9088b31 --- /dev/null +++ b/patches/dwm-rmaster-6.2.diff @@ -0,0 +1,113 @@ +From de2bfe560a8085630ffe976fd5972ee1e8d03916 Mon Sep 17 00:00:00 2001 +From: pskry +Date: Mon, 16 Nov 2020 17:47:05 +0100 +Subject: [PATCH] Enable swapping master- and stack-area + +Enables swapping the master- and stack area such that the master-client +appears on the right and the stack-clients appear on the left. + +A variable and a toggle-function are introduced to achieve this +behaviour which are set in the config.h: + +* The rmaster-variable can be set to 1 to make the right area the +default master-area +* The togglemaster-function can be used to swap the master- and +stack-areas dynamically. +--- + config.def.h | 2 ++ + dwm.c | 23 ++++++++++++++++++++--- + 2 files changed, 22 insertions(+), 3 deletions(-) + +diff --git a/config.def.h b/config.def.h +index 1c0b587..1d00282 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -3,6 +3,7 @@ + /* appearance */ + static const unsigned int borderpx = 1; /* border pixel of windows */ + static const unsigned int snap = 32; /* snap pixel */ ++static const int rmaster = 1; /* 1 means master-area is initially on the right */ + static const int showbar = 1; /* 0 means no bar */ + static const int topbar = 1; /* 0 means bottom bar */ + static const char *fonts[] = { "monospace:size=10" }; +@@ -78,6 +79,7 @@ static Key keys[] = { + { MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, + { MODKEY, XK_space, setlayout, {0} }, + { MODKEY|ShiftMask, XK_space, togglefloating, {0} }, ++ { MODKEY, XK_r, togglermaster, {0} }, + { MODKEY, XK_0, view, {.ui = ~0 } }, + { MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } }, + { MODKEY, XK_comma, focusmon, {.i = -1 } }, +diff --git a/dwm.c b/dwm.c +index 4465af1..a2d118b 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -122,6 +122,7 @@ struct Monitor { + unsigned int seltags; + unsigned int sellt; + unsigned int tagset[2]; ++ int rmaster; + int showbar; + int topbar; + Client *clients; +@@ -211,6 +212,7 @@ static void tagmon(const Arg *arg); + static void tile(Monitor *); + static void togglebar(const Arg *arg); + static void togglefloating(const Arg *arg); ++static void togglermaster(const Arg *arg); + static void toggletag(const Arg *arg); + static void toggleview(const Arg *arg); + static void unfocus(Client *c, int setfocus); +@@ -636,6 +638,7 @@ createmon(void) + m->tagset[0] = m->tagset[1] = 1; + m->mfact = mfact; + m->nmaster = nmaster; ++ m->rmaster = rmaster; + m->showbar = showbar; + m->topbar = topbar; + m->lt[0] = &layouts[0]; +@@ -1681,17 +1684,21 @@ tile(Monitor *m) + return; + + if (n > m->nmaster) +- mw = m->nmaster ? m->ww * m->mfact : 0; ++ mw = m->nmaster ++ ? m->ww * (m->rmaster ? 1.0 - m->mfact : m->mfact) ++ : 0; + else + mw = m->ww; + for (i = my = ty = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) + if (i < m->nmaster) { + h = (m->wh - my) / (MIN(n, m->nmaster) - i); +- resize(c, m->wx, m->wy + my, mw - (2*c->bw), h - (2*c->bw), 0); ++ resize(c, m->rmaster ? m->wx + m->ww - mw : m->wx, ++ m->wy + my, mw - (2*c->bw), h - (2*c->bw), 0); + my += HEIGHT(c); + } else { + h = (m->wh - ty) / (n - i); +- resize(c, m->wx + mw, m->wy + ty, m->ww - mw - (2*c->bw), h - (2*c->bw), 0); ++ resize(c, m->rmaster ? m->wx : m->wx + mw, m->wy + ty, ++ m->ww - mw - (2*c->bw), h - (2*c->bw), 0); + ty += HEIGHT(c); + } + } +@@ -1719,6 +1726,16 @@ togglefloating(const Arg *arg) + arrange(selmon); + } + ++void ++togglermaster(const Arg *arg) ++{ ++ selmon->rmaster = !selmon->rmaster; ++ /* now mfact represents the left factor */ ++ selmon->mfact = 1.0 - selmon->mfact; ++ if (selmon->lt[selmon->sellt]->arrange) ++ arrange(selmon); ++} ++ + void + toggletag(const Arg *arg) + { +-- +2.31.1 + diff --git a/patches/dwm-scratchpads-20200414-728d397b.diff b/patches/dwm-scratchpads-20200414-728d397b.diff new file mode 100644 index 000000000..d3e90c01a --- /dev/null +++ b/patches/dwm-scratchpads-20200414-728d397b.diff @@ -0,0 +1,199 @@ +From 728d397b21982af88737277fd9d6939a7b558786 Mon Sep 17 00:00:00 2001 +From: Christian Tenllado +Date: Tue, 14 Apr 2020 23:31:15 +0200 +Subject: [PATCH] Multiple scratchpads + +This patch enables multiple scratchpads, each with one asigned window. +This enables the same scratchpad workflow that you have in i3. + +Scratchpads are implemented as special tags, whose mask does not +apply to new spawned windows. To assign a window to a scratchpad you +have to set up a rule, as you do with regular tags. + +Windows tagged with scratchpad tags can be set floating or not in the +rules array. Most users would probably want them floating (i3 style), +but having them tiled does also perfectly work and might fit better the +DWM approach. In case they are set floating, the patch moves them to the +center of the screen whenever they are shown. The patch can easily be +modified to make this last feature configurable in the rules array (see +the center patch). + +The togglescratch function, borrowed from the previous scratchpad patch +and slightly modified, can be used to spawn a registered scratchpad +process or toggle its view. This function looks for a window tagged with +the selected scratchpad tag. If it is found its view is toggled. If it is +not found the corresponding registered command is spawned. The +config.def.h shows three examples of its use to spawn a terminal in the +first scratchpad tag, a second terminal running ranger on the second +scratchpad tag and the keepassxc application to manage passwords on a +third scratchpad tag. + +If you prefer to spawn your scratchpad applications from the startup +script, you might opt for binding keys to toggleview instead, as +scratchpads are just special tags (you may even extend the TAGKEYS macro +to generalize the key bindings). +--- + config.def.h | 28 ++++++++++++++++++++++++---- + dwm.c | 43 +++++++++++++++++++++++++++++++++++++++++-- + 2 files changed, 65 insertions(+), 6 deletions(-) + +diff --git a/config.def.h b/config.def.h +index 1c0b587..06265e1 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -18,17 +18,33 @@ static const char *colors[][3] = { + [SchemeSel] = { col_gray4, col_cyan, col_cyan }, + }; + ++typedef struct { ++ const char *name; ++ const void *cmd; ++} Sp; ++const char *spcmd1[] = {"st", "-n", "spterm", "-g", "120x34", NULL }; ++const char *spcmd2[] = {"st", "-n", "spfm", "-g", "144x41", "-e", "ranger", NULL }; ++const char *spcmd3[] = {"keepassxc", NULL }; ++static Sp scratchpads[] = { ++ /* name cmd */ ++ {"spterm", spcmd1}, ++ {"spranger", spcmd2}, ++ {"keepassxc", spcmd3}, ++}; ++ + /* tagging */ + static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; +- + static const Rule rules[] = { + /* xprop(1): + * WM_CLASS(STRING) = instance, class + * WM_NAME(STRING) = title + */ + /* class instance title tags mask isfloating monitor */ +- { "Gimp", NULL, NULL, 0, 1, -1 }, +- { "Firefox", NULL, NULL, 1 << 8, 0, -1 }, ++ { "Gimp", NULL, NULL, 0, 1, -1 }, ++ { "Firefox", NULL, NULL, 1 << 8, 0, -1 }, ++ { NULL, "spterm", NULL, SPTAG(0), 1, -1 }, ++ { NULL, "spfm", NULL, SPTAG(1), 1, -1 }, ++ { NULL, "keepassxc", NULL, SPTAG(2), 0, -1 }, + }; + + /* layout(s) */ +@@ -59,6 +75,7 @@ static char dmenumon[2] = "0"; /* component of dmenucmd, manipulated in spawn() + static const char *dmenucmd[] = { "dmenu_run", "-m", dmenumon, "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL }; + static const char *termcmd[] = { "st", NULL }; + ++ + static Key keys[] = { + /* modifier key function argument */ + { MODKEY, XK_p, spawn, {.v = dmenucmd } }, +@@ -84,6 +101,9 @@ static Key keys[] = { + { MODKEY, XK_period, focusmon, {.i = +1 } }, + { MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } }, + { MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } }, ++ { MODKEY, XK_y, togglescratch, {.ui = 0 } }, ++ { MODKEY, XK_u, togglescratch, {.ui = 1 } }, ++ { MODKEY, XK_x, togglescratch, {.ui = 2 } }, + TAGKEYS( XK_1, 0) + TAGKEYS( XK_2, 1) + TAGKEYS( XK_3, 2) +@@ -106,7 +126,7 @@ static Button buttons[] = { + { ClkStatusText, 0, Button2, spawn, {.v = termcmd } }, + { ClkClientWin, MODKEY, Button1, movemouse, {0} }, + { ClkClientWin, MODKEY, Button2, togglefloating, {0} }, +- { ClkClientWin, MODKEY, Button3, resizemouse, {0} }, ++ { ClkClientWin, MODKEY, Button1, resizemouse, {0} }, + { ClkTagBar, 0, Button1, view, {0} }, + { ClkTagBar, 0, Button3, toggleview, {0} }, + { ClkTagBar, MODKEY, Button1, tag, {0} }, +diff --git a/dwm.c b/dwm.c +index 4465af1..646aa1a 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -54,7 +54,10 @@ + #define MOUSEMASK (BUTTONMASK|PointerMotionMask) + #define WIDTH(X) ((X)->w + 2 * (X)->bw) + #define HEIGHT(X) ((X)->h + 2 * (X)->bw) +-#define TAGMASK ((1 << LENGTH(tags)) - 1) ++#define NUMTAGS (LENGTH(tags) + LENGTH(scratchpads)) ++#define TAGMASK ((1 << NUMTAGS) - 1) ++#define SPTAG(i) ((1 << LENGTH(tags)) << (i)) ++#define SPTAGMASK (((1 << LENGTH(scratchpads))-1) << LENGTH(tags)) + #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) + + /* enums */ +@@ -211,6 +214,7 @@ static void tagmon(const Arg *arg); + static void tile(Monitor *); + static void togglebar(const Arg *arg); + static void togglefloating(const Arg *arg); ++static void togglescratch(const Arg *arg); + static void toggletag(const Arg *arg); + static void toggleview(const Arg *arg); + static void unfocus(Client *c, int setfocus); +@@ -299,6 +303,11 @@ applyrules(Client *c) + { + c->isfloating = r->isfloating; + c->tags |= r->tags; ++ if ((r->tags & SPTAGMASK) && r->isfloating) { ++ c->x = c->mon->wx + (c->mon->ww / 2 - WIDTH(c) / 2); ++ c->y = c->mon->wy + (c->mon->wh / 2 - HEIGHT(c) / 2); ++ } ++ + for (m = mons; m && m->num != r->monitor; m = m->next); + if (m) + c->mon = m; +@@ -308,7 +317,7 @@ applyrules(Client *c) + 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]; ++ c->tags = c->tags & TAGMASK ? c->tags & TAGMASK : (c->mon->tagset[c->mon->seltags] & ~SPTAGMASK); + } + + int +@@ -1616,6 +1625,10 @@ showhide(Client *c) + if (!c) + return; + if (ISVISIBLE(c)) { ++ if ((c->tags & SPTAGMASK) && c->isfloating) { ++ c->x = c->mon->wx + (c->mon->ww / 2 - WIDTH(c) / 2); ++ c->y = c->mon->wy + (c->mon->wh / 2 - HEIGHT(c) / 2); ++ } + /* show clients top down */ + XMoveWindow(dpy, c->win, c->x, c->y); + if ((!c->mon->lt[c->mon->sellt]->arrange || c->isfloating) && !c->isfullscreen) +@@ -1719,6 +1732,32 @@ togglefloating(const Arg *arg) + arrange(selmon); + } + ++void ++togglescratch(const Arg *arg) ++{ ++ Client *c; ++ unsigned int found = 0; ++ unsigned int scratchtag = SPTAG(arg->ui); ++ Arg sparg = {.v = scratchpads[arg->ui].cmd}; ++ ++ for (c = selmon->clients; c && !(found = c->tags & scratchtag); c = c->next); ++ if (found) { ++ unsigned int newtagset = selmon->tagset[selmon->seltags] ^ scratchtag; ++ if (newtagset) { ++ selmon->tagset[selmon->seltags] = newtagset; ++ focus(NULL); ++ arrange(selmon); ++ } ++ if (ISVISIBLE(c)) { ++ focus(c); ++ restack(selmon); ++ } ++ } else { ++ selmon->tagset[selmon->seltags] |= scratchtag; ++ spawn(&sparg); ++ } ++} ++ + void + toggletag(const Arg *arg) + { +-- +2.20.1 + diff --git a/patches/dwm-statuspadding-6.3.diff b/patches/dwm-statuspadding-6.3.diff new file mode 100644 index 000000000..fa6780f6f --- /dev/null +++ b/patches/dwm-statuspadding-6.3.diff @@ -0,0 +1,62 @@ +From d6dd69c26f4272f87672ae54f69dc0d48650d34b Mon Sep 17 00:00:00 2001 +From: taep96 <64481039+taep96@users.noreply.github.com> +Date: Mon, 7 Feb 2022 19:09:45 +0100 +Subject: [PATCH] Fixed | Replaces magic numbers in statusbar with configurable + variables. + +horizpadbar for horizontal statusbar padding +vertpadbar for vertical statusbar padding + +StatusText now has both left and right padding, +as well as the vertical padding that all of the statusbar shares. + +Other than the addition of left padding to StatusText, appearance +of the statusbar is identical to pre-patch when using the defaults +in config.def.h +--- + config.def.h | 2 ++ + dwm.c | 8 ++++---- + 2 files changed, 6 insertions(+), 4 deletions(-) + +diff --git a/config.def.h b/config.def.h +index a2ac963..6cb845c 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -5,6 +5,8 @@ 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 horizpadbar = 2; /* horizontal padding for statusbar */ ++static const int vertpadbar = 0; /* vertical padding for statusbar */ + static const char *fonts[] = { "monospace:size=10" }; + static const char dmenufont[] = "monospace:size=10"; + static const char col_gray1[] = "#222222"; +diff --git a/dwm.c b/dwm.c +index a96f33c..a1b8c95 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -708,8 +708,8 @@ drawbar(Monitor *m) + /* draw status first so it can be overdrawn by tags later */ + if (m == selmon) { /* status is only drawn on selected monitor */ + drw_setscheme(drw, scheme[SchemeNorm]); +- tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ +- drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0); ++ tw = TEXTW(stext); ++ drw_text(drw, m->ww - tw, 0, tw, bh, lrpad / 2, stext, 0); + } + + for (c = m->clients; c; c = c->next) { +@@ -1548,8 +1548,8 @@ setup(void) + drw = drw_create(dpy, screen, root, sw, sh); + if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) + die("no fonts could be loaded."); +- lrpad = drw->fonts->h; +- bh = drw->fonts->h + 2; ++ lrpad = drw->fonts->h + horizpadbar; ++ bh = drw->fonts->h + vertpadbar; + updategeom(); + /* init atoms */ + utf8string = XInternAtom(dpy, "UTF8_STRING", False); +-- +2.35.1 + diff --git a/patches/dwm-sticky-6.4.diff b/patches/dwm-sticky-6.4.diff new file mode 100644 index 000000000..2c26343c7 --- /dev/null +++ b/patches/dwm-sticky-6.4.diff @@ -0,0 +1,141 @@ +From d47ba0b8aab26ffb2569c0d05df24fdd61b3f4b0 Mon Sep 17 00:00:00 2001 +From: aymey +Date: Mon, 5 Dec 2022 22:57:41 +1100 +Subject: [PATCH] Sticky windows respect EWMH + +--- + dwm.c | 51 ++++++++++++++++++++++++++++++++++++++++++++------- + 1 file changed, 44 insertions(+), 7 deletions(-) + +diff --git a/dwm.c b/dwm.c +index 253aba7..a9157bf 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -49,7 +49,7 @@ + #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 ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags]) || C->issticky) + #define LENGTH(X) (sizeof X / sizeof X[0]) + #define MOUSEMASK (BUTTONMASK|PointerMotionMask) + #define WIDTH(X) ((X)->w + 2 * (X)->bw) +@@ -61,7 +61,7 @@ + enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ + enum { SchemeNorm, SchemeSel }; /* color schemes */ + enum { NetSupported, NetWMName, NetWMState, NetWMCheck, +- NetWMFullscreen, NetActiveWindow, NetWMWindowType, ++ NetWMFullscreen, NetWMSticky, NetActiveWindow, NetWMWindowType, + NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ + enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ + enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, +@@ -92,7 +92,7 @@ struct Client { + int basew, baseh, incw, inch, maxw, maxh, minw, minh, hintsvalid; + int bw, oldbw; + unsigned int tags; +- int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen; ++ int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen, issticky; + Client *next; + Client *snext; + Monitor *mon; +@@ -200,6 +200,7 @@ 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 setsticky(Client *c, int sticky); + static void setlayout(const Arg *arg); + static void setmfact(const Arg *arg); + static void setup(void); +@@ -212,6 +213,7 @@ static void tagmon(const Arg *arg); + static void tile(Monitor *m); + static void togglebar(const Arg *arg); + static void togglefloating(const Arg *arg); ++static void togglesticky(const Arg *arg); + static void toggletag(const Arg *arg); + static void toggleview(const Arg *arg); + static void unfocus(Client *c, int setfocus); +@@ -526,6 +528,10 @@ clientmessage(XEvent *e) + || 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))); ++ ++ if (cme->data.l[1] == netatom[NetWMSticky] ++ || cme->data.l[2] == netatom[NetWMSticky]) ++ setsticky(c, (cme->data.l[0] == 1 || (cme->data.l[0] == 2 && !c->issticky))); + } else if (cme->message_type == netatom[NetActiveWindow]) { + if (c != selmon->sel && !c->isurgent) + seturgent(c, 1); +@@ -1498,6 +1504,23 @@ setfullscreen(Client *c, int fullscreen) + } + } + ++void ++setsticky(Client *c, int sticky) ++{ ++ ++ if(sticky && !c->issticky) { ++ XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, ++ PropModeReplace, (unsigned char *) &netatom[NetWMSticky], 1); ++ c->issticky = 1; ++ } else if(!sticky && c->issticky){ ++ XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, ++ PropModeReplace, (unsigned char *)0, 0); ++ c->issticky = 0; ++ arrange(c->mon); ++ } ++} ++ ++ + void + setlayout(const Arg *arg) + { +@@ -1560,6 +1583,7 @@ setup(void) + 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[NetWMSticky] = XInternAtom(dpy, "_NET_WM_STATE_STICKY", False); + netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); + netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); + netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False); +@@ -1719,6 +1743,15 @@ togglefloating(const Arg *arg) + arrange(selmon); + } + ++void ++togglesticky(const Arg *arg) ++{ ++ if (!selmon->sel) ++ return; ++ setsticky(selmon->sel, !selmon->sel->issticky); ++ arrange(selmon); ++} ++ + void + toggletag(const Arg *arg) + { +@@ -2009,10 +2042,13 @@ updatewindowtype(Client *c) + Atom state = getatomprop(c, netatom[NetWMState]); + Atom wtype = getatomprop(c, netatom[NetWMWindowType]); + +- if (state == netatom[NetWMFullscreen]) +- setfullscreen(c, 1); +- if (wtype == netatom[NetWMWindowTypeDialog]) +- c->isfloating = 1; ++ if (state == netatom[NetWMFullscreen]) ++ setfullscreen(c, 1); ++ if (state == netatom[NetWMSticky]) { ++ setsticky(c, 1); ++ } ++ if (wtype == netatom[NetWMWindowTypeDialog]) ++ c->isfloating = 1; + } + + void +@@ -2147,3 +2183,4 @@ main(int argc, char *argv[]) + XCloseDisplay(dpy); + return EXIT_SUCCESS; + } ++ +-- +2.38.1 + diff --git a/patches/dwm-stickyindicator-6.2.diff b/patches/dwm-stickyindicator-6.2.diff new file mode 100644 index 000000000..8b6ec4188 --- /dev/null +++ b/patches/dwm-stickyindicator-6.2.diff @@ -0,0 +1,65 @@ +diff -pu dwm.stickypatch/config.def.h dwm.stickyindicator/config.def.h +--- dwm.stickypatch/config.def.h 2021-02-28 23:51:25.118904642 -0600 ++++ dwm.stickyindicator/config.def.h 2021-03-15 20:19:53.533323727 -0500 +@@ -17,6 +17,8 @@ static const char *colors[][3] = { + [SchemeNorm] = { col_gray3, col_gray1, col_gray2 }, + [SchemeSel] = { col_gray4, col_cyan, col_cyan }, + }; ++static const XPoint stickyicon[] = { {0,0}, {4,0}, {4,8}, {2,6}, {0,8}, {0,0} }; /* represents the icon as an array of vertices */ ++static const XPoint stickyiconbb = {4,8}; /* defines the bottom right corner of the polygon's bounding box (speeds up scaling) */ + + /* tagging */ + static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; +diff -pu dwm.stickypatch/drw.c dwm.stickyindicator/drw.c +--- dwm.stickypatch/drw.c 2021-02-28 23:51:06.992237482 -0600 ++++ dwm.stickyindicator/drw.c 2021-03-15 20:19:19.499990633 -0500 +@@ -248,6 +248,26 @@ drw_rect(Drw *drw, int x, int y, unsigne + XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w - 1, h - 1); + } + ++void ++drw_polygon(Drw *drw, int x, int y, int ow, int oh, int sw, int sh, const XPoint *points, int npoints, int shape, int filled) /* wrapper function to scale and draw a polygon with X11 */ ++{ ++ if (!drw || !drw->scheme) ++ return; ++ XSetForeground(drw->dpy, drw->gc, drw->scheme[ColFg].pixel); ++ if (!filled) { /* reduces the scaled width and height by 1 when drawing the outline to compensate for X11 drawing the line 1 pixel over */ ++ sw -= 1; ++ sh -= 1; ++ } ++ XPoint scaledpoints[npoints]; ++ memcpy(scaledpoints, points, npoints); ++ for (int v = 0; v < npoints; v++) ++ scaledpoints[v] = (XPoint){ .x = points[v].x * sw / ow + x, .y = points[v].y * sh / oh + y }; ++ if (filled) ++ XFillPolygon(drw->dpy, drw->drawable, drw->gc, scaledpoints, npoints, shape, CoordModeOrigin); /* Change shape to 'Convex' or 'Complex' in dwm.c if the shape is not 'Nonconvex' */ ++ else ++ XDrawLines(drw->dpy, drw->drawable, drw->gc, scaledpoints, npoints, CoordModeOrigin); ++} ++ + int + drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert) + { +diff -pu dwm.stickypatch/drw.h dwm.stickyindicator/drw.h +--- dwm.stickypatch/drw.h 2021-02-28 23:51:06.992237482 -0600 ++++ dwm.stickyindicator/drw.h 2021-03-01 01:34:02.739074730 -0600 +@@ -51,6 +51,7 @@ 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); ++void drw_polygon(Drw *drw, int x, int y, int ow, int oh, int sw, int sh, const XPoint *points, int npoints, int shape, int filled); + int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert); + + /* Map functions */ +diff -pu dwm.stickypatch/dwm.c dwm.stickyindicator/dwm.c +--- dwm.stickypatch/dwm.c 2021-02-28 23:51:25.118904642 -0600 ++++ dwm.stickyindicator/dwm.c 2021-03-15 20:12:32.063326766 -0500 +@@ -736,6 +736,8 @@ drawbar(Monitor *m) + drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); + if (m->sel->isfloating) + drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0); ++ if (m->sel->issticky) ++ drw_polygon(drw, x + boxs, m->sel->isfloating ? boxs * 2 + boxw : boxs, stickyiconbb.x, stickyiconbb.y, boxw, boxw * stickyiconbb.y / stickyiconbb.x, stickyicon, LENGTH(stickyicon), Nonconvex, m->sel->tags & m->tagset[m->seltags]); + } else { + drw_setscheme(drw, scheme[SchemeNorm]); + drw_rect(drw, x, 0, w, bh, 1, 1); diff --git a/patches/dwm-tag-preview-6.3.diff b/patches/dwm-tag-preview-6.3.diff new file mode 100644 index 000000000..dbee35d4c --- /dev/null +++ b/patches/dwm-tag-preview-6.3.diff @@ -0,0 +1,315 @@ +From 841ad7d5767f945ee9da6c5afc8cff98ca2f8231 Mon Sep 17 00:00:00 2001 +From: explosion-mental +Date: Thu, 1 Sep 2022 16:21:58 -0500 +Subject: [PATCH] [PATCH] tag previews: free() tagmap and add previewtag func + +Allows you to see the contents of an already viewed tag. So a more +accurate description would be to re-view a tag. + +Allows you to see the contents of an already viewed tag. So a more +accurate description would be to re-view a tag. + +Compatibility with the alpha patch (replacing DefaultDepth() and +DefaultVisual() with depth and visual + window masks) and hide vacants can be +achieved, I left some lines to uncomment. + +added: +* more compact structure, more probable to patch on top of other patches + or easier to patch manually (like not moving the Monitor struct..) +* create the window preview in updatebars() +* renamed switchtag() -> takepreview(), makes more sense since it's + "taking" the preview (basically a screenshot). +* option previewbar, whether to show the bar in the preview or not. +* previewtag which takes a tag (unsigned int from 0 to the last tag) and + previews it. This allows to preview tags without using the + cursor/mouse (which avoids a recursive previews preview). + adding it to the TAGKEYS macro makes more sense so I've added it + replacing (keybinding wise, not functionality) toggletag. +``` +\#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, previewtag, {.ui = TAG } }, +``` +--- + config.def.h | 4 +- + config.mk | 5 +- + dwm.c | 139 ++++++++++++++++++++++++++++++++++++++++++++++++++- + 3 files changed, 145 insertions(+), 3 deletions(-) + +diff --git a/config.def.h b/config.def.h +index a2ac963..eb70348 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -3,6 +3,8 @@ + /* appearance */ + static const unsigned int borderpx = 1; /* border pixel of windows */ + static const unsigned int snap = 32; /* snap pixel */ ++static const int scalepreview = 4; /* preview scaling (display w and h / scalepreview) */ ++static const int previewbar = 1; /* show the bar in the preview window */ + static const int showbar = 1; /* 0 means no bar */ + static const int topbar = 1; /* 0 means bottom bar */ + static const char *fonts[] = { "monospace:size=10" }; +@@ -50,7 +52,7 @@ static const Layout layouts[] = { + { 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} }, ++ { MODKEY|ControlMask|ShiftMask, KEY, previewtag, {.ui = TAG } }, \ + + /* helper for spawning shell commands in the pre dwm-5.0 fashion */ + #define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } } +diff --git a/config.mk b/config.mk +index b6eb7e0..6f5129e 100644 +--- a/config.mk ++++ b/config.mk +@@ -20,9 +20,12 @@ FREETYPEINC = /usr/include/freetype2 + # OpenBSD (uncomment) + #FREETYPEINC = ${X11INC}/freetype2 + ++# Imlib2 (tag previews) ++IMLIB2LIBS = -lImlib2 ++ + # includes and libs + INCS = -I${X11INC} -I${FREETYPEINC} +-LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} ++LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} ${IMLIB2LIBS} + + # flags + CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} +diff --git a/dwm.c b/dwm.c +index a96f33c..0c0ba12 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -40,6 +40,7 @@ + #include + #endif /* XINERAMA */ + #include ++#include + + #include "drw.h" + #include "util.h" +@@ -112,6 +113,9 @@ typedef struct { + } Layout; + + struct Monitor { ++ int previewshow; ++ Window tagwin; ++ Pixmap *tagmap; + char ltsymbol[16]; + float mfact; + int nmaster; +@@ -235,6 +239,10 @@ static int xerrordummy(Display *dpy, XErrorEvent *ee); + static int xerrorstart(Display *dpy, XErrorEvent *ee); + static void zoom(const Arg *arg); + ++static void showtagpreview(unsigned int i); ++static void takepreview(void); ++static void previewtag(const Arg *arg); ++ + /* variables */ + static const char broken[] = "broken"; + static char stext[256]; +@@ -438,6 +446,11 @@ buttonpress(XEvent *e) + if (i < LENGTH(tags)) { + click = ClkTagBar; + arg.ui = 1 << i; ++ /* hide preview if we click the bar */ ++ if (selmon->previewshow) { ++ selmon->previewshow = 0; ++ XUnmapWindow(dpy, selmon->tagwin); ++ } + } else if (ev->x < x + blw) + click = ClkLtSymbol; + else if (ev->x > selmon->ww - (int)TEXTW(stext)) +@@ -498,6 +511,7 @@ void + cleanupmon(Monitor *mon) + { + Monitor *m; ++ size_t i; + + if (mon == mons) + mons = mons->next; +@@ -505,8 +519,14 @@ cleanupmon(Monitor *mon) + for (m = mons; m && m->next != mon; m = m->next); + m->next = mon->next; + } ++ for (i = 0; i < LENGTH(tags); i++) ++ if (mon->tagmap[i]) ++ XFreePixmap(dpy, mon->tagmap[i]); ++ free(mon->tagmap); + XUnmapWindow(dpy, mon->barwin); + XDestroyWindow(dpy, mon->barwin); ++ XUnmapWindow(dpy, mon->tagwin); ++ XDestroyWindow(dpy, mon->tagwin); + free(mon); + } + +@@ -641,6 +661,7 @@ createmon(void) + m->topbar = topbar; + m->lt[0] = &layouts[0]; + m->lt[1] = &layouts[1 % LENGTH(layouts)]; ++ m->tagmap = ecalloc(LENGTH(tags), sizeof(Pixmap)); + strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); + return m; + } +@@ -1125,6 +1146,36 @@ motionnotify(XEvent *e) + static Monitor *mon = NULL; + Monitor *m; + XMotionEvent *ev = &e->xmotion; ++ unsigned int i, x; ++ ++ if (ev->window == selmon->barwin) { ++ i = x = 0; ++ do ++ x += TEXTW(tags[i]); ++ while (ev->x >= x && ++i < LENGTH(tags)); ++ /* FIXME when hovering the mouse over the tags and we view the tag, ++ * the preview window get's in the preview shot */ ++ ++ if (i < LENGTH(tags)) { ++ if (selmon->previewshow != (i + 1) ++ && !(selmon->tagset[selmon->seltags] & 1 << i)) { ++ selmon->previewshow = i + 1; ++ showtagpreview(i); ++ } else if (selmon->tagset[selmon->seltags] & 1 << i) { ++ selmon->previewshow = 0; ++ XUnmapWindow(dpy, selmon->tagwin); ++ } ++ } else if (selmon->previewshow) { ++ selmon->previewshow = 0; ++ XUnmapWindow(dpy, selmon->tagwin); ++ } ++ } else if (ev->window == selmon->tagwin) { ++ selmon->previewshow = 0; ++ XUnmapWindow(dpy, selmon->tagwin); ++ } else if (selmon->previewshow) { ++ selmon->previewshow = 0; ++ XUnmapWindow(dpy, selmon->tagwin); ++ } + + if (ev->window != root) + return; +@@ -1530,6 +1581,82 @@ setmfact(const Arg *arg) + arrange(selmon); + } + ++void ++showtagpreview(unsigned int i) ++{ ++ if (!selmon->previewshow || !selmon->tagmap[i]) { ++ XUnmapWindow(dpy, selmon->tagwin); ++ return; ++ } ++ ++ XSetWindowBackgroundPixmap(dpy, selmon->tagwin, selmon->tagmap[i]); ++ XCopyArea(dpy, selmon->tagmap[i], selmon->tagwin, drw->gc, 0, 0, ++ selmon->mw / scalepreview, selmon->mh / scalepreview, ++ 0, 0); ++ XSync(dpy, False); ++ XMapRaised(dpy, selmon->tagwin); ++} ++ ++void ++takepreview(void) ++{ ++ Client *c; ++ Imlib_Image image; ++ unsigned int occ = 0, i; ++ ++ for (c = selmon->clients; c; c = c->next) ++ occ |= c->tags; ++ //occ |= c->tags == 255 ? 0 : c->tags; /* hide vacants */ ++ ++ for (i = 0; i < LENGTH(tags); i++) { ++ /* searching for tags that are occupied && selected */ ++ if (!(occ & 1 << i) || !(selmon->tagset[selmon->seltags] & 1 << i)) ++ continue; ++ ++ if (selmon->tagmap[i]) { /* tagmap exist, clean it */ ++ XFreePixmap(dpy, selmon->tagmap[i]); ++ selmon->tagmap[i] = 0; ++ } ++ ++ /* try to unmap the window so it doesn't show the preview on the preview */ ++ selmon->previewshow = 0; ++ XUnmapWindow(dpy, selmon->tagwin); ++ XSync(dpy, False); ++ ++ if (!(image = imlib_create_image(sw, sh))) { ++ fprintf(stderr, "dwm: imlib: failed to create image, skipping."); ++ continue; ++ } ++ imlib_context_set_image(image); ++ imlib_context_set_display(dpy); ++ /* uncomment if using alpha patch */ ++ //imlib_image_set_has_alpha(1); ++ //imlib_context_set_blend(0); ++ //imlib_context_set_visual(visual); ++ imlib_context_set_visual(DefaultVisual(dpy, screen)); ++ imlib_context_set_drawable(root); ++ ++ if (previewbar) ++ imlib_copy_drawable_to_image(0, selmon->wx, selmon->wy, selmon->ww, selmon->wh, 0, 0, 1); ++ else ++ imlib_copy_drawable_to_image(0, selmon->mx, selmon->my, selmon->mw ,selmon->mh, 0, 0, 1); ++ selmon->tagmap[i] = XCreatePixmap(dpy, selmon->tagwin, selmon->mw / scalepreview, selmon->mh / scalepreview, DefaultDepth(dpy, screen)); ++ imlib_context_set_drawable(selmon->tagmap[i]); ++ imlib_render_image_part_on_drawable_at_size(0, 0, selmon->mw, selmon->mh, 0, 0, selmon->mw / scalepreview, selmon->mh / scalepreview); ++ imlib_free_image(); ++ } ++} ++ ++void ++previewtag(const Arg *arg) ++{ ++ if (selmon->previewshow != (arg->ui + 1)) ++ selmon->previewshow = arg->ui + 1; ++ else ++ selmon->previewshow = 0; ++ showtagpreview(arg->ui); ++} ++ + void + setup(void) + { +@@ -1746,6 +1873,7 @@ toggleview(const Arg *arg) + unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK); + + if (newtagset) { ++ takepreview(); + selmon->tagset[selmon->seltags] = newtagset; + focus(NULL); + arrange(selmon); +@@ -1811,10 +1939,18 @@ updatebars(void) + XSetWindowAttributes wa = { + .override_redirect = True, + .background_pixmap = ParentRelative, +- .event_mask = ButtonPressMask|ExposureMask ++ .event_mask = ButtonPressMask|ExposureMask|PointerMotionMask + }; ++ + XClassHint ch = {"dwm", "dwm"}; + for (m = mons; m; m = m->next) { ++ if (!m->tagwin) { ++ m->tagwin = XCreateWindow(dpy, root, m->wx, m->by + bh, m->mw / scalepreview, ++ m->mh / scalepreview, 0, DefaultDepth(dpy, screen), CopyFromParent, ++ DefaultVisual(dpy, screen), CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); ++ XDefineCursor(dpy, m->tagwin, cursor[CurNormal]->cursor); ++ XUnmapWindow(dpy, m->tagwin); ++ } + if (m->barwin) + continue; + m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen), +@@ -2043,6 +2179,7 @@ view(const Arg *arg) + { + if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) + return; ++ takepreview(); + selmon->seltags ^= 1; /* toggle sel tagset */ + if (arg->ui & TAGMASK) + selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; +-- +2.37.3 + diff --git a/patches/dwm-tapresize-20200819-f04cac6.diff b/patches/dwm-tapresize-20200819-f04cac6.diff new file mode 100644 index 000000000..b7cdd9029 --- /dev/null +++ b/patches/dwm-tapresize-20200819-f04cac6.diff @@ -0,0 +1,128 @@ +From d781863fb98f066d1ad98b573796d0880b48af8f Mon Sep 17 00:00:00 2001 +From: verschmelzen +Date: Wed, 19 Aug 2020 00:05:34 +0300 +Subject: [PATCH] Resize windows with touchpad two-finger scroll + +This patch allows to resize windows using mouse scroll events. Since +there is no right-click-tap-to-drag I found this patch to be the only +way to be able to both move and resize windows with touchpad. +--- + config.def.h | 16 ++++++++++++++++ + dwm.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 62 insertions(+) + +diff --git a/config.def.h b/config.def.h +index 1c0b587..d7d208f 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -36,6 +36,9 @@ static const float mfact = 0.55; /* factor of master area size [0.05..0.95] + static const int nmaster = 1; /* number of clients in master area */ + static const int resizehints = 1; /* 1 means respect size hints in tiled resizals */ + ++/* mouse scroll resize */ ++static const int scrollsensetivity = 30; /* 1 means resize window by 1 pixel for each scroll event */ ++ + static const Layout layouts[] = { + /* symbol arrange function */ + { "[]=", tile }, /* first entry is default */ +@@ -96,6 +99,15 @@ static Key keys[] = { + { MODKEY|ShiftMask, XK_q, quit, {0} }, + }; + ++/* resizemousescroll direction argument list */ ++static const int scrollargs[][2] = { ++ /* width change height change */ ++ { +scrollsensetivity, 0 }, ++ { -scrollsensetivity, 0 }, ++ { 0, +scrollsensetivity }, ++ { 0, -scrollsensetivity }, ++}; ++ + /* button definitions */ + /* click can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */ + static Button buttons[] = { +@@ -107,6 +119,10 @@ static Button buttons[] = { + { ClkClientWin, MODKEY, Button1, movemouse, {0} }, + { ClkClientWin, MODKEY, Button2, togglefloating, {0} }, + { ClkClientWin, MODKEY, Button3, resizemouse, {0} }, ++ { ClkClientWin, MODKEY, Button4, resizemousescroll, {.v = &scrollargs[0]} }, ++ { ClkClientWin, MODKEY, Button5, resizemousescroll, {.v = &scrollargs[1]} }, ++ { ClkClientWin, MODKEY, Button6, resizemousescroll, {.v = &scrollargs[2]} }, ++ { ClkClientWin, MODKEY, Button7, resizemousescroll, {.v = &scrollargs[3]} }, + { ClkTagBar, 0, Button1, view, {0} }, + { ClkTagBar, 0, Button3, toggleview, {0} }, + { ClkTagBar, MODKEY, Button1, tag, {0} }, +diff --git a/dwm.c b/dwm.c +index 9fd0286..30f14db 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -57,6 +57,12 @@ + #define TAGMASK ((1 << LENGTH(tags)) - 1) + #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) + ++/* Undefined in X11/X.h buttons that are actualy exist and correspond to ++ * horizontal scroll ++ */ ++#define Button6 6 ++#define Button7 7 ++ + /* enums */ + enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ + enum { SchemeNorm, SchemeSel }; /* color schemes */ +@@ -192,6 +198,7 @@ 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 resizemousescroll(const Arg *arg); + static void restack(Monitor *m); + static void run(void); + static void scan(void); +@@ -1345,6 +1352,45 @@ resizemouse(const Arg *arg) + } + } + ++void ++resizemousescroll(const Arg *arg) ++{ ++ int nw, nh; ++ Client *c; ++ Monitor *m; ++ XEvent ev; ++ int dw = *((int*)arg->v + 1); ++ int dh = *(int*)arg->v; ++ ++ if (!(c = selmon->sel)) ++ return; ++ if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */ ++ return; ++ restack(selmon); ++ if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, ++ None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess) ++ return; ++ nw = MAX(c->w + dw, 1); ++ nh = MAX(c->h + dh, 1); ++ if (c->mon->wx + nw >= selmon->wx && c->mon->wx + nw <= selmon->wx + selmon->ww ++ && c->mon->wy + nh >= selmon->wy && c->mon->wy + nh <= selmon->wy + selmon->wh) ++ { ++ if (!c->isfloating && selmon->lt[selmon->sellt]->arrange ++ && (abs(nw - c->w) > snap || abs(nh - c->h) > snap)) ++ togglefloating(NULL); ++ } ++ if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) ++ resize(c, c->x, c->y, nw, nh, 1); ++ XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); ++ XUngrabPointer(dpy, CurrentTime); ++ while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); ++ if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { ++ sendmon(c, m); ++ selmon = m; ++ focus(NULL); ++ } ++} ++ + void + restack(Monitor *m) + { +-- +2.28.0 + diff --git a/patches/dwm-truecenteredtitle-6.3.diff b/patches/dwm-truecenteredtitle-6.3.diff new file mode 100644 index 000000000..57eb0281a --- /dev/null +++ b/patches/dwm-truecenteredtitle-6.3.diff @@ -0,0 +1,34 @@ +From be8b8d6a0b864a7c6ca7e37a1df9f53ddd87916b Mon Sep 17 00:00:00 2001 +From: explosion-mental +Date: Tue, 12 Apr 2022 12:10:14 -0500 +Subject: [PATCH] [PATCH][truecenteredtitle]Center the title with proportion to + the area that the title has, unlike the [other center title](../centretitle) + patch that center proportion to the screen size width (`m->ww`), which on + smaller monitors doesn't get the effect. + +If the title name is to long (title area too small for the title string) +it will back up to the default behaviour, which is to truncate the +title. +--- + dwm.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/dwm.c b/dwm.c +index a96f33c..6198b29 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -735,7 +735,10 @@ drawbar(Monitor *m) + if ((w = m->ww - tw - x) > bh) { + if (m->sel) { + drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); +- drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); ++ if (TEXTW(m->sel->name) > w) /* title is bigger than the width of the title rectangle, don't center */ ++ drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); ++ else /* center window title */ ++ drw_text(drw, x, 0, w, bh, (w - TEXTW(m->sel->name)) / 2, m->sel->name, 0); + if (m->sel->isfloating) + drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0); + } else { +-- +2.35.1 + diff --git a/patches/dwm-warp-6.4.diff b/patches/dwm-warp-6.4.diff new file mode 100644 index 000000000..02fcdba1a --- /dev/null +++ b/patches/dwm-warp-6.4.diff @@ -0,0 +1,79 @@ +From a229c36f51ad6f8b40109ed53c643f242351962a Mon Sep 17 00:00:00 2001 +From: Jonas Dujava +Date: Fri, 26 May 2023 22:14:48 +0200 +Subject: [PATCH] Warp 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. + +This version properly handles warping to windows that have not been +mapped yet (before it resulted in a change of the stack order). +See the discussion in (thanks goes to Bakkeby): + https://github.com/bakkeby/patches/issues/60 +--- + dwm.c | 26 ++++++++++++++++++++++++++ + 1 file changed, 26 insertions(+) + +diff --git a/dwm.c b/dwm.c +index e5efb6a..7ea6c14 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -228,6 +228,7 @@ static void updatetitle(Client *c); + static void updatewindowtype(Client *c); + static void updatewmhints(Client *c); + static void view(const Arg *arg); ++static void warp(const Client *c); + static Client *wintoclient(Window w); + static Monitor *wintomon(Window w); + static int xerror(Display *dpy, XErrorEvent *ee); +@@ -834,6 +835,7 @@ focusmon(const Arg *arg) + unfocus(selmon->sel, 0); + selmon = m; + focus(NULL); ++ warp(selmon->sel); + } + + void +@@ -1366,6 +1368,8 @@ restack(Monitor *m) + wc.sibling = c->win; + } + } ++ if (m == selmon && (m->tagset[m->seltags] & m->sel->tags) && m->lt[m->sellt]->arrange != &monocle) ++ warp(m->sel); + XSync(dpy, False); + while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); + } +@@ -2044,6 +2048,28 @@ view(const Arg *arg) + arrange(selmon); + } + ++void ++warp(const Client *c) ++{ ++ int x, y; ++ ++ if (!c) { ++ XWarpPointer(dpy, None, root, 0, 0, 0, 0, selmon->wx + selmon->ww / 2, selmon->wy + selmon->wh / 2); ++ return; ++ } ++ ++ if (!getrootptr(&x, &y) || ++ (x > c->x - c->bw && ++ y > c->y - c->bw && ++ x < c->x + c->w + c->bw*2 && ++ y < c->y + c->h + c->bw*2) || ++ (y > c->mon->by && y < c->mon->by + bh) || ++ (c->mon->topbar && !y)) ++ return; ++ ++ XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w / 2, c->h / 2); ++} ++ + Client * + wintoclient(Window w) + { +-- +2.40.1 + diff --git a/patches/dwm-winicon-6.3-v2.1.diff b/patches/dwm-winicon-6.3-v2.1.diff new file mode 100644 index 000000000..427843178 --- /dev/null +++ b/patches/dwm-winicon-6.3-v2.1.diff @@ -0,0 +1,371 @@ +diff --git a/config.def.h b/config.def.h +index a2ac963..322d181 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -5,6 +5,8 @@ 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 */ ++#define ICONSIZE 16 /* icon size */ ++#define ICONSPACING 5 /* space between icon and title */ + static const char *fonts[] = { "monospace:size=10" }; + static const char dmenufont[] = "monospace:size=10"; + static const char col_gray1[] = "#222222"; +diff --git a/config.mk b/config.mk +index b6eb7e0..f3c01b0 100644 +--- a/config.mk ++++ b/config.mk +@@ -22,7 +22,7 @@ FREETYPEINC = /usr/include/freetype2 + + # includes and libs + INCS = -I${X11INC} -I${FREETYPEINC} +-LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} ++LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} -lXrender -lImlib2 + + # flags + CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} +diff --git a/drw.c b/drw.c +index 4cdbcbe..9b474c5 100644 +--- a/drw.c ++++ b/drw.c +@@ -4,6 +4,7 @@ + #include + #include + #include ++#include + + #include "drw.h" + #include "util.h" +@@ -71,6 +72,7 @@ drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h + drw->w = w; + drw->h = h; + drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen)); ++ drw->picture = XRenderCreatePicture(dpy, drw->drawable, XRenderFindVisualFormat(dpy, DefaultVisual(dpy, screen)), 0, NULL); + drw->gc = XCreateGC(dpy, root, 0, NULL); + XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter); + +@@ -85,14 +87,18 @@ drw_resize(Drw *drw, unsigned int w, unsigned int h) + + drw->w = w; + drw->h = h; ++ if (drw->picture) ++ XRenderFreePicture(drw->dpy, drw->picture); + if (drw->drawable) + XFreePixmap(drw->dpy, drw->drawable); + drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen)); ++ drw->picture = XRenderCreatePicture(drw->dpy, drw->drawable, XRenderFindVisualFormat(drw->dpy, DefaultVisual(drw->dpy, drw->screen)), 0, NULL); + } + + void + drw_free(Drw *drw) + { ++ XRenderFreePicture(drw->dpy, drw->picture); + XFreePixmap(drw->dpy, drw->drawable); + XFreeGC(drw->dpy, drw->gc); + drw_fontset_free(drw->fonts); +@@ -236,6 +242,67 @@ drw_setscheme(Drw *drw, Clr *scm) + drw->scheme = scm; + } + ++Picture ++drw_picture_create_resized(Drw *drw, char *src, unsigned int srcw, unsigned int srch, unsigned int dstw, unsigned int dsth) { ++ Pixmap pm; ++ Picture pic; ++ GC gc; ++ ++ if (srcw <= (dstw << 1u) && srch <= (dsth << 1u)) { ++ XImage img = { ++ srcw, srch, 0, ZPixmap, src, ++ ImageByteOrder(drw->dpy), BitmapUnit(drw->dpy), BitmapBitOrder(drw->dpy), 32, ++ 32, 0, 32, ++ 0, 0, 0 ++ }; ++ XInitImage(&img); ++ ++ pm = XCreatePixmap(drw->dpy, drw->root, srcw, srch, 32); ++ gc = XCreateGC(drw->dpy, pm, 0, NULL); ++ XPutImage(drw->dpy, pm, gc, &img, 0, 0, 0, 0, srcw, srch); ++ XFreeGC(drw->dpy, gc); ++ ++ pic = XRenderCreatePicture(drw->dpy, pm, XRenderFindStandardFormat(drw->dpy, PictStandardARGB32), 0, NULL); ++ XFreePixmap(drw->dpy, pm); ++ ++ XRenderSetPictureFilter(drw->dpy, pic, FilterBilinear, NULL, 0); ++ XTransform xf; ++ xf.matrix[0][0] = (srcw << 16u) / dstw; xf.matrix[0][1] = 0; xf.matrix[0][2] = 0; ++ xf.matrix[1][0] = 0; xf.matrix[1][1] = (srch << 16u) / dsth; xf.matrix[1][2] = 0; ++ xf.matrix[2][0] = 0; xf.matrix[2][1] = 0; xf.matrix[2][2] = 65536; ++ XRenderSetPictureTransform(drw->dpy, pic, &xf); ++ } else { ++ Imlib_Image origin = imlib_create_image_using_data(srcw, srch, (DATA32 *)src); ++ if (!origin) return None; ++ imlib_context_set_image(origin); ++ imlib_image_set_has_alpha(1); ++ Imlib_Image scaled = imlib_create_cropped_scaled_image(0, 0, srcw, srch, dstw, dsth); ++ imlib_free_image_and_decache(); ++ if (!scaled) return None; ++ imlib_context_set_image(scaled); ++ imlib_image_set_has_alpha(1); ++ ++ XImage img = { ++ dstw, dsth, 0, ZPixmap, (char *)imlib_image_get_data_for_reading_only(), ++ ImageByteOrder(drw->dpy), BitmapUnit(drw->dpy), BitmapBitOrder(drw->dpy), 32, ++ 32, 0, 32, ++ 0, 0, 0 ++ }; ++ XInitImage(&img); ++ ++ pm = XCreatePixmap(drw->dpy, drw->root, dstw, dsth, 32); ++ gc = XCreateGC(drw->dpy, pm, 0, NULL); ++ XPutImage(drw->dpy, pm, gc, &img, 0, 0, 0, 0, dstw, dsth); ++ imlib_free_image_and_decache(); ++ XFreeGC(drw->dpy, gc); ++ ++ pic = XRenderCreatePicture(drw->dpy, pm, XRenderFindStandardFormat(drw->dpy, PictStandardARGB32), 0, NULL); ++ XFreePixmap(drw->dpy, pm); ++ } ++ ++ return pic; ++} ++ + void + drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert) + { +@@ -379,6 +446,14 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp + return x + (render ? w : 0); + } + ++void ++drw_pic(Drw *drw, int x, int y, unsigned int w, unsigned int h, Picture pic) ++{ ++ if (!drw) ++ return; ++ XRenderComposite(drw->dpy, PictOpOver, pic, None, drw->picture, 0, 0, 0, 0, x, y, w, h); ++} ++ + void + drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h) + { +diff --git a/drw.h b/drw.h +index 4bcd5ad..71aefa2 100644 +--- a/drw.h ++++ b/drw.h +@@ -21,6 +21,7 @@ typedef struct { + int screen; + Window root; + Drawable drawable; ++ Picture picture; + GC gc; + Clr *scheme; + Fnt *fonts; +@@ -49,9 +50,12 @@ void drw_cur_free(Drw *drw, Cur *cursor); + void drw_setfontset(Drw *drw, Fnt *set); + void drw_setscheme(Drw *drw, Clr *scm); + ++Picture drw_picture_create_resized(Drw *drw, char *src, unsigned int src_w, unsigned int src_h, unsigned int dst_w, unsigned int dst_h); ++ + /* 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); ++void drw_pic(Drw *drw, int x, int y, unsigned int w, unsigned int h, Picture pic); + + /* Map functions */ + void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h); +diff --git a/dwm.c b/dwm.c +index a96f33c..033ccec 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -28,6 +28,8 @@ + #include + #include + #include ++#include ++#include + #include + #include + #include +@@ -60,7 +62,7 @@ + /* enums */ + enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ + enum { SchemeNorm, SchemeSel }; /* color schemes */ +-enum { NetSupported, NetWMName, NetWMState, NetWMCheck, ++enum { NetSupported, NetWMName, NetWMIcon, NetWMState, NetWMCheck, + NetWMFullscreen, NetActiveWindow, NetWMWindowType, + NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ + enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ +@@ -93,6 +95,7 @@ struct Client { + int bw, oldbw; + unsigned int tags; + int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen; ++ unsigned int icw, ich; Picture icon; + Client *next; + Client *snext; + Monitor *mon; +@@ -170,6 +173,7 @@ static void focusin(XEvent *e); + static void focusmon(const Arg *arg); + static void focusstack(const Arg *arg); + static Atom getatomprop(Client *c, Atom prop); ++static Picture geticonprop(Window w, unsigned int *icw, unsigned int *ich); + static int getrootptr(int *x, int *y); + static long getstate(Window w); + static int gettextprop(Window w, Atom atom, char *text, unsigned int size); +@@ -214,6 +218,7 @@ 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 freeicon(Client *c); + static void unfocus(Client *c, int setfocus); + static void unmanage(Client *c, int destroyed); + static void unmapnotify(XEvent *e); +@@ -225,6 +230,7 @@ static void updatenumlockmask(void); + static void updatesizehints(Client *c); + static void updatestatus(void); + static void updatetitle(Client *c); ++static void updateicon(Client *c); + static void updatewindowtype(Client *c); + static void updatewmhints(Client *c); + static void view(const Arg *arg); +@@ -735,7 +741,8 @@ drawbar(Monitor *m) + if ((w = m->ww - tw - x) > bh) { + if (m->sel) { + drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); +- drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); ++ drw_text(drw, x, 0, w, bh, lrpad / 2 + (m->sel->icon ? m->sel->icw + ICONSPACING : 0), m->sel->name, 0); ++ if (m->sel->icon) drw_pic(drw, x + lrpad / 2, (bh - m->sel->ich) / 2, m->sel->icw, m->sel->ich, m->sel->icon); + if (m->sel->isfloating) + drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0); + } else { +@@ -875,6 +882,67 @@ getatomprop(Client *c, Atom prop) + return atom; + } + ++static uint32_t prealpha(uint32_t p) { ++ uint8_t a = p >> 24u; ++ uint32_t rb = (a * (p & 0xFF00FFu)) >> 8u; ++ uint32_t g = (a * (p & 0x00FF00u)) >> 8u; ++ return (rb & 0xFF00FFu) | (g & 0x00FF00u) | (a << 24u); ++} ++ ++Picture ++geticonprop(Window win, unsigned int *picw, unsigned int *pich) ++{ ++ int format; ++ unsigned long n, extra, *p = NULL; ++ Atom real; ++ ++ if (XGetWindowProperty(dpy, win, netatom[NetWMIcon], 0L, LONG_MAX, False, AnyPropertyType, ++ &real, &format, &n, &extra, (unsigned char **)&p) != Success) ++ return None; ++ if (n == 0 || format != 32) { XFree(p); return None; } ++ ++ unsigned long *bstp = NULL; ++ uint32_t w, h, sz; ++ { ++ unsigned long *i; const unsigned long *end = p + n; ++ uint32_t bstd = UINT32_MAX, d, m; ++ for (i = p; i < end - 1; i += sz) { ++ if ((w = *i++) >= 16384 || (h = *i++) >= 16384) { XFree(p); return None; } ++ if ((sz = w * h) > end - i) break; ++ if ((m = w > h ? w : h) >= ICONSIZE && (d = m - ICONSIZE) < bstd) { bstd = d; bstp = i; } ++ } ++ if (!bstp) { ++ for (i = p; i < end - 1; i += sz) { ++ if ((w = *i++) >= 16384 || (h = *i++) >= 16384) { XFree(p); return None; } ++ if ((sz = w * h) > end - i) break; ++ if ((d = ICONSIZE - (w > h ? w : h)) < bstd) { bstd = d; bstp = i; } ++ } ++ } ++ if (!bstp) { XFree(p); return None; } ++ } ++ ++ if ((w = *(bstp - 2)) == 0 || (h = *(bstp - 1)) == 0) { XFree(p); return None; } ++ ++ uint32_t icw, ich; ++ if (w <= h) { ++ ich = ICONSIZE; icw = w * ICONSIZE / h; ++ if (icw == 0) icw = 1; ++ } ++ else { ++ icw = ICONSIZE; ich = h * ICONSIZE / w; ++ if (ich == 0) ich = 1; ++ } ++ *picw = icw; *pich = ich; ++ ++ uint32_t i, *bstp32 = (uint32_t *)bstp; ++ for (sz = w * h, i = 0; i < sz; ++i) bstp32[i] = prealpha(bstp[i]); ++ ++ Picture ret = drw_picture_create_resized(drw, (char *)bstp, w, h, icw, ich); ++ XFree(p); ++ ++ return ret; ++} ++ + int + getrootptr(int *x, int *y) + { +@@ -1034,6 +1102,7 @@ manage(Window w, XWindowAttributes *wa) + c->h = c->oldh = wa->height; + c->oldbw = wa->border_width; + ++ updateicon(c); + updatetitle(c); + if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) { + c->mon = t->mon; +@@ -1244,6 +1313,11 @@ propertynotify(XEvent *e) + if (c == c->mon->sel) + drawbar(c->mon); + } ++ else if (ev->atom == netatom[NetWMIcon]) { ++ updateicon(c); ++ if (c == c->mon->sel) ++ drawbar(c->mon); ++ } + if (ev->atom == netatom[NetWMWindowType]) + updatewindowtype(c); + } +@@ -1560,6 +1634,7 @@ setup(void) + netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); + netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); + netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); ++ netatom[NetWMIcon] = XInternAtom(dpy, "_NET_WM_ICON", 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); +@@ -1752,6 +1827,15 @@ toggleview(const Arg *arg) + } + } + ++void ++freeicon(Client *c) ++{ ++ if (c->icon) { ++ XRenderFreePicture(dpy, c->icon); ++ c->icon = None; ++ } ++} ++ + void + unfocus(Client *c, int setfocus) + { +@@ -1773,6 +1857,7 @@ unmanage(Client *c, int destroyed) + + detach(c); + detachstack(c); ++ freeicon(c); + if (!destroyed) { + wc.border_width = c->oldbw; + XGrabServer(dpy); /* avoid race conditions */ +@@ -2007,6 +2092,13 @@ updatetitle(Client *c) + strcpy(c->name, broken); + } + ++void ++updateicon(Client *c) ++{ ++ freeicon(c); ++ c->icon = geticonprop(c->win, &c->icw, &c->ich); ++} ++ + void + updatewindowtype(Client *c) + { diff --git a/transient.c b/transient.c new file mode 100644 index 000000000..040adb5b3 --- /dev/null +++ b/transient.c @@ -0,0 +1,42 @@ +/* cc transient.c -o transient -lX11 */ + +#include +#include +#include +#include + +int main(void) { + Display *d; + Window r, f, t = None; + XSizeHints h; + XEvent e; + + d = XOpenDisplay(NULL); + if (!d) + exit(1); + r = DefaultRootWindow(d); + + f = XCreateSimpleWindow(d, r, 100, 100, 400, 400, 0, 0, 0); + h.min_width = h.max_width = h.min_height = h.max_height = 400; + h.flags = PMinSize | PMaxSize; + XSetWMNormalHints(d, f, &h); + XStoreName(d, f, "floating"); + XMapWindow(d, f); + + XSelectInput(d, f, ExposureMask); + while (1) { + XNextEvent(d, &e); + + if (t == None) { + sleep(5); + t = XCreateSimpleWindow(d, r, 50, 50, 100, 100, 0, 0, 0); + XSetTransientForHint(d, t, f); + XStoreName(d, t, "transient"); + XMapWindow(d, t); + XSelectInput(d, t, ExposureMask); + } + } + + XCloseDisplay(d); + exit(0); +} diff --git a/util.c b/util.c new file mode 100644 index 000000000..87f259016 --- /dev/null +++ b/util.c @@ -0,0 +1,32 @@ +/* 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 000000000..f633b5173 --- /dev/null +++ b/util.h @@ -0,0 +1,8 @@ +/* See LICENSE file for copyright and license details. */ + +#define MAX(A, B) ((A) > (B) ? (A) : (B)) +#define MIN(A, B) ((A) < (B) ? (A) : (B)) +#define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B)) + +void die(const char *fmt, ...); +void *ecalloc(size_t nmemb, size_t size);