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 000000000..507f5dad9 Binary files /dev/null and b/dwm.png differ 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);