diff --git a/hosts/example/home.nix b/hosts/example/home.nix index da8d856..823b43f 100644 --- a/hosts/example/home.nix +++ b/hosts/example/home.nix @@ -14,4 +14,17 @@ name = "Example User"; email = "user@example.com"; }; + + # Optional: give workspaces a home monitor (see modules/hyprland-workspaces.nix). + # Bind by connector name (`hyprctl monitors`), not panel identity, so any + # monitor on that port inherits the rules. `default:true` sets what a screen + # shows on connect. Absent monitors fall back, so a single-screen session still + # works. Left unset here — uncomment and adjust to your outputs. + # + # local.hyprland.workspaceRules = [ + # "1, monitor:HDMI-A-1, default:true" # external, workspaces 1-5 + # "2, monitor:HDMI-A-1" + # "6, monitor:LVDS-1, default:true" # laptop, workspaces 6-9 + # "7, monitor:LVDS-1" + # ]; } diff --git a/modules/home.nix b/modules/home.nix index 40f26b8..f8af0a9 100644 --- a/modules/home.nix +++ b/modules/home.nix @@ -81,7 +81,7 @@ let ''; in { - imports = [ ./gpg.nix ./media.nix ./texlive.nix ./librewolf.nix ./mc.nix ./mime.nix ]; + imports = [ ./gpg.nix ./media.nix ./texlive.nix ./librewolf.nix ./mc.nix ./mime.nix ./hyprland-workspaces.nix ]; # Put ~/.local/bin (the XDG user-bin dir) on PATH so hand-dropped scripts run # by name without wiring each through nix. @@ -578,6 +578,10 @@ in # master area width. Master layout uses `mfact`, not dwindle's `splitratio`. "$mod, h, layoutmsg, mfact -0.05" "$mod, l, layoutmsg, mfact +0.05" + # Move focus / the current workspace to the other monitor ("o" = other). + # With two monitors the relative +1 just toggles between them. + "$mod, o, focusmonitor, +1" + "$mod SHIFT, o, movecurrentworkspacetomonitor, +1" ] ++ ( # 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`. diff --git a/modules/hyprland-workspaces.nix b/modules/hyprland-workspaces.nix new file mode 100644 index 0000000..9d4d8e3 --- /dev/null +++ b/modules/hyprland-workspaces.nix @@ -0,0 +1,42 @@ +# Per-monitor "home" for workspaces. Each rule pins a workspace to a monitor +# connector, so it opens there and Hyprland moves it back when that monitor +# reappears after an unplug. The connector names are host hardware, so the rule +# list is an option a consumer sets from its own host config (see the example in +# `hosts/example/home.nix`), not baked in here; empty by default, so the example +# host binds nothing. +# +# Soft binding: when a bound monitor is absent the workspace just falls back to +# an active one — every workspace stays reachable, so a laptop-only (no external) +# session works unchanged. The `default` keyword marks the workspace a monitor +# shows on connect. +{ config, lib, pkgs, ... }: + +{ + options.local.hyprland.workspaceRules = lib.mkOption { + type = with lib.types; listOf str; + default = [ ]; + example = [ + "1, monitor:HDMI-A-1, default:true" + "6, monitor:LVDS-1, default:true" + ]; + description = '' + Hyprland `workspace` rules binding workspaces to monitor connectors, so + each workspace has a home screen. Connector names are host-specific — set + this from the inventory rather than here. + ''; + }; + + config.wayland.windowManager.hyprland.settings = { + workspace = config.local.hyprland.workspaceRules; + + # With a support monitor whose `default` workspace is high (e.g. a laptop + # holding 6-9), a solo session with that monitor as the only screen opens on + # that high workspace instead of 1. When rules are in effect, nudge a + # single-monitor startup back to workspace 1. No-op with no rules (1 is + # already the default). jq is pinned by store path so a consumer needn't + # have it installed; hyprctl is bare (a Hyprland session always has it, like + # the wallpaper exec-once assumes awww). + exec-once = lib.optional (config.local.hyprland.workspaceRules != [ ]) + "sh -c '[ \"$(hyprctl monitors -j | ${pkgs.jq}/bin/jq length)\" -eq 1 ] && hyprctl dispatch workspace 1'"; + }; +}