feat(sway): port keybindings, modes and tweaks from dotfiles

Adapted from emmaisadev/dotfiles (sway/config.d) into the Nix config:

- Screenshots to swappy: Print = drag a region, Shift+Print = focused
  window (a writeShellScript reusing the grimshot.sh tree/jq logic).
  Replaces the old plain full-screen grim->file.
- Workspace cycle Mod+z / Mod+x (prev/next).
- Media keys (playerctl) and mic mute (wpctl source).
- Re-home `focus mode_toggle` onto Mod+Alt+space (Mod+Space is the
  launcher now).
- Clipboard history: services.clipman stores copies; Mod+c picks one
  through a Catppuccin-themed fuzzel (programs.fuzzel).
- Binding modes: a layout submenu (Mod+y -> s/w/e, which also restores
  split-toggle that Mod+e gave up to nemo) and a power menu
  (Mod+Shift+x -> lock/exit/sleep/reboot/shutdown). Mod+l still locks
  immediately.
- Touchpad tap + natural scroll (laptops; inert on desktop).
- Solid Catppuccin base as the wallpaper (output * bg, no image).
- foot: term=xterm-256color and scrollback 100000 (colours unchanged).
- swaywm.nix: add slurp/swappy/jq/playerctl to the session packages.

Skipped from the dotfiles: named workspaces + app auto-assign, the foot
--server/footclient setup, and pactl/MX-Master/lxqt device-specific bits.
All in the shared files, so every Sway host gets it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Emma Thorpe
2026-06-09 20:49:36 +01:00
parent 2cbcc3d7f1
commit b04391809e
2 changed files with 122 additions and 1 deletions
+118 -1
View File
@@ -19,6 +19,19 @@ let
# where a consumer needs it -- Sway/i3status/dunst want "#", foot/swaylock do
# not.
ctp = import ../catppuccin-mocha.nix;
# Focused-window screenshot -> swappy editor (the dotfiles' grimshot.sh logic).
# Full store paths so it needs nothing on PATH.
screenshotWindow = pkgs.writeShellScript "screenshot-window" ''
${pkgs.grim}/bin/grim -g "$(${pkgs.sway}/bin/swaymsg -t get_tree \
| ${pkgs.jq}/bin/jq -r '.. | select(.focused?) | .rect | "\(.x),\(.y) \(.width)x\(.height)"')" \
- | ${pkgs.swappy}/bin/swappy -f -
'';
# Binding-mode names. The string is both the `modes` attr key and what the
# bar's mode indicator shows, so the keys are spelled out in the label.
layoutMode = "layout: [s]tacking [w]tabbed [e]split";
systemMode = "system: [l]ock [e]xit [s]leep [r]eboot [Shift+s]shutdown";
in
{
wayland.windowManager.sway = {
@@ -39,6 +52,15 @@ in
xkb_layout = "us";
xkb_variant = "dvorak";
};
# Touchpads (laptops): tap-to-click and natural scrolling. Inert on the
# desktop hosts, which have no touchpad.
input."type:touchpad" = {
tap = "enabled";
natural_scroll = "enabled";
};
# Solid Catppuccin Mocha base as the wallpaper (no image dependency).
output."*".bg = "#${ctp.base} solid_color";
# Window borders -- Catppuccin Mocha (blue accent on the focused window).
colors = {
@@ -79,6 +101,33 @@ in
}
];
# Binding modes (submenus). Entered from keybindings below; each action
# returns to the default mode. mkOptionDefault-merged with the module's
# built-in "resize" mode.
modes = {
# Layout submenu (Mod+y). Mirrors Sway's default s/w/e layout keys --
# notably it restores split-toggle, which moved off Mod+e when that
# became the nemo launcher.
${layoutMode} = {
"s" = "layout stacking, mode default";
"w" = "layout tabbed, mode default";
"e" = "layout toggle split, mode default";
"Return" = "mode default";
"Escape" = "mode default";
};
# Power menu (Mod+Shift+x). Lock reuses the themed swaylock; the rest go
# through systemd/logind (allowed for the active local session).
${systemMode} = {
"l" = "exec ${pkgs.swaylock}/bin/swaylock -f, mode default";
"e" = "exec ${pkgs.sway}/bin/swaymsg exit, mode default";
"s" = "exec systemctl suspend, mode default";
"r" = "exec systemctl reboot, mode default";
"Shift+s" = "exec systemctl poweroff, mode default";
"Return" = "mode default";
"Escape" = "mode default";
};
};
bars = [
{
position = "top";
@@ -134,10 +183,39 @@ in
# File manager. mkForce overrides the module default (layout toggle split).
"${modifier}+e" = lib.mkForce "exec ${pkgs.nemo}/bin/nemo";
"${modifier}+l" = "exec ${pkgs.swaylock}/bin/swaylock -f";
"Print" = "exec ${pkgs.grim}/bin/grim ~/screenshot-$(date +%F-%H%M%S).png";
# Cycle workspaces.
"${modifier}+z" = "workspace prev";
"${modifier}+x" = "workspace next";
# focus mode_toggle (tiling <-> floating focus) -- re-homed off
# Mod+Space, which is now the launcher.
"${modifier}+Mod1+space" = "focus mode_toggle";
# Enter the binding-mode submenus defined above.
"${modifier}+y" = "mode \"${layoutMode}\"";
"${modifier}+Shift+x" = "mode \"${systemMode}\"";
# Clipboard history: pick a past entry through fuzzel (clipman stores
# it -- see services.clipman below).
"${modifier}+c" =
"exec ${pkgs.clipman}/bin/clipman pick -t CUSTOM --tool-args=\"${pkgs.fuzzel}/bin/fuzzel --dmenu\"";
# Screenshots -> swappy editor: Print = drag a region, Shift+Print =
# the focused window.
"Print" =
"exec ${pkgs.grim}/bin/grim -g \"$(${pkgs.slurp}/bin/slurp)\" - | ${pkgs.swappy}/bin/swappy -f -";
"Shift+Print" = "exec ${screenshotWindow}";
"XF86AudioRaiseVolume" = "exec ${pkgs.wireplumber}/bin/wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+";
"XF86AudioLowerVolume" = "exec ${pkgs.wireplumber}/bin/wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-";
"XF86AudioMute" = "exec ${pkgs.wireplumber}/bin/wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle";
"XF86AudioMicMute" = "exec ${pkgs.wireplumber}/bin/wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle";
# Media keys (MPRIS via playerctl).
"XF86AudioPlay" = "exec ${pkgs.playerctl}/bin/playerctl play-pause";
"XF86AudioNext" = "exec ${pkgs.playerctl}/bin/playerctl next";
"XF86AudioPrev" = "exec ${pkgs.playerctl}/bin/playerctl previous";
}
# Screen backlight: laptops only (no internal backlight on a desktop).
// lib.optionalAttrs portable {
@@ -158,6 +236,13 @@ in
# "<text> <cursor>" (man foot.ini(5): "ff0000 00ff00" => green cursor, red
# text). Only colors-dark is needed; we never set initial-color-theme=light.
settings = {
main = {
# Advertise as xterm-256color so remote hosts without foot's terminfo
# still behave (tmux re-adds foot's RGB/sync/etc. features -- see
# shell.nix). The [main] section is the unheadered top of foot.ini.
term = "xterm-256color";
};
scrollback.lines = 100000;
"colors-dark" = {
background = ctp.base;
foreground = ctp.text;
@@ -184,6 +269,38 @@ in
};
};
# Clipboard history: a user service runs `wl-paste --watch clipman store`,
# bound to the Wayland session, so copies persist and Mod+c (above) can pick
# an old entry through fuzzel.
services.clipman.enable = true;
# fuzzel: the dmenu picker used by clipman, themed Catppuccin Mocha to match
# (fuzzel colours are RRGGBBAA -- 8 hex digits).
programs.fuzzel = {
enable = true;
settings = {
main = {
font = "Noto Sans:size=12";
prompt = "\"clipboard \"";
};
border = {
width = 2;
radius = 8;
};
colors = {
background = "${ctp.base}f0";
text = "${ctp.text}ff";
prompt = "${ctp.subtext0}ff";
input = "${ctp.text}ff";
match = "${ctp.blue}ff";
selection = "${ctp.surface1}ff";
selection-text = "${ctp.text}ff";
selection-match = "${ctp.blue}ff";
border = "${ctp.blue}ff";
};
};
};
programs.swaylock = {
enable = true;
# Catppuccin Mocha (swaylock colours are hex without "#").
+4
View File
@@ -37,6 +37,10 @@ in
brightnessctl
foot
grim
slurp # region selection for screenshots
swappy # screenshot annotation/save
jq # used by the focused-window screenshot bind
playerctl # MPRIS media keys
sway-launcher-desktop
pavucontrol
];