diff --git a/modules/home.nix b/modules/home.nix index b51a09f..fcd4e64 100644 --- a/modules/home.nix +++ b/modules/home.nix @@ -79,6 +79,47 @@ let 2) ${pkgs.systemd}/bin/systemctl poweroff ;; esac ''; + + # Swap the physical left/right placement of a two-monitor setup at runtime. + # Reflows the pair adjacent from x=0 (left one at origin, right one butted + # against its logical width) after reversing their order — so it stays + # gap-free regardless of differing widths, unlike a naive x-origin exchange + # (equal widths only). Machine-agnostic: reads whichever two connectors are + # live from `hyprctl -j`, no connector names. Ephemeral (hyprctl keyword) — + # the per-host default layout is restored on the next reload/relogin. No-op + # unless exactly two monitors are connected. + # + # Hyprland doesn't reliably re-notify layer-shell clients when an output is + # reconfigured at runtime (a known limitation — same class as the mirror/unplug + # cases where waybar needs a restart). Observed here: after the move, waybar and + # the wallpaper keep drawing at the pre-swap offsets, so the moved monitor's + # bar/bg land off-screen. Re-init both against the new geometry: waybar rebuilds + # its bars on SIGUSR2, and awww only paints outputs present when `awww img` last + # ran, so re-push the wallpaper symlink. Both are idempotent — harmless when a + # surface happened not to lag (which surface stales seems to race). + swapMonitors = pkgs.writeShellScript "hypr-swap-monitors" '' + set -eu + hc=${config.wayland.windowManager.hyprland.finalPackage}/bin/hyprctl + jq=${pkgs.jq}/bin/jq + data=$("$hc" monitors -j) + n=$(printf '%s' "$data" | "$jq" length) + if [ "$n" -ne 2 ]; then + ${pkgs.libnotify}/bin/notify-send "Monitor swap" "need exactly 2 monitors ($n connected)" + exit 0 + fi + # sort by current x → [left, right]; the new left is the old right. + cmd=$(printf '%s' "$data" | "$jq" -r ' + sort_by(.x) + | .[1] as $l | .[0] as $r + | (($l.width / $l.scale) | floor) as $off + | "keyword monitor \($l.name),preferred,0x0,\($l.scale)" + + " ; keyword monitor \($r.name),preferred,\($off)x0,\($r.scale)"') + "$hc" --batch "$cmd" + # re-anchor the layer-shell clients onto the new geometry (see comment above) + ${pkgs.procps}/bin/pkill -USR2 -x .waybar-wrapped || true + bg="$HOME/.local/share/bg" + [ -e "$bg" ] && ${pkgs.awww}/bin/awww img "$bg" || true + ''; in { imports = [ ./gpg.nix ./media.nix ./texlive.nix ./librewolf.nix ./mc.nix ./mime.nix ./hyprland-workspaces.nix ]; @@ -479,7 +520,10 @@ in configType = "hyprlang"; settings = { - monitor = ",preferred,auto,1"; + # Fallback for hosts that don't pin a layout; a two-monitor host overrides + # this with explicit positions (see the private inventory). mkDefault so + # that override is a plain value, not a mkForce fight. + monitor = lib.mkDefault ",preferred,auto,1"; # The qt module sets QT_QPA_PLATFORMTHEME=gtk3 (→ Qt apps read the GTK # Adwaita-dark theme), but only into hm-session-vars.sh + the systemd user @@ -582,6 +626,8 @@ in # With two monitors the relative +1 just toggles between them. "$mod, o, focusmonitor, +1" "$mod SHIFT, o, movecurrentworkspacetomonitor, +1" + # Swap the two monitors' left/right placement (see swapMonitors above). + "$mod CTRL, o, exec, ${swapMonitors}" ] ++ ( # Dynamic definitions for viewing workspaces 1 to 9 and moving windows between them. # References physical key codes for robustness with the SHIFT key, e.g. `code:10` instead of `1`.