diff --git a/nixos/flake.nix b/nixos/flake.nix index 95e367c1..248c6967 100644 --- a/nixos/flake.nix +++ b/nixos/flake.nix @@ -351,11 +351,9 @@ system = "aarch64-linux"; }; railbird-sf = { - specialArgs = let - containerInfo = mkOrgAgendaApiContainerInfo "x86_64-linux"; - in { - orgAgendaApiContainer = containerInfo.imageFile; - orgAgendaApiImageName = containerInfo.imageName; + specialArgs = { + orgAgendaApiContainer = null; + orgAgendaApiImageName = "localhost/org-agenda-api:colonelpanic-dbb1cb8-030a673"; }; }; }; diff --git a/nixos/machines/railbird-sf.nix b/nixos/machines/railbird-sf.nix index 346b4288..ea94ae01 100644 --- a/nixos/machines/railbird-sf.nix +++ b/nixos/machines/railbird-sf.nix @@ -1,4 +1,12 @@ -{ config, lib, pkgs, inputs, orgAgendaApiContainer ? null, orgAgendaApiImageName ? "org-agenda-api", ... }: +{ + config, + lib, + pkgs, + inputs, + orgAgendaApiContainer ? null, + orgAgendaApiImageName ? "localhost/org-agenda-api:colonelpanic-dbb1cb8-030a673", + ... +}: { imports = [ ../configuration.nix diff --git a/nixos/remote-hyprland.nix b/nixos/remote-hyprland.nix index cd54e463..1c64c750 100644 --- a/nixos/remote-hyprland.nix +++ b/nixos/remote-hyprland.nix @@ -7,13 +7,47 @@ let remoteHyprlandStartVnc = pkgs.writeShellScript "remote-hyprland-start-vnc" '' set -euo pipefail - export WAYLAND_DISPLAY=${cfg.socket} export XDG_CURRENT_DESKTOP=Hyprland export XDG_SESSION_DESKTOP=Hyprland export XDG_SESSION_TYPE=wayland + export LIBSEAT_BACKEND=seatd for _ in $(${pkgs.coreutils}/bin/seq 1 50); do - if ${hyprlandPackage}/bin/hyprctl -j monitors >/dev/null 2>&1; then + instance="$( + ${hyprlandPackage}/bin/hyprctl instances \ + | ${pkgs.gawk}/bin/awk ' + /^instance / { + sig = $2 + sub(/:$/, "", sig) + } + /^[[:space:]]*time:/ { time = $2 } + /^[[:space:]]*wl socket:/ { + if (sig != "" && time != "") { + print time " " sig " " $3 + } + } + ' \ + | ${pkgs.coreutils}/bin/sort -n \ + | ${pkgs.coreutils}/bin/tail -n 1 + )" + + if [ -n "$instance" ]; then + read -r _ HYPRLAND_INSTANCE_SIGNATURE WAYLAND_DISPLAY <&2 + exit 1 + fi + + for _ in $(${pkgs.coreutils}/bin/seq 1 50); do + if ${hyprlandPackage}/bin/hyprctl -i "$HYPRLAND_INSTANCE_SIGNATURE" -j monitors >/dev/null 2>&1; then break fi ${pkgs.coreutils}/bin/sleep 0.1 @@ -21,8 +55,8 @@ let # Give wayvnc a stable output name instead of relying on Hyprland's # fallback HEADLESS-* naming. - ${hyprlandPackage}/bin/hyprctl output create headless ${cfg.output} >/dev/null 2>&1 || true - ${hyprlandPackage}/bin/hyprctl keyword monitor '${monitorRule}' >/dev/null 2>&1 || true + ${hyprlandPackage}/bin/hyprctl -i "$HYPRLAND_INSTANCE_SIGNATURE" output create headless ${cfg.output} >/dev/null 2>&1 || true + ${hyprlandPackage}/bin/hyprctl -i "$HYPRLAND_INSTANCE_SIGNATURE" keyword monitor '${monitorRule}' >/dev/null 2>&1 || true exec ${pkgs.wayvnc}/bin/wayvnc \ --log-level=info \ @@ -100,35 +134,73 @@ let bind = $mainMod SHIFT, 8, movetoworkspace, 8 bind = $mainMod SHIFT, 9, movetoworkspace, 9 - exec-once = ${remoteHyprlandStartVnc} exec-once = ${cfg.terminalCommand} ''; + servicePath = lib.makeBinPath [ + pkgs.coreutils + pkgs.gnugrep + pkgs.gnused + pkgs.systemd + ]; + autostartInstall = lib.optionalAttrs cfg.autoStart { + Install = { + WantedBy = [ "default.target" ]; + }; + }; enabledModule = makeEnable config "myModules.remote-hyprland" false { myModules.hyprland.enable = true; + services.seatd = { + enable = true; + group = "video"; + }; + users.manageLingering = true; users.users.${cfg.user}.linger = true; environment.systemPackages = [ pkgs.wayvnc ]; - home-manager.users.${cfg.user}.systemd.user.services.remote-hyprland = { - Unit = { - Description = "Headless Hyprland session for remote VNC access"; - After = [ "default.target" ]; - }; - Service = { - ExecStart = "${hyprlandPackage}/bin/start-hyprland --path ${hyprlandPackage}/bin/Hyprland -- --socket ${cfg.socket} --config ${remoteHyprlandConfig}"; - Restart = "on-failure"; - RestartSec = 5; - Environment = [ - "XDG_CURRENT_DESKTOP=Hyprland" - "XDG_SESSION_DESKTOP=Hyprland" - "XDG_SESSION_TYPE=wayland" - "WAYLAND_DISPLAY=${cfg.socket}" - ]; - }; - Install = { - WantedBy = [ "default.target" ]; + home-manager.users.${cfg.user} = { + systemd.user.services = { + remote-hyprland = { + Unit = { + Description = "Headless Hyprland session for remote VNC access"; + After = [ "default.target" ]; + }; + Service = { + ExecStart = "${hyprlandPackage}/bin/start-hyprland --path ${hyprlandPackage}/bin/Hyprland -- --config ${remoteHyprlandConfig}"; + Restart = "on-failure"; + RestartSec = 5; + Environment = [ + "XDG_CURRENT_DESKTOP=Hyprland" + "XDG_SESSION_DESKTOP=Hyprland" + "XDG_SESSION_TYPE=wayland" + "LIBSEAT_BACKEND=seatd" + "PATH=${servicePath}" + ]; + }; + } // autostartInstall; + + remote-hyprland-wayvnc = { + Unit = { + Description = "VNC server for the headless Hyprland session"; + After = [ "remote-hyprland.service" ]; + Requires = [ "remote-hyprland.service" ]; + PartOf = [ "remote-hyprland.service" ]; + }; + Service = { + ExecStart = "${remoteHyprlandStartVnc}"; + Restart = "on-failure"; + RestartSec = 5; + Environment = [ + "XDG_CURRENT_DESKTOP=Hyprland" + "XDG_SESSION_DESKTOP=Hyprland" + "XDG_SESSION_TYPE=wayland" + "LIBSEAT_BACKEND=seatd" + "PATH=${servicePath}" + ]; + }; + } // autostartInstall; }; }; }; @@ -154,12 +226,6 @@ enabledModule // { description = "TCP port for wayvnc."; }; - socket = lib.mkOption { - type = lib.types.str; - default = "wayland-remote-hyprland"; - description = "Wayland socket name used by the remote Hyprland instance."; - }; - output = lib.mkOption { type = lib.types.str; default = "remote"; @@ -195,6 +261,16 @@ enabledModule // { default = "${pkgs.ghostty}/bin/ghostty --gtk-single-instance=false"; description = "Command launched for the default terminal binding and initial window."; }; + + autoStart = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + Whether to start the remote Hyprland session automatically with the + user's systemd manager. Keep this disabled on single-GPU hosts with + an active display manager, because Hyprland needs DRM master. + ''; + }; }; }; }