Files
dotfiles/hosts/mainframe/systemd/sway-headless.service
T
connor 8ec2955c2e Fix remote input: give the compositor a libinput backend
The stream rendered but ignored keyboard and mouse. Sunshine injects input
through uinput exclusively — it has no support for the Wayland virtual
keyboard/pointer protocols — so the compositor has to have a libinput
backend to see those devices. WLR_BACKENDS=headless creates only the
headless backend, so sway had zero input devices against the kernel's 24.

libinput needs a seat, and a systemd --user service belongs to no logind
session, so libseat needs seatd. seatd is now enabled on this host.

Its socket defaults to group `seat`, which does not help here: the user
manager's group list is fixed when it starts, Linger=yes means that is at
boot, and so adding connor to `seat` would not take effect until a reboot —
re-logging in changes nothing. The drop-in points seatd at `video`, a group
connor already holds for GPU access, which makes this deterministic instead
of dependent on group-propagation timing.

sway now reports 7 input devices including libvirtualhid Mouse and
Keyboard, which are Sunshine's.

hosts/mainframe/etc/ is a new tree for system-level config; install flags
it the same way it flags sbin.
2026-09-14 16:10:24 -04:00

34 lines
1.5 KiB
Desktop File

[Unit]
Description=Headless Wayland compositor for Sunshine
Documentation=https://docs.lizardbyte.dev/projects/sunshine/
ConditionHost=mainframe
[Service]
Type=simple
# headless for the virtual output, libinput so there is an input backend at
# all. With WLR_BACKENDS=headless alone wlroots creates no libinput backend,
# so the compositor has zero input devices and Sunshine's uinput keyboard and
# mouse are never picked up — the stream shows the desktop and ignores you.
#
# libinput needs a seat, and a systemd --user service sits in no logind
# session, so libseat falls back to seatd. That is why seatd.service is enabled
# on this host and connor is in the seat group.
Environment=WLR_BACKENDS=headless,libinput
Environment=LIBSEAT_BACKEND=seatd
# Do not fail to start when no input device is present yet; Sunshine creates
# its virtual ones only once a client connects.
Environment=WLR_LIBINPUT_NO_DEVICES=1
# Pin the compositor to the NVIDIA card (card0 = 01:00.0, renderD129), which is
# also what sunshine.conf names as its encoder. Without this wlroots picks the
# Intel iGPU and every captured frame crosses GPUs.
Environment=WLR_DRM_DEVICES=/dev/dri/card0
Environment=XDG_CURRENT_DESKTOP=sway
ExecStart=/usr/bin/sway -c %h/.config/sway/headless.conf
ExecStopPost=/usr/bin/systemctl --user stop sunshine.service
Restart=on-failure
RestartSec=5s
# Deliberately NOT WantedBy=default.target. The 3060 also holds the local LLM,
# and this stack costs ~220 MiB of VRAM. Start it with `stream on`; the
# idle-stop timer puts it away again.