| Mode | Name | Last commit | Updated | Size |
|---|---|---|---|---|
| BG | Add configuration files for Kitty terminal, sxbar, and sxwm | 4 months ago | ||
| README.md | install: Omarchy-style UI with logo, progress bar and WiFi setup | 2 hours ago | 22.9 KB | |
| applications | update | 2 weeks ago | ||
| bin | update | 2 weeks ago | ||
| config | update | 2 weeks ago | ||
| cursor-dpi-fix.patch | new | 1 month ago | 3.67 KB | |
| fullscreen-monitor-fix.patch | new | 1 month ago | 955 B | |
| install.sh | install: Omarchy-style UI with logo, progress bar and WiFi setup | 2 hours ago | 36.58 KB | |
| monitor-hotplug-remap.patch | new | 1 month ago | 1.47 KB | |
| multi-monitor-struts.patch | new | 1 month ago | 5.57 KB | |
| net-active-window.patch | updates | 1 month ago | 992 B | |
| st-patches | st: don't overwrite CLIPBOARD with empty selections | 2 hours ago | ||
| st.code-workspace | update | 2 weeks ago | 93 B | |
| strut-fresh-screen-size.patch | Add screensaver toggle functionality and improve screensaver behavior | 1 month ago | 1006 B | |
| themes | update | 2 weeks ago | ||
| udev | update | 2 weeks ago |
mmmm mm mm
*@@@***@@m @@ m@* ** @@ *@@@
@@ *@@m @@ @@* @@
@@ *@@ m@@*@@m @@@@@@ @@@@@ *@@@ @@ mm@*@@ m@@*@@@
@@ @@ @@* *@@ @@ @@ @@ !@ m@* @@ @@ **
@! m@@ @@ @@ @@ !@ !@ !@ !@****** *@@@@@m
@! m@!* @@ !@ @! !@ !@ !@ !@m m @@
!! !!! !@ !! !! !: !! !! !!****** *! @!
!: !!:* !!! !!! !! !: !! :! :!! !! !!
: : : : : : : : : ::: :: ::: : : : : : : : : :: : :!:
My personal dotfiles for a minimal, tiling X11 desktop
Contents
- About
- Features
- Installation
- Structure
- sxwm patches
- st patches
- st quirks
- Keyboard shortcuts (sxwm)
- Status bar modules (sxbar)
- Utility scripts (bin/)
- Screensaver
- Wallpaper
About
This is my personal dotfiles setup for a lightweight, keyboard-driven X11 desktop environment built around sxwm (tiling window manager, patched with a handful of local fixes โ see below), sxbar (status bar), dmenu (application launcher) and st (terminal, patched with a curated set of upstream suckless patches โ see below). Everything is set up automatically via install.sh โ clone the repo, run the script, and you have a working environment.
Features
| ๐ช | sxwm โ tiling window manager, patched with 5 local fixes (DPI, taskbar focus, multi-monitor struts, hotplug remap, fullscreen targeting) |
| ๐ | sxbar โ status bar with clock, date, battery, volume, CPU, brightness, Bluetooth, network, media player, taskbar and start menu popups |
| ๐ | dmenu โ fast application launcher (mod + p) |
| ๐ถ | bluetui โ Bluetooth management right in the terminal |
| ๐ฅ๏ธ | st โ suckless terminal, patched with 11 upstream fixes (scrollback, clipboard, box-drawing, alpha, blinking cursor, inline images, and more) |
| ๐ค | JetBrainsMono Nerd Font โ icon support in the bar and terminal |
| ๐ | PipeWire โ audio (pipewire + pipewire-pulse + wireplumber) enabled automatically |
| ๐ | Quiet install โ PC speaker (pcspkr) and the X bell are disabled |
| ๐ | Screen blanking / DPMS after 5 minutes of inactivity, plus a TTE-powered terminal screensaver |
| ๐ป | Laptop-lid handling โ auto-toggle the internal display on lid open/close, with an inhibitor to temporarily disable it |
| ๐ฑ๏ธ | Touchpad tweaks applied automatically (natural scrolling, tap-to-click, etc. via xinput) |
| โจ๏ธ | Swedish keyboard layout (setxkbmap se) |
| ๐ข | X autostarts on TTY1 at login |
Installation
git clone <this repo>
cd dotfiles
./install.sh
The script installs system packages, builds and installs sxwm (with all local patches applied), sxbar, dmenu, st (with all local patches applied) and bluetui from source, installs the JetBrainsMono Nerd Font, copies config files (backing up anything that already exists), installs the utility scripts under ~/.local/bin, sets up Geany with a Gruvbox theme (see Geany below), and offers to write ~/.xinitrc for you. A couple of steps are interactive (writing ~/.xinitrc, enabling the lid-watch daemon, setting HandleLidSwitch=ignore, installing the Bluetooth no-autosuspend udev rule, Rust/bluetui, autostarting X on TTY1) so you can opt out if you already have your own setup for those.
The first question is an optional interactive WiFi setup (iwd + dhcpcd), done before the install so apt has network: it installs the two packages if missing, starts iwd, lets you pick the adapter (only asked when there's more than one), scans and lets you pick a network (or enter a hidden one), connects with iwctl, and enables dhcpcd@<adapter>.service so DHCP only runs on the adapter you chose. If dhcpcd had to be installed, the generic dhcpcd.service (all interfaces) that Debian enables is turned off again so it doesn't grab the wired interface. 802.1x (enterprise) networks aren't handled โ configure those manually with iwctl.
The installer has an Omarchy-style UI: a centered logo, all the yes/no questions asked up front (one keypress each), then an unattended run with a progress bar, the current step and the tail of the log. Full output (apt, make, git, ...) goes to /tmp/dotfiles-install.log; if a step fails the log tail is shown and the script stops. ./install.sh --demo plays the UI with fake steps and touches nothing, useful for previewing or tweaking the look. Without a terminal (piped/CI) it falls back to plain ==> step lines and answers "no" to every question.
HandleLidSwitch=ignore only takes effect on the next reboot โ the script deliberately doesn't restart systemd-logind live, since on this machine's manually-started (no display manager) X session that has hung the whole desktop and forced a hard reboot.
Structure
dotfiles/
โโโ install.sh # Main install script
โโโ BG/
โ โโโ j9huwdxo1zzg1.jpeg # Desktop wallpaper
โโโ bin/ # Utility scripts, installed to ~/.local/bin
โ โโโ gcfs # Minimal gocryptfs vault manager (config-driven)
โ โโโ geany # Wrapper: launches Geany with GTK_THEME=Gruvbox-Dark
โ โโโ internal-display # on/off/toggle/status for the internal (eDP) screen
โ โโโ lid-display-watch # Background daemon: auto internal-display on lid open/close
โ โโโ lidsuspend # Temporarily inhibit lid-close suspend
โ โโโ mouse-settings # Applies touchpad tweaks via xinput
โ โโโ screensaver # Runs random TTE animations in a loop
โ โโโ screensaverd # Idle watcher, launches the screensaver after inactivity
โ โโโ screensaver-launch # Launches the screensaver fullscreen on every monitor
โ โโโ screensaver.txt # ASCII art shown by the screensaver
โ โโโ status # Overview: WM stack, services, network, bluetooth, audio, battery, lock state
โโโ *.patch # Local sxwm patches, see "sxwm patches" below
โโโ st-patches/ # Upstream st patches, see "st patches" below
โ โโโ 01-delkey.patch
โ โโโ 02-scrollback.patch
โ โโโ 03-clipboard.patch
โ โโโ 04-boxdraw.patch
โ โโโ 05-vertcenter.patch
โ โโโ 06-alpha.patch
โ โโโ 07-blinking_cursor.patch
โ โโโ 08-bold-is-not-bright.patch
โ โโโ 09-anysize.patch
โ โโโ 10-histsize-5000.patch
โ โโโ 11-kitty-graphics-protocol.patch
โโโ applications/
โ โโโ geany.desktop # Geany launcher override (forces Gruvbox-Dark GTK theme)
โโโ udev/
โ โโโ 71-bluetooth-no-autosuspend.rules # Opt-in: disables USB autosuspend on the BT adapter
โโโ themes/
โ โโโ Gruvbox-Dark/ # GTK3 theme, installed to ~/.local/share/themes
โ โโโ index.theme
โ โโโ gtk-3.0/gtk.css
โโโ config/
โโโ sxwmrc # sxwm config (keybinds, layout, colours)
โโโ sxbarc # sxbar config (modules, colours, icons)
โโโ gcfs/
โ โโโ vaults.conf # gcfs vault list (ships with the commented example only)
โโโ geany/
โ โโโ geany.conf # Geany prefs (Nerd Font, Gruvbox editor colour scheme)
โ โโโ colorschemes/
โ โโโ gruvbox.conf # Gruvbox editor colour scheme
โโโ st/
โโโ config.def.h # st config (font, colours, alpha, cursor, keybinds)
sxwm patches
install.sh clones upstream sxwm and applies these on top before building, in order:
| Patch | Fixes |
|---|---|
cursor-dpi-fix.patch | Incorrect cursor scaling on high-DPI displays |
net-active-window.patch | Adds _NET_ACTIVE_WINDOW support โ required for sxbar's taskbar module; without it, clicking a taskbar entry does nothing |
multi-monitor-struts.patch | _NET_WM_STRUT_PARTIAL reservations weren't clamped to the panel's own monitor, so a monitor's reserved area could exceed its actual size and push windows off-screen |
monitor-hotplug-remap.patch | Windows lost their correct monitor association on hotplug (e.g. lid-display-watch toggling eDP); monitor ownership is now recomputed from each window's actual position |
fullscreen-monitor-fix.patch | Fullscreening a window that had been moved to another monitor by an external tool (xdotool, screensaver-launch) fullscreened it on the wrong screen; the target monitor is now recomputed from the window's real geometry |
strut-fresh-screen-size.patch | A bottom/right-edge panel (e.g. sxbar's secondary_bar) could end up hidden behind normal windows โ reserve_bottom/reserve_right are measured from the opposite screen edge and need the full screen size, but read it from scr_width/scr_height, cached globals only refreshed by update_mons(); reserve_top/reserve_left never depend on screen size so stayed correct. Reads the screen size fresh from the X server instead |
st patches
install.sh clones upstream st (v0.9.3) and applies these on top before building, in order โ all sourced from st.suckless.org/patches:
| Patch | Adds |
|---|---|
01-delkey.patch | Fixes the Delete key to send \033[3~ instead of the legacy \033[P |
02-scrollback.patch | Keyboard (Shift+PageUp/Down) and mouse-wheel scrollback, disabled in the alt screen (vim, less, etc.) |
03-clipboard.patch | Selecting text also copies it to the CLIPBOARD selection, not just PRIMARY. Local fix on top of the upstream patch: empty/whitespace-only selections (a jittery click, dragging over blank cells) no longer overwrite CLIPBOARD, which used to wipe what you'd just copied in e.g. Firefox before you could paste it |
04-boxdraw.patch | Renders box-drawing/braille characters natively instead of relying on the font |
05-vertcenter.patch | Vertically centers glyphs that don't fill the cell (pairs with boxdraw) |
06-alpha.patch | Background transparency (-A/alpha), needs a compositor |
07-blinking_cursor.patch | Blinking cursor support with a configurable rate |
08-bold-is-not-bright.patch | Bold text no longer automatically renders in the bright color variant |
09-anysize.patch | Window can be resized to any pixel size instead of snapping to whole cells |
10-histsize-5000.patch | Bumps the scrollback buffer from 2000 to 5000 lines (local tweak, not from upstream โ matches the previous kitty scrollback_lines) |
11-kitty-graphics-protocol.patch | Inline image display via a subset of the kitty graphics protocol (works with icat, image.nvim, yazi, etc.). Needs imlib2/zlib (installed by install.sh), plus imagemagick and bc at runtime for the bundled icat script. Hand-merged against the other patches โ several hunks conflicted with boxdraw/vertcenter/anysize/blinking_cursor and were reconciled manually; confirmed working |
config/st/config.def.h is copied over the patched source before building, so local tweaks survive a rebuild โ edit it and re-run install.sh to apply changes. It's tuned to match the previous kitty setup: Gruvbox Dark Hard colours, opaque background, steady (non-blinking) block cursor, JetBrainsMono Nerd Font, and 3 lines scrolled per mouse-wheel notch. Live font zoom is built into vanilla st โ Ctrl+Shift+PageUp/PageDown to zoom, Ctrl+Shift+Home to reset โ no patch needed.
install.sh also installs icat (the patch's bundled icat-mini.sh) to ~/.local/bin/icat โ run icat path/to/image.png in st to display it inline.
Image protocol shortcuts (from patch 11): Ctrl+Shift+click on an image previews it in feh, Ctrl+Shift+middle-click shows placement info in less, Ctrl+Shift+F1 toggles graphics debug output, Ctrl+Shift+F6 dumps internal state, Ctrl+Shift+F7 unloads cached images to free RAM, and Ctrl+Shift+F8 toggles image display on/off entirely.
st quirks
Coming from kitty, a few things about st behave differently enough to be worth writing down:
- Config is compiled in, not read at runtime. kitty re-reads
kitty.confon the fly; st bakesconfig.def.hinto the binary at build time asconfig.h. Changing font/colours/keybinds means editingconfig/st/config.def.hand rebuilding (./install.sh, or the manual steps below) โ there's no config file to just edit and reopen the terminal. config.hdoesn't auto-refresh fromconfig.def.h. The Makefile'srule for
config.honly runs if the file doesn't exist yet โmake cleandoes not delete it. If you rebuild in a directory that already has a staleconfig.hfrom a previous build, yourconfig.def.hedits are silently ignored. Eitherrm -f config.hbeforemake, or always build in a freshgit clone(which is whatinstall.shdoes).- Manual rebuild, step by step (what
install.shdoes for st, if youwant to do it yourself):
cd ~ # don't sit inside the directory you're about to rm -rf!
rm -rf /tmp/st-build
git clone --depth=1 https://git.suckless.org/st /tmp/st-build
cd /tmp/st-build
for p in ~/dotfiles/st-patches/*.patch; do patch -p1 < "$p"; done
cp ~/dotfiles/config/st/config.def.h config.def.h
make
sudo make install
- Live font zoom exists but isn't advertised.
Ctrl+Shift+PageUp/PageDownzooms the font in/out,
Ctrl+Shift+Homeresets it โ built into vanilla st, no patch needed. Easy to miss since kitty's equivalent (change_font_size) is the one actually documented inkitty.conf. - No runtime equivalent of kitty's per-launch
--override. Things likewindow_padding_widthormouse_hide_waitare compile-time-only in st;screensaver-launchcan only override font (-f) and alpha (-A) when spawning the screensaver's st instance, not padding or cursor-hiding. - Window size is in character cells, not pixels.
cols/rowsinconfig.def.hset the initial size; kitty'sinitial_window_width/heightwere literal pixels. With theanysizepatch the window can still be resized to any pixel size afterwards, it just doesn't start at one under sxwm anyway since sxwm tiles it immediately. icatneedsimagemagickandbcat runtime, not just build-timeimlib2/zlib.makesucceeds without them; the script only fails the first time you actually try to display an image. Both are ininstall.sh's package list now.- Scrollback is intentionally disabled inside full-screen apps (vim,
less, tmux, etc. โ anything using the alt screen). That's the
02-scrollback.patchbehaviour, not a bug โ those apps have their own scrolling. - Selecting text copies to both
PRIMARYandCLIPBOARD(from03-clipboard.patch), unlike vanilla st which only ever touchesPRIMARYon select and needs an explicit keybind to push toCLIPBOARD.
Keyboard shortcuts (sxwm)
mod = Super
| Shortcut | Action |
|---|---|
mod + Return | Open st |
mod + b | Open Firefox |
mod + p | dmenu (application launcher) |
mod + q | Close window |
mod + shift + e | Quit sxwm |
mod + c | Centre window |
mod + m | Toggle monocle mode |
mod + j / mod + k | Focus next / previous window |
mod + comma / mod + period | Focus previous / next monitor |
mod + shift + comma / mod + shift + period | Move window to previous / next monitor |
mod + shift + j / mod + shift + k | Move window in master/stack |
mod + l / mod + h | Increase / decrease master area |
mod + ctrl + l / mod + ctrl + h | Increase / decrease stack window size |
mod + Arrow keys | Move window |
mod + shift + Arrow keys | Resize window |
mod + o / mod + i | Increase / decrease gaps |
mod + space | Toggle floating |
mod + shift + space | Toggle global floating |
mod + shift + f | Fullscreen |
mod + r | Reload config |
mod + alt + [1-4] | Create scratchpad |
mod + ctrl + [1-4] | Toggle scratchpad |
mod + alt + shift + [1-4] | Remove scratchpad |
mod + [1-9] | Switch workspace |
mod + shift + [1-9] | Move window to workspace |
Media/function keys (volume, brightness, playback) are bound too โ see config/sxwmrc.
Status bar modules (sxbar)
| Module | Refresh interval | Colour | Notes |
|---|---|---|---|
| ๐ Clock | 1 s | #50fa7b | Centred in the bar |
| ๐ Date | 60 s | #8be9fd | |
| ๐ Battery | 30 s | #ffb86c | |
| ๐ Volume | 5 s | #ff79c6 | |
| โ๏ธ CPU | 3 s | #bd93f9 | |
| โ๏ธ Brightness | 5 s | #f1fa8c | Hover to reveal a slider |
| ๐ต Bluetooth | 10 s | #4a9eff | Click for an on/off/scan/pair menu |
| ๐ค User menu | 300 s | #ff5555 | Click for sleep/log out/shut down |
| ๐ Network | 10 s | #2ee6d6 | Click for a WiFi/Ethernet + IP menu |
| ๐ต Media | 2 s | #cba6f7 | Hover for album art + prev/play-pause/next |
| ๐ Taskbar | 1 s | โ | Secondary bar; click an entry to focus that window |
| โญ Start menu | 3600 s | โ | Hover for favourite apps as popup buttons |
Utility scripts (bin/)
Installed to ~/.local/bin by install.sh and wired into sxwmrc/~/.xinitrc:
mouse-settingsโ applies libinput touchpad tweaks viaxinput(naturalscrolling on the touchpad only, tap-to-click, etc.). Safe to re-run any time.
internal-display [on|off|toggle|status]โ (de)activates the internal(eDP) screen via
xrandr. Refuses to turn it off if it's the only active display.lid-display-watchโ background daemon (started via sxwm'sexec) thatwatches the laptop lid state through
systemd-logindand automatically turns the internal display off on lid-close / on on lid-open, provided an external monitor is connected. Opt-in during install.lidsuspend [on|off|status]โ temporarily disables/re-enables what happenson lid-close (suspend), without touching
/etc/systemd/logind.conf, by holding a systemd inhibitor lock.screensaverdโ idle watcher; launches the screensaver after a period ofinactivity (default 150 s) and kills it the moment real activity resumes.
screensaver-launchโ launches the screensaver fullscreen on everyconnected monitor.
screensaverโ runs random TTE(terminal text effects) animations in a loop over the ASCII art in
screensaver.txt.statusโ read-only overview: which WM-stack processes and systemdservices (bluetooth, pipewire, pipewire-pulse, wireplumber) are running, network/wifi state, connected Bluetooth devices, default audio sink, battery/AC state, DPMS/lid/lidsuspend state, and currently playing media.
gcfs [status|mount|umount|mount-all|umount-all|add|init|edit]โ minimalgocryptfs vault manager. Reads a
name|cipherdir|mountpointlist from~/.config/gcfs/vaults.conf, shows each vault's mount state, and mounts/unmounts by name or number (run with no args for an interactive menu). Letsgocryptfsdo its own password prompt, so no secret passes through the script. Needsgocryptfsandfusermount(fuse3), both pulled in byinstall.sh.
Screensaver
The screensaver renders bin/screensaver.txt through random terminaltexteffects animations (installed via pipx during setup) inside a fullscreen st window, and exits the instant you touch the mouse or keyboard.
Geany
Geany is set up with a Gruvbox dark theme and JetBrainsMono Nerd Font, covering both the editor and the surrounding GTK chrome (menus, toolbar, sidebar):
config/geany/geany.confโ editor/tagbar/msgwinfonts set to JetBrainsMono Nerd Font,
color_scheme=gruvbox.conf.config/geany/colorschemes/gruvbox.confโ Gruvbox dark syntax colours for the editor itself.
themes/Gruvbox-Dark/โ a GTK3 theme (Adwaita-dark'sstructure with Gruvbox colours layered on via
@define-color) for the rest of the window, since Geany's owncolor_schemeonly styles the editor pane, not the menus/toolbar/sidebar.bin/geanyโ wrapper installed to~/.local/bin/geanythatlaunches the real
/usr/bin/geanywithGTK_THEME=Gruvbox-Dark. A shell alias isn't enough here: dmenu/sxwm never source~/.bashrc, but~/.local/binis first inPATH(see~/.xinitrc), so the wrapper shadows the system binary for every launch path (dmenu,.desktop, terminal).applications/geany.desktopโ copy of thesystem
.desktopentry withExec=env GTK_THEME=Gruvbox-Dark geany %F, so app-launcher entries that read.desktopfiles (not just$PATH) pick up the theme too.
The theme is scoped to Geany only โ the system GTK theme (Adwaita) is untouched, so other GTK apps aren't affected.
Wallpaper

Set automatically via feh --bg-scale in config/sxwmrc.
Built for a fast, minimal, keyboard-driven workday.