diff --git a/config/hypr/hyprland.conf b/config/hypr/hyprland.conf index 25401dc..c441654 100644 --- a/config/hypr/hyprland.conf +++ b/config/hypr/hyprland.conf @@ -2,20 +2,6 @@ # Monitor layout is host-specific; written by the flake per host. source = ~/.config/hypr/monitors.conf -# Virtual desktops plugin. A vdesk spans every monitor, so SUPER+1..5 switches -# all screens together instead of workspaces being pinned per monitor. -source = ~/.config/hypr/plugins.conf - -plugin { - virtual-desktops { - # The startup toast would fire on every login now that mako exists. - notifyinit = 0 - rememberlayout = size - # Deliberately NOT enabling cycleworkspaces: upstream states it does - # not work with more than 2 monitors, and pc has 3. - } -} - device { name = apple-spi-keyboard kb_options = caps:ctrl_modifier,grp:win_space_toggle @@ -62,6 +48,10 @@ dwindle { force_split = 2 } +# Apps don't get to maximize themselves: kitty (0.49) asks ~250ms after it +# maps, so every new terminal opened maximized. SUPER+F still works. +windowrule = match:class .*, suppress_event maximize + misc { disable_hyprland_logo = true disable_splash_rendering = true @@ -126,18 +116,28 @@ bind = SUPER SHIFT, j, movewindow, d bind = SUPER SHIFT, k, movewindow, u bind = SUPER SHIFT, l, movewindow, r -# -- Virtual desktops (plugin dispatchers, not Hyprland workspaces) -- -bind = SUPER, 1, vdesk, 1 -bind = SUPER, 2, vdesk, 2 -bind = SUPER, 3, vdesk, 3 -bind = SUPER, 4, vdesk, 4 -bind = SUPER, 5, vdesk, 5 +# -- Workspaces (AeroSpace-style) -- +# Each workspace lives on one monitor. SUPER+N shows N on its monitor and +# focuses it, leaving the other screens alone; a new workspace opens on the +# focused monitor. SUPER+ALT+h/l carries a workspace to another monitor. +bind = SUPER, 1, workspace, 1 +bind = SUPER, 2, workspace, 2 +bind = SUPER, 3, workspace, 3 +bind = SUPER, 4, workspace, 4 +bind = SUPER, 5, workspace, 5 -bind = SUPER SHIFT, 1, movetodesk, 1 -bind = SUPER SHIFT, 2, movetodesk, 2 -bind = SUPER SHIFT, 3, movetodesk, 3 -bind = SUPER SHIFT, 4, movetodesk, 4 -bind = SUPER SHIFT, 5, movetodesk, 5 +# Send the window without following it, like AeroSpace's move-node-to-workspace. +bind = SUPER SHIFT, 1, movetoworkspacesilent, 1 +bind = SUPER SHIFT, 2, movetoworkspacesilent, 2 +bind = SUPER SHIFT, 3, movetoworkspacesilent, 3 +bind = SUPER SHIFT, 4, movetoworkspacesilent, 4 +bind = SUPER SHIFT, 5, movetoworkspacesilent, 5 + +# AeroSpace's alt-tab / alt-shift-tab: previous workspace, and send the +# current workspace to the next monitor (+1 wraps past the last one). +bind = SUPER, Tab, workspace, previous +bind = SUPER SHIFT, Tab, movecurrentworkspacetomonitor, +1 +bind = SUPER ALT, Tab, movecurrentworkspacetomonitor, +1 # -- Move current workspace to another monitor -- bind = SUPER ALT, h, movecurrentworkspacetomonitor, l diff --git a/flake.lock b/flake.lock index fbe068a..55bd3df 100644 --- a/flake.lock +++ b/flake.lock @@ -78,26 +78,6 @@ "type": "github" } }, - "hyprland-virtual-desktops": { - "inputs": { - "nixpkgs": [ - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1790338297, - "narHash": "sha256-iJP+CyrS/fw/dqysRN9GtVB8GlJZcja4rkjBeJsXFsg=", - "owner": "levnikmyskin", - "repo": "hyprland-virtual-desktops", - "rev": "74b88ee5209c4dcea74eedc4646e185b02a8f946", - "type": "github" - }, - "original": { - "owner": "levnikmyskin", - "repo": "hyprland-virtual-desktops", - "type": "github" - } - }, "linear-tui-src": { "flake": false, "locked": { @@ -136,7 +116,6 @@ "apple-silicon": "apple-silicon", "firefox-addons": "firefox-addons", "home-manager": "home-manager", - "hyprland-virtual-desktops": "hyprland-virtual-desktops", "linear-tui-src": "linear-tui-src", "nixpkgs": "nixpkgs", "zen-browser": "zen-browser" @@ -152,11 +131,11 @@ ] }, "locked": { - "lastModified": 1786347712, - "narHash": "sha256-yH+jWXH2TVd5h3J5KoZiE8/4O4HqoggfB0zl9uVj39w=", + "lastModified": 1791266850, + "narHash": "sha256-F/scdmE9Y03TfSQWrBHaJWTRTr3rRQBqvJSDgF48g1c=", "owner": "0xc000022070", "repo": "zen-browser-flake", - "rev": "945efbc704b7f8c1731a922aabbc5d95edc9eb74", + "rev": "d9e4e25d6ade5d541074045d332d887c044e11f5", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index ac41cef..4650ea1 100644 --- a/flake.nix +++ b/flake.nix @@ -32,18 +32,9 @@ url = "github:k1-c/linear-tui/v0.13.0"; flake = false; }; - - # Virtual desktops: one desktop spans all monitors, replacing Hyprland's - # workspace-pinned-to-monitor model. Its own nixpkgs pin is older than - # ours, and Hyprland plugins are ABI-locked to the compositor, so it must - # follow ours to compile against the hyprland we actually run. - hyprland-virtual-desktops = { - url = "github:levnikmyskin/hyprland-virtual-desktops"; - inputs.nixpkgs.follows = "nixpkgs"; - }; }; - outputs = { self, nixpkgs, apple-silicon, home-manager, firefox-addons, zen-browser, linear-tui-src, hyprland-virtual-desktops, ... }: + outputs = { self, nixpkgs, apple-silicon, home-manager, firefox-addons, zen-browser, linear-tui-src, ... }: let # linear-tui, built from the pinned source input above. Upstream ships no # flake and it is not in nixpkgs, but it has a committed Cargo.lock, so @@ -93,15 +84,14 @@ monitor = desc:Lenovo Group Limited P27q-20 V909LPNY, 2560x1440@60, 2560x0, 1.0 monitor = desc:Lenovo Group Limited P27q-20 V909LRDD, 2560x1440@60, 5120x0, 1.0 + # Each screen's home workspace; SUPER+ALT+h/l can still move it. + workspace = 1, monitor:desc:Lenovo Group Limited P27q-20 V909LRFR, default:true + workspace = 2, monitor:desc:Lenovo Group Limited P27q-20 V909LPNY, default:true + workspace = 3, monitor:desc:Lenovo Group Limited P27q-20 V909LRDD, default:true + # Same panels, sized for 2560x1440 logical. windowrule = match:class ^(panel-wifi|panel-bt)$, float on, size 1792 1440, center on ''; - # Plugin load line, as a store path. Sourced from hyprland.conf, the - # same pattern as monitors.conf. Upstream installs the library as - # libvirtual-desktops.so (not lib.so). - "hypr/plugins.conf".text = '' - plugin = ${hyprland-virtual-desktops.packages.${pkgs.stdenv.hostPlatform.system}.virtual-desktops}/lib/libvirtual-desktops.so - ''; "eww".source = ./config/eww; "fuzzel".source = ./config/fuzzel; }; @@ -379,8 +369,25 @@ } ]; - networking.networkmanager.enable = true; - networking.networkmanager.wifi.backend = "iwd"; + # iwd owns Wi-Fi (driven from impala, which is iwd-only); networkd does + # DHCP and resolved does DNS. No NetworkManager: running it on top of + # iwd meant two managers with separate saved networks, which ended in + # "Secrets were required, but not provided". + networking.wireless.iwd.enable = true; + networking.useNetworkd = true; # DHCP on any ethernet/wifi by default + systemd.network.wait-online.anyInterface = true; # one link up is enough + + # iwd only reads /var/lib/iwd. C = copy once, so a password added to + # the file later (or via impala) is never overwritten. + systemd.tmpfiles.rules = [ + "C /var/lib/iwd/eduroam.8021x 0600 root root - ${pkgs.writeText "eduroam.8021x" '' + [Security] + EAP-Method=PEAP + EAP-Identity=447087@net.aau.dk + EAP-PEAP-Phase2-Method=MSCHAPV2 + EAP-PEAP-Phase2-Identity=447087@net.aau.dk + ''}" + ]; # Secret Service (org.freedesktop.secrets) provider, D-Bus-activated on # demand. Without it 1Password can't persist the 2FA device token @@ -515,7 +522,7 @@ users.users.mads = { isNormalUser = true; - extraGroups = [ "wheel" "networkmanager" "video" "docker" ]; + extraGroups = [ "wheel" "video" "docker" ]; shell = pkgs.zsh; initialPassword = "changeme"; # CHANGE after first boot (`passwd`) }; @@ -602,6 +609,7 @@ enable = true; nssmdns4 = true; }; + services.resolved.settings.Resolve.MulticastDNS = false; # avahi owns mDNS # Asahi kernel + Mesa cache, and the Raspberry Pi vendor # kernel/firmware cache (else both compile from source). @@ -626,6 +634,19 @@ ./hosts/pc/hardware-configuration.nix # generated on the PC ({ config, ... }: { networking.hostName = "pc"; + + # GigE camera link: static, never a route out. The segment also + # carries AAU's IPv6 RAs, and taking them made v6 traffic default + # out this port into a dead end. + networking.interfaces.enp7s0 = { + useDHCP = false; + ipv4.addresses = [ { address = "192.168.12.1"; prefixLength = 24; } ]; + }; + systemd.network.networks."40-enp7s0" = { + networkConfig.IPv6AcceptRA = false; + networkConfig.ConfigureWithoutCarrier = true; # camera may be off + linkConfig.RequiredForOnline = "no"; + }; boot.loader.systemd-boot.enable = true; boot.loader.efi.canTouchEfiVariables = true; # normal x86 UEFI