From 1ec29059adf169270920181f4cf7d7173564188d Mon Sep 17 00:00:00 2001 From: Ryan Yin Date: Tue, 1 Sep 2026 22:32:19 +0800 Subject: [PATCH] fix(sunshine): stabilize remote Niri sessions --- hosts/idols-ai/niri-hardware.kdl | 4 +-- .../desktop/networking/remote-desktop.nix | 1 + .../networking/sunshine/remote-session.nu | 34 +++++++++++++++---- .../tests/idols-ai-gpu/expected.nix | 1 + .../x86_64-linux/tests/idols-ai-gpu/expr.nix | 1 + 5 files changed, 33 insertions(+), 8 deletions(-) diff --git a/hosts/idols-ai/niri-hardware.kdl b/hosts/idols-ai/niri-hardware.kdl index 849678ea..497cb676 100644 --- a/hosts/idols-ai/niri-hardware.kdl +++ b/hosts/idols-ai/niri-hardware.kdl @@ -47,9 +47,9 @@ output "Virtual-1" { // Created by vkms in hardware-intel.nix. Sunshine captures this output when // the NVIDIA-attached physical monitors are off. It remains available in // normal physical sessions, with minor idle GPU and framebuffer overhead. - scale 1 + scale 1.5 transform "normal" - mode "2560x1600@60" + mode "2560x1600" position x=0 y=0 } diff --git a/modules/nixos/desktop/networking/remote-desktop.nix b/modules/nixos/desktop/networking/remote-desktop.nix index 181f4c6e..5e9a7d2b 100644 --- a/modules/nixos/desktop/networking/remote-desktop.nix +++ b/modules/nixos/desktop/networking/remote-desktop.nix @@ -35,6 +35,7 @@ capSysAdmin = false; openFirewall = true; settings = { + capture = "wlr"; # pc - Only localhost may access the web ui # lan - Only LAN devices may access the web ui origin_web_ui_allowed = "pc"; diff --git a/modules/nixos/desktop/networking/sunshine/remote-session.nu b/modules/nixos/desktop/networking/sunshine/remote-session.nu index 2b1e4669..5630485f 100755 --- a/modules/nixos/desktop/networking/sunshine/remote-session.nu +++ b/modules/nixos/desktop/networking/sunshine/remote-session.nu @@ -85,18 +85,38 @@ def wait-ready [user: string, machine: string] { error make { msg: 'Remote session did not become ready within 30 seconds; inspect journalctl -u greetd-sunshine.service' } } -def wait-niri [user: string] { +def niri-socket [machine: string] { + let environment = (^systemctl --machine $machine --user show-environment | complete) + if $environment.exit_code != 0 { return '' } + + ($environment.stdout + | lines + | where { |line| $line starts-with 'NIRI_SOCKET=' } + | get 0? + | default '' + | str replace 'NIRI_SOCKET=' '') +} + +def wait-niri [user: string, machine: string] { for _ in 1..30 { let greetd = ((^systemctl is-active greetd-sunshine.service | complete).stdout | str trim) == 'active' let niri = ((^pgrep -u $user -x niri | complete).exit_code) == 0 - if $greetd and $niri { return } + let socket = (niri-socket $machine) + let ipc = if $socket == '' or not ($socket | path exists) { + false + } else { + with-env { NIRI_SOCKET: $socket } { + ((^niri msg outputs | complete).exit_code) == 0 + } + } + if $greetd and $niri and $ipc { return } sleep 1sec } - error make { msg: 'Niri did not become ready within 30 seconds; inspect journalctl -u greetd-sunshine.service' } + error make { msg: 'Niri IPC did not become ready within 30 seconds; inspect journalctl -u greetd-sunshine.service' } } def start-sunshine [machine: string] { - ^systemctl --machine $machine --user reset-failed sunshine.service + let _ = (^systemctl --machine $machine --user reset-failed sunshine.service | complete) ^systemctl --machine $machine --user start sunshine.service } @@ -110,7 +130,9 @@ def main [ let session_user = ($env.SUDO_USER? | default $user) let user_machine = (user-machine $session_user) # niri-session starts niri.service through the user manager. Run the - # compositor directly so greetd owns this temporary session. + # compositor directly so greetd owns this temporary session. This + # intentionally skips xdg-desktop-autostart.target, keeping XDG autostart + # apps such as browsers out of the remote session. let command = if $command == '' { '/run/current-system/sw/bin/niri --session' } else { $command } let runtime = (preflight $tty $command) if $check { @@ -144,7 +166,7 @@ def main [ $runtime.greetd --config $config) - wait-niri $session_user + wait-niri $session_user $user_machine start-sunshine $user_machine wait-ready $session_user $user_machine print 'Remote Niri and Sunshine session is ready.' diff --git a/outputs/x86_64-linux/tests/idols-ai-gpu/expected.nix b/outputs/x86_64-linux/tests/idols-ai-gpu/expected.nix index 13b77e7f..73fcb8fd 100644 --- a/outputs/x86_64-linux/tests/idols-ai-gpu/expected.nix +++ b/outputs/x86_64-linux/tests/idols-ai-gpu/expected.nix @@ -4,6 +4,7 @@ niriUsesIntelRenderer = true; sunshineUserHasInputAccess = false; sunshineHasSysAdmin = false; + sunshineUsesWlrCapture = "wlr"; sunshineStartsAfterNiri = true; sunshineWaitsForNiriOutput = true; } diff --git a/outputs/x86_64-linux/tests/idols-ai-gpu/expr.nix b/outputs/x86_64-linux/tests/idols-ai-gpu/expr.nix index 7c3063a6..45af6be8 100644 --- a/outputs/x86_64-linux/tests/idols-ai-gpu/expr.nix +++ b/outputs/x86_64-linux/tests/idols-ai-gpu/expr.nix @@ -13,6 +13,7 @@ in niriUsesIntelRenderer = lib.hasInfix "/dev/dri/by-path/pci-0000:00:02.0-render" niriHardware; sunshineUserHasInputAccess = builtins.elem "input" cfg.users.users.ryan.extraGroups; sunshineHasSysAdmin = cfg.services.sunshine.capSysAdmin; + sunshineUsesWlrCapture = cfg.services.sunshine.settings.capture; sunshineStartsAfterNiri = builtins.elem "niri.service" cfg.systemd.user.services.sunshine.after; sunshineWaitsForNiriOutput = lib.hasInfix "niri msg outputs" cfg.systemd.user.services.sunshine.preStart; }