feat(sway): 4K scale 2, natural mouse scroll, idle inhibit for emulators
CI / flake (push) Skipped

Three unrelated desktop-side fixes, all in home/sway.nix.

Sway cannot key an output rule on resolution, so scale 2 on 4K displays
is applied by a user service that runs once at session start and again on
every output event, which is what makes it survive a display being
switched off and on. The scale guard in the jq filter stops the
subscription feeding itself. kanshi still owns mode and position; no
profile here sets a scale, so the two do not fight.

natural_scroll on type:pointer gives mice the same scroll direction as
the touchpads already had.

PCSX2 and Dolphin default to borderless-windowed rather than compositor
fullscreen, so the existing inhibit_idle fullscreen rules never matched
and swaylock engaged mid-game. Inhibit on focus for the emulators
instead. The Steam session is unaffected either way: swayidle is wanted
by graphical-session.target and conditional on WAYLAND_DISPLAY, and the
gamescope session provides neither.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Emma Thorpe
2026-09-01 10:19:30 +01:00
co-authored by Claude Opus 5
parent 0e10083ea4
commit 61a57f41e6
3 changed files with 75 additions and 2 deletions
+54
View File
@@ -30,6 +30,28 @@ let
- | ${pkgs.swappy}/bin/swappy -f -
'';
# Sway's config cannot express an output rule keyed on resolution, so scale is
# applied from outside it: once at session start, then on every output event,
# which is what makes it survive a display being unplugged or power-cycled.
# The `.scale != 2` guard stops the subscription feeding itself, since setting
# a scale is itself an output event.
scaleHidpi = pkgs.writeShellScript "sway-scale-hidpi" ''
apply() {
${pkgs.sway}/bin/swaymsg -r -t get_outputs \
| ${pkgs.jq}/bin/jq -r '.[]
| select(.active and .current_mode.width >= 3840 and .scale != 2)
| .name' \
| while read -r name; do
${pkgs.sway}/bin/swaymsg output "$name" scale 2
done
}
apply
${pkgs.sway}/bin/swaymsg -t subscribe -m '["output"]' | while read -r _; do
apply
done
'';
# 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";
@@ -60,6 +82,8 @@ in
tap = "enabled";
natural_scroll = "enabled";
};
# Mice (the MX Master 3 among them) scroll the same way as the touchpads.
input."type:pointer".natural_scroll = "enabled";
# Solid Catppuccin Mocha base as the wallpaper (no image dependency).
output."*".bg = "#${ctp.base} solid_color";
@@ -111,6 +135,18 @@ in
criteria.class = ".*";
command = "inhibit_idle fullscreen";
}
# Emulators default to borderless-windowed rather than compositor
# fullscreen, so the rules above never fire and swaylock cuts in
# mid-game. Inhibit on focus instead, which does not depend on how the
# application asked to fill the screen.
{
criteria.app_id = "(?i)^(pcsx2|dolphin-emu|retroarch|clone.?hero)";
command = "inhibit_idle focus";
}
{
criteria.class = "(?i)^(pcsx2|dolphin-emu|retroarch|clone.?hero)";
command = "inhibit_idle focus";
}
];
# Binding modes (submenus). Entered from keybindings below; each action
@@ -307,6 +343,24 @@ in
Install.WantedBy = [ "sway-session.target" ];
};
# Every 4K output runs at scale 2 (1920x1080 logical), on every host and
# whatever the display is called. kanshi below owns mode and position; this
# owns scale for the one case that is a fleet-wide rule rather than a
# per-display fact, and the two do not fight because no kanshi profile here
# sets a scale.
systemd.user.services.sway-scale-hidpi = {
Unit = {
Description = "Force scale 2 on 4K Sway outputs";
PartOf = [ "graphical-session.target" ];
After = [ "graphical-session.target" ];
};
Service = {
ExecStart = "${scaleHidpi}";
Restart = "on-failure";
};
Install.WantedBy = [ "sway-session.target" ];
};
# Output/display management. Reacts to hotplug and applies per-display
# mode/scale/position. Profiles are hardware-specific: the safe default below
# just enables the internal laptop panel; add docked/desktop profiles with the