1
0
Fork 0

Add sway/wofi/foot Wayland desktop stack with per-host toggle

New opt-in feature for x86_64 desktops: sway + wofi + foot + swaybg +
swaylock + swayidle + grim + slurp + waybar + wl-clipboard. mako on
arch, dunst on debian (mako isn't packaged for debian).

Files:
- .chezmoi.yaml.tmpl: added data.sway flag (true on miche/byte/kaiser,
  false on Pis). Override per host with ~/.config/chezmoi/features/sway
  or ~/.config/chezmoi/features/no-sway marker files.
- run_once_40-install-sway.sh.tmpl: installs packages if .sway=true,
  exits 0 otherwise. Sets up the marker file.
- dot_config/{sway,foot,wofi,waybar,mako}/: existing configs from miche.

Per-host toggle workflow:
  # On any box, enable sway:
  touch ~/.config/chezmoi/features/sway
  chezmoi apply

  # On a sway-enabled box, disable it:
  touch ~/.config/chezmoi/features/no-sway
  rm ~/.config/chezmoi/features/sway
  chezmoi apply

Currently sway packages are already installed on miche (existed before
this commit). Byte will get them via the new run_once_40 script.
Pis (rye, crouton) are unaffected — install script early-returns.
This commit is contained in:
Rain 2026-06-22 14:23:56 -04:00
parent fe73bbecba
commit 6bbaa8f2f5
21 changed files with 965 additions and 1 deletions

24
dot_config/sway/caffeine.sh Executable file
View file

@ -0,0 +1,24 @@
#!/bin/bash
# caffeine.sh — toggle caffeine mode by killing/restarting swayidle
# When ON: kill swayidle (no idle lock/screen-off)
# When OFF: restart swayidle with normal timeouts
WALLPAPER="$HOME/.local/share/wallpapers/teach-invaders-gruvbox.png"
FLAG="/tmp/caffeine-inhibit"
if [ -f "$FLAG" ]; then
# Turn OFF caffeine — restart swayidle
rm -f "$FLAG"
killall swayidle 2>/dev/null
swayidle -w \
timeout 300 "swaylock -f -i $WALLPAPER" \
timeout 600 'swaymsg "output * power off"' \
resume 'swaymsg "output * power on"' \
before-sleep "swaylock -f -i $WALLPAPER" &
notify-send -t 2000 "☕ Caffeine OFF" "Idle sleep enabled" 2>/dev/null
else
# Turn ON caffeine — kill swayidle
touch "$FLAG"
killall swayidle 2>/dev/null
notify-send -t 2000 "☕ Caffeine ON" "Idle sleep disabled" 2>/dev/null
fi

View file

@ -0,0 +1,48 @@
# === sway keybinds cheatsheet ===
# generated from ~/.config/sway/config — `Super` = the Windows key (Mod4)
# reload this view with: Super + /
# LAUNCHERS
Super+Enter open foot terminal
Super+D open wofi app launcher
Super+E open thunar (file manager)
Super+B open firefox
Super+/ show this cheatsheet (in foot, via less)
Super+Shift+E exit sway (with prompt)
# WINDOW MANAGEMENT
Super+Q kill focused window
Super+F fullscreen toggle
Super+V split toggle (vertical <-> horizontal)
Super+S stacking layout
Super+W tabbed layout
Super+Space floating toggle
# FOCUS / MOVE
Super+arrows focus window in direction
Super+Shift+arrows move window in direction
# WORKSPACES
Super+1 .. Super+0 switch to workspace N (0 = 10)
Super+Tab toggle back to previous workspace
Super+Shift+1..0 move focused window to workspace N
# SCREENSHOTS
Print region screenshot to file
Super+Print full-screen screenshot to file
Super+Shift+S region screenshot to clipboard
# SCRATCHPAD
Super+` toggle dropdown foot (sway scratchpad)
# RESIZE MODE (Super+R enters, Esc/Enter exits)
resize mode:
arrows shrink/grow 30px in direction
Esc / Enter exit resize mode
# LOCK / IDLE
Ctrl+Alt+L lock now
5 min idle auto-lock
10 min idle auto screen-off
# TIP: Super+Shift+C reloads this config without logging out.

4
dot_config/sway/cheatsheet.sh Executable file
View file

@ -0,0 +1,4 @@
#!/bin/bash
# cheatsheet.sh — opens the sway keybind cheatsheet in a new foot terminal
# running less. Press q to quit.
exec foot sh -c 'exec less -SRFX "$HOME/.config/sway/cheatsheet.md"'

148
dot_config/sway/config Normal file
View file

@ -0,0 +1,148 @@
# === ~/.config/sway/config ===
# MINIMAL — single file, no includes. Tier-1+2+3 in one go.
# We can split into includes later once this is proven to boot.
# === variables ===
set $mod Mod4
set $term foot
set $menu wofi --show drun --insensitive -p "launch"
set $browser zen
set $fileManager thunar
# === output / input ===
# Gruvbox wallpaper — sway's native bg command manages swaybg lifecycle internally
output * bg $HOME/.local/share/wallpapers/teach-invaders-gruvbox.png fill
# Touchpad: tap-to-click, natural scroll, drag
input type:touchpad {
tap enabled
tap_button_map lrm
natural_scroll enabled
dwt enabled
accel_profile adaptive
}
# === window borders (gruvbox-hard) ===
default_border pixel 2
default_floating_border pixel 2
gaps inner 8
gaps outer 4
smart_borders on
smart_gaps on
# gruvbox-orange focus border (replaces the default "generic blue")
client.focused #d79921 #1d2021 #ebdbb2 #d79921 #d79921
client.unfocused #3c3836 #1d2021 #a89984 #3c3836 #3c3836
client.focused_inactive #504945 #1d2021 #a89984 #504945 #504945
client.urgent #fb4934 #1d2021 #ebdbb2 #fb4934 #fb4934
# === swaybar (replaced by waybar — keeping as fallback) ===
# If waybar isn't installed yet, this provides a basic bar.
bar {
swaybar_command waybar
}
# === autostart (kept tiny, all stderr silenced) ===
# Make user-local themes/icons visible to GTK and Qt
exec sh -c 'export XDG_DATA_DIRS="$HOME/.local/share:$HOME/.local/share/flatpak/exports/share:/usr/local/share:/usr/share"; export XDG_DATA_HOME="$HOME/.local/share"; export XDG_CONFIG_HOME="$HOME/.config"; systemctl --user import-environment XDG_DATA_DIRS XDG_DATA_HOME XDG_CONFIG_HOME 2>/dev/null; dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP=sway'
exec /usr/libexec/polkit-gnome-authentication-agent-1
exec clipmenud
exec sh -c 'systemctl --user start xdg-desktop-portal xdg-desktop-portal-wlr 2>/dev/null || true'
exec sh -c 'gsettings set org.gnome.desktop.interface gtk-theme Gruvbox-Material-Dark-Hard 2>/dev/null; gsettings set org.gnome.desktop.interface icon-theme Gruvbox-Material-Dark 2>/dev/null; gsettings set org.gnome.desktop.interface color-scheme prefer-dark 2>/dev/null; true'
exec gnome-keyring-daemon --start --components=secrets
exec mako
exec wlsunset -l 39.96 -L -82.99
exec sworkstyle
# === idle / lock ===
exec swayidle -w \
timeout 300 '$HOME/.config/sway/lock-fancy.sh' \
timeout 600 'swaymsg "output * power off"' \
resume 'swaymsg "output * power on"' \
before-sleep '$HOME/.config/sway/lock-fancy.sh'
# === keybinds ===
bindsym $mod+Return exec $term
bindsym $mod+d exec $menu
bindsym $mod+e exec $fileManager
bindsym $mod+b exec $browser
bindsym $mod+q kill
bindsym $mod+Shift+c reload
bindsym Ctrl+Alt+l exec $HOME/.config/sway/lock-fancy.sh
bindsym $mod+slash exec $HOME/.config/sway/cheatsheet.sh
bindsym $mod+question exec $HOME/.config/sway/cheatsheet.sh
bindsym $mod+Shift+e exec swaynag -t warning -m 'Exit Sway?' -B 'Yes, exit Sway' 'swaymsg exit'
bindsym $mod+Left focus left
bindsym $mod+Down focus down
bindsym $mod+Up focus up
bindsym $mod+Right focus right
bindsym $mod+Shift+Left move left
bindsym $mod+Shift+Down move down
bindsym $mod+Shift+Up move up
bindsym $mod+Shift+Right move right
bindsym $mod+1 workspace number 1
bindsym $mod+2 workspace number 2
bindsym $mod+3 workspace number 3
bindsym $mod+4 workspace number 4
bindsym $mod+5 workspace number 5
bindsym $mod+6 workspace number 6
bindsym $mod+7 workspace number 7
bindsym $mod+8 workspace number 8
bindsym $mod+9 workspace number 9
bindsym $mod+0 workspace number 10
bindsym $mod+Tab workspace back_and_forth
bindsym $mod+Shift+1 move container to workspace number 1
bindsym $mod+Shift+2 move container to workspace number 2
bindsym $mod+Shift+3 move container to workspace number 3
bindsym $mod+Shift+4 move container to workspace number 4
bindsym $mod+Shift+5 move container to workspace number 5
bindsym $mod+Shift+6 move container to workspace number 6
bindsym $mod+Shift+7 move container to workspace number 7
bindsym $mod+Shift+8 move container to workspace number 8
bindsym $mod+Shift+9 move container to workspace number 9
bindsym $mod+Shift+0 move container to workspace number 10
bindsym $mod+f fullscreen
bindsym $mod+v split toggle splitv
bindsym $mod+s layout stacking
bindsym $mod+w layout tabbed
bindsym $mod+space exec $menu
bindsym $mod+Shift+space floating toggle
bindsym Print exec grim -g "$(slurp)" ~/Pictures/screenshot-$(date +%Y%m%d-%H%M%S).png
bindsym $mod+Print exec grim ~/Pictures/screenshot-$(date +%Y%m%d-%H%M%S).png
bindsym $mod+Shift+s exec grim -g "$(slurp)" - | wl-copy
mode "resize" {
bindsym Left resize shrink width 30px
bindsym Down resize grow height 30px
bindsym Up resize shrink height 30px
bindsym Right resize grow width 30px
bindsym Escape mode "default"
bindsym Return mode "default"
}
bindsym $mod+r mode "resize"
# === F12 dropdown terminal ===
# F12 opens a floating foot. Uses -a dropdown so for_window only floats
# the dropdown, not regular foot windows from Super+Enter.
# Close it with Super+Q or Ctrl+D.
bindsym F12 exec foot -a dropdown
bindsym $mod+grave exec foot -a dropdown
for_window [app_id="dropdown"] floating enable, sticky enable, resize set width 1100 height 600, move position center
# === media keys ===
bindsym --locked XF86AudioRaiseVolume exec wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+
bindsym --locked XF86AudioLowerVolume exec wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-
bindsym --locked XF86AudioMute exec wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle
bindsym --locked XF86AudioMicMute exec wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle
bindsym --locked XF86AudioPlay exec playerctl play-pause 2>/dev/null || true
bindsym --locked XF86AudioNext exec playerctl next 2>/dev/null || true
bindsym --locked XF86AudioPrev exec playerctl previous 2>/dev/null || true
bindsym --locked XF86MonBrightnessUp exec sh -c 'echo $(( $(cat /sys/class/backlight/acpi_video0/brightness) + 1 > $(cat /sys/class/backlight/acpi_video0/max_brightness) ? $(cat /sys/class/backlight/acpi_video0/max_brightness) : $(cat /sys/class/backlight/acpi_video0/brightness) + 1 )) | sudo tee /sys/class/backlight/acpi_video0/brightness'
bindsym --locked XF86MonBrightnessDown exec sh -c 'echo $(( $(cat /sys/class/backlight/acpi_video0/brightness) - 1 < 0 ? 0 : $(cat /sys/class/backlight/acpi_video0/brightness) - 1 )) | sudo tee /sys/class/backlight/acpi_video0/brightness'
# pointer / mouse — the gruvbox-material-gtk repo doesn't ship an xcursor theme,
# so leave the default alone. (Cursor shows fine on the default theme.)

View file

@ -0,0 +1,18 @@
# === ~/.config/sway/config.d/00-gruvbox.conf ===
# gruvbox material dark hard — wired through gsettings so Thunar, Firefox chrome,
# and any GTK3/4 app pick it up the moment sway starts.
# Run gsettings in a sh -c wrapper so we can pipe stderr to /dev/null.
# This is necessary because the very first exec block runs before dbus is
# always reachable, and gsettings prints a long bar-warning to stderr when
# it can't find the bus. We don't want the red bar to surface that.
exec_always {
gsettings set org.gnome.desktop.interface gtk-theme 'Gruvbox-Material-Dark-Hard' 2>/dev/null
gsettings set org.gnome.desktop.interface icon-theme 'Gruvbox-Material-Dark' 2>/dev/null
gsettings set org.gnome.desktop.interface cursor-theme 'Gruvbox-Material-Dark' 2>/dev/null
gsettings set org.gnome.desktop.interface color-scheme 'prefer-dark' 2>/dev/null
gsettings set org.cinnamon.desktop.interface gtk-theme 'Gruvbox-Material-Dark-Hard' 2>/dev/null
}
# gnome-keyring (in case any GTK app needs secret service for ssh-agent bridge)
exec gnome-keyring-daemon --start --components=secrets

View file

@ -0,0 +1,76 @@
# === ~/.config/sway/config.d/10-keybinds.conf ===
# sway keybindings. Mint+Cinnamon muscle memory → Super (Mod4).
# === essentials ===
bindsym $mod+Return exec $term
bindsym $mod+d exec $menu
bindsym $mod+e exec $fileManager
bindsym $mod+b exec $browser
bindsym $mod+q kill
bindsym $mod+Shift+c reload
bindsym $mod+Shift+e exec swaynag -t warning \
-m 'Exit Sway?' \
-B 'Yes, exit Sway' 'swaymsg exit'
# === move focus ===
bindsym $mod+Left focus left
bindsym $mod+Down focus down
bindsym $mod+Up focus up
bindsym $mod+Right focus right
bindsym $mod+Shift+Left move left
bindsym $mod+Shift+Down move down
bindsym $mod+Shift+Up move up
bindsym $mod+Shift+Right move right
# === workspace navigation ===
bindsym $mod+1 workspace number 1
bindsym $mod+2 workspace number 2
bindsym $mod+3 workspace number 3
bindsym $mod+4 workspace number 4
bindsym $mod+5 workspace number 5
bindsym $mod+6 workspace number 6
bindsym $mod+7 workspace number 7
bindsym $mod+8 workspace number 8
bindsym $mod+9 workspace number 9
bindsym $mod+0 workspace number 10
bindsym $mod+Tab workspace back_and_forth
bindsym $mod+Shift+1 move container to workspace number 1
bindsym $mod+Shift+2 move container to workspace number 2
bindsym $mod+Shift+3 move container to workspace number 3
bindsym $mod+Shift+4 move container to workspace number 4
bindsym $mod+Shift+5 move container to workspace number 5
bindsym $mod+Shift+6 move container to workspace number 6
bindsym $mod+Shift+7 move container to workspace number 7
bindsym $mod+Shift+8 move container to workspace number 8
bindsym $mod+Shift+9 move container to workspace number 9
bindsym $mod+Shift+0 move container to workspace number 10
# === layout ===
bindsym $mod+f fullscreen
bindsym $mod+v split toggle splitv
bindsym $mod+s layout stacking
bindsym $mod+w layout tabbed
bindsym $mod+space floating toggle
bindsym $mod+Shift+space focus mode_toggle
# === screenshot (grim + slurp) ===
bindsym Print exec grim -g "$(slurp)" ~/Pictures/screenshot-$(date +%Y%m%d-%H%M%S).png
bindsym $mod+Print exec grim ~/Pictures/screenshot-$(date +%Y%m%d-%H%M%S).png
bindsym $mod+Shift+s exec grim -g "$(slurp)" - | wl-copy
# === resize ===
mode "resize" {
bindsym Left resize shrink width 30px
bindsym Down resize grow height 30px
bindsym Up resize shrink height 30px
bindsym Right resize grow width 30px
bindsym Escape mode "default"
bindsym Return mode "default"
}
bindsym $mod+r mode "resize"
# === scratchpads (quick terminal / browser) ===
# Start a headless foot server, then a dropdown client window. Toggle with $mod+grave.
bindsym $mod+grave exec foot --server
bindsym $mod+apostrophe exec foot -a dropdown
for_window [app_id="dropdown"] floating enable, sticky enable, resize set width 1100 height 600, move position center

View file

@ -0,0 +1,15 @@
# === ~/.config/sway/config.d/20-autostart.conf ===
# background services started when sway comes up.
# polkit agent — needed for gparted, mount UIs in thunar, etc.
exec /usr/libexec/polkit-gnome-authentication-agent-1
# clipboard — start clipmenud directly (it watches the clipboard internally)
# Drop the wl-paste --watch wrapper to avoid bar noise if the socket isn't ready.
exec clipmenud
# xdg-desktop-portal — required for native file/screen sharing in Firefox/Chromium.
# Skip if services aren't enabled; the user can enable with:
# systemctl --user enable --now xdg-desktop-portal xdg-desktop-portal-wlr
# Until then, those systemctl lines are quiet no-ops.
exec sh -c 'systemctl --user start xdg-desktop-portal xdg-desktop-portal-wlr 2>/dev/null || true'

View file

@ -0,0 +1,17 @@
# ~/.config/sway/config.d/50-launchers.conf
# Launcher keybinds. The base $menu var in config is the drun launcher.
# These add run mode, window switcher, and the powermenu.
# $mod+grave already used by foot dropdown — don't clobber.
# $mod+Tab already used by workspace back_and_forth — don't clobber.
# $mod+slash is the cheatsheet — keep it.
# Exec ad-hoc command (not in $PATH discovery)
bindsym $mod+F12 exec wofi --show run --prompt "exec"
# Sway window switcher (replaces `swayr`-style overview)
bindsym $mod+F11 exec wofi --show window
# Power menu — dmenu mode driven by wofi-powermenu script.
# wofi has no custom-mode feature; the script does the dispatch.
bindsym $mod+Escape exec $HOME/.local/bin/wofi-powermenu

34
dot_config/sway/lock-fancy.sh Executable file
View file

@ -0,0 +1,34 @@
#!/bin/bash
# lock-fancy.sh — fancy swaylock: snapshot the screen, dim+blur it, and use as background.
# grim + ffmpeg (both already installed). No swaylock-effects overlay needed.
set -e
TMP=/tmp/lock-bg-483833.png
DIM=/tmp/lock-bg-dim-483833.png
# Snapshot the current screen
grim -t png "$TMP" 2>/dev/null
# Dim + blur via ffmpeg (much faster than ImageMagick)
if [ -f "$TMP" ] && command -v ffmpeg >/dev/null; then
ffmpeg -loglevel error -y -i "$TMP" -vf "boxblur=12:1,eq=brightness=-0.30:saturation=0.6" -frames:v 1 "$DIM"
IMG="$DIM"
else
# Fallback to the regular wallpaper
IMG="$HOME/.local/share/wallpapers/teach-invaders-gruvbox.png"
fi
# Launch swaylock
swaylock -f -i "$IMG" \
--indicator-radius 100 \
--indicator-thickness 6 \
--ring-color ebdbb2 \
--inside-color 1d202180 \
--key-hl-color fe8019 \
--bs-hl-color fb4934 \
--text-color ebdbb2 \
--separator-color 00000000 \
--fade-in 0.3
rm -f "$TMP" "$DIM" 2>/dev/null

View file

@ -0,0 +1,58 @@
#!/bin/bash
# toggle-dropdown.sh — toggle the dropdown foot terminal
# Writes debug info to /tmp/sway-f12.log so we can diagnose from SSH
LOG=/tmp/sway-f12.log
echo "=== $(date) F12/Super+grave pressed ===" >> "$LOG"
echo "WAYLAND_DISPLAY=$WAYLAND_DISPLAY" >> "$LOG"
echo "USER=$USER XDG_RUNTIME_DIR=$XDG_RUNTIME_DIR" >> "$LOG"
# Find the sway socket
SOCK=$(ls -t /run/user/$(id -u)/sway-ipc.*.sock 2>/dev/null | head -1)
echo "SOCK=$SOCK" >> "$LOG"
if [ -z "$SOCK" ]; then
echo "ERROR: no sway socket found" >> "$LOG"
notify-send -t 2000 "sway: no IPC socket" 2>>"$LOG" || true
exit 1
fi
# Check if a foot window is in the scratchpad (hidden)
HIDDEN=$(SWAYSOCK=$SOCK swaymsg -t get_tree 2>>"$LOG" | python3 -c "
import sys, json
try:
tree = json.load(sys.stdin)
def find_app(node, app_id):
if node.get('app_id') == app_id and node.get('name', '').startswith('__i3'):
return node
for c in node.get('nodes', []) + node.get('floating_nodes', []):
r = find_app(c, app_id)
if r is not None:
return r
return None
dd = find_app(tree, 'foot')
print('hidden' if dd is not None else 'none')
except Exception as e:
print(f'err:{e}')
" 2>>"$LOG")
echo "HIDDEN=$HIDDEN" >> "$LOG"
if [ "$HIDDEN" = "hidden" ]; then
SWAYSOCK=$SOCK swaymsg '[app_id="foot"] scratchpad show' >>"$LOG" 2>&1
echo "toggled: scratchpad show" >> "$LOG"
else
# Spawn a new foot. Pass through WAYLAND_DISPLAY explicitly.
if [ -n "$WAYLAND_DISPLAY" ]; then
WAYLAND_DISPLAY=$WAYLAND_DISPLAY foot >>"$LOG" 2>&1 &
else
# try to find wayland-1 socket
if [ -S "/run/user/$(id -u)/wayland-1" ]; then
WAYLAND_DISPLAY=wayland-1 foot >>"$LOG" 2>&1 &
else
foot >>"$LOG" 2>&1 &
fi
fi
echo "spawned foot" >> "$LOG"
fi
echo "=== done ===" >> "$LOG"

29
dot_config/sway/wifi-menu.sh Executable file
View file

@ -0,0 +1,29 @@
#!/bin/bash
# wifi-menu.sh — wofi-based WiFi network picker using nmcli
# Lists available networks, connects on selection.
# Get list of networks: SSID, signal, security
SELECTED=$(nmcli -t -f SSID,SIGNAL,SECURITY device wifi list --rescan yes 2>/dev/null | \
grep -v '^$' | \
sort -t: -k2 -nr | \
awk -F: '{
sig = $2 + 0
if (sig > 75) bars = "████"
else if (sig > 50) bars = "███░"
else if (sig > 25) bars = "██░░"
else bars = "█░░░"
sec = ($3 != "" && $3 != "--") ? " 󰌾" : ""
printf "%s %s%s\n", bars, $1, sec
}' | \
wofi --dmenu --insensitive --prompt "WiFi" -W 400 -H 500 2>/dev/null)
# Exit if nothing selected
[ -z "$SELECTED" ] && exit 0
# Extract SSID (everything after the signal bars + spaces)
SSID=$(echo "$SELECTED" | sed 's/^[█░ ]* //; s/ 🔒$//')
# Connect
nmcli device wifi connect "$SSID" 2>&1 | grep -q "successfully" && \
notify-send -t 3000 "WiFi" "Connected to $SSID" || \
notify-send -t 5000 "WiFi" "Failed to connect to $SSID"