fix(sunshine): stabilize remote Niri sessions

This commit is contained in:
Ryan Yin
2026-09-01 22:32:19 +08:00
parent f766dcc24f
commit 1ec29059ad
5 changed files with 33 additions and 8 deletions
+2 -2
View File
@@ -47,9 +47,9 @@ output "Virtual-1" {
// Created by vkms in hardware-intel.nix. Sunshine captures this output when // Created by vkms in hardware-intel.nix. Sunshine captures this output when
// the NVIDIA-attached physical monitors are off. It remains available in // the NVIDIA-attached physical monitors are off. It remains available in
// normal physical sessions, with minor idle GPU and framebuffer overhead. // normal physical sessions, with minor idle GPU and framebuffer overhead.
scale 1 scale 1.5
transform "normal" transform "normal"
mode "2560x1600@60" mode "2560x1600"
position x=0 y=0 position x=0 y=0
} }
@@ -35,6 +35,7 @@
capSysAdmin = false; capSysAdmin = false;
openFirewall = true; openFirewall = true;
settings = { settings = {
capture = "wlr";
# pc - Only localhost may access the web ui # pc - Only localhost may access the web ui
# lan - Only LAN devices may access the web ui # lan - Only LAN devices may access the web ui
origin_web_ui_allowed = "pc"; origin_web_ui_allowed = "pc";
@@ -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' } 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 { for _ in 1..30 {
let greetd = ((^systemctl is-active greetd-sunshine.service | complete).stdout | str trim) == 'active' let greetd = ((^systemctl is-active greetd-sunshine.service | complete).stdout | str trim) == 'active'
let niri = ((^pgrep -u $user -x niri | complete).exit_code) == 0 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 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] { 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 ^systemctl --machine $machine --user start sunshine.service
} }
@@ -110,7 +130,9 @@ def main [
let session_user = ($env.SUDO_USER? | default $user) let session_user = ($env.SUDO_USER? | default $user)
let user_machine = (user-machine $session_user) let user_machine = (user-machine $session_user)
# niri-session starts niri.service through the user manager. Run the # 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 command = if $command == '' { '/run/current-system/sw/bin/niri --session' } else { $command }
let runtime = (preflight $tty $command) let runtime = (preflight $tty $command)
if $check { if $check {
@@ -144,7 +166,7 @@ def main [
$runtime.greetd $runtime.greetd
--config $config) --config $config)
wait-niri $session_user wait-niri $session_user $user_machine
start-sunshine $user_machine start-sunshine $user_machine
wait-ready $session_user $user_machine wait-ready $session_user $user_machine
print 'Remote Niri and Sunshine session is ready.' print 'Remote Niri and Sunshine session is ready.'
@@ -4,6 +4,7 @@
niriUsesIntelRenderer = true; niriUsesIntelRenderer = true;
sunshineUserHasInputAccess = false; sunshineUserHasInputAccess = false;
sunshineHasSysAdmin = false; sunshineHasSysAdmin = false;
sunshineUsesWlrCapture = "wlr";
sunshineStartsAfterNiri = true; sunshineStartsAfterNiri = true;
sunshineWaitsForNiriOutput = true; sunshineWaitsForNiriOutput = true;
} }
@@ -13,6 +13,7 @@ in
niriUsesIntelRenderer = lib.hasInfix "/dev/dri/by-path/pci-0000:00:02.0-render" niriHardware; niriUsesIntelRenderer = lib.hasInfix "/dev/dri/by-path/pci-0000:00:02.0-render" niriHardware;
sunshineUserHasInputAccess = builtins.elem "input" cfg.users.users.ryan.extraGroups; sunshineUserHasInputAccess = builtins.elem "input" cfg.users.users.ryan.extraGroups;
sunshineHasSysAdmin = cfg.services.sunshine.capSysAdmin; sunshineHasSysAdmin = cfg.services.sunshine.capSysAdmin;
sunshineUsesWlrCapture = cfg.services.sunshine.settings.capture;
sunshineStartsAfterNiri = builtins.elem "niri.service" cfg.systemd.user.services.sunshine.after; sunshineStartsAfterNiri = builtins.elem "niri.service" cfg.systemd.user.services.sunshine.after;
sunshineWaitsForNiriOutput = lib.hasInfix "niri msg outputs" cfg.systemd.user.services.sunshine.preStart; sunshineWaitsForNiriOutput = lib.hasInfix "niri msg outputs" cfg.systemd.user.services.sunshine.preStart;
} }