Releases: guitaripod/remoteplay-display
Release list
1.1.0
Sunshine sessions are now first-class, and the dummy plug is no longer limited to the modes it happens to advertise.
Sunshine / Moonlight
Sunshine never creates Steam's steam-streaming-playback sink, so the daemon cannot see its sessions. It announces them instead — global_prep_cmd calls remoteplay-display on --owner sunshine / off --owner sunshine, and the SUNSHINE_CLIENT_WIDTH/HEIGHT/FPS it exports are exactly the hint the mode scorer wants, so Moonlight clients get per-client modes with no extra configuration.
Every layout now carries an owner. off --owner NAME only restores a layout with that tag, so the Steam daemon and Sunshine prep commands cannot unwind each other's work, while a bare off still forces everything back.
steam-app-launch <appid> lets Sunshine host Steam games. It blocks until the game exits by tracking SteamLaunch AppId=<id> on the reaper's command line, excluding the install-script evaluator that runs under the same banner first — without that exclusion the session ends a few seconds after launch, right before the game appears.
This is also the workaround for games that bind input to whichever pad is "player one": Sunshine's virtual gamepad exists before the app command runs, so the game finds a controller at startup. God of War Ragnarök needs exactly that.
Synthetic dummy EDID
dummy-edid builds and installs a replacement EDID for the plug. Nothing is receiving the signal, so there is no reason to accept modes chosen to look like a generic monitor. The default set targets streaming clients — 1920x1200@90 and @89, 2560x1600@90, 3840x2160@60 in RGB rather than YCbCr 4:2:0, 2560x1440@120, 1920x1080@120 and @90.
Every timing is retimed so its refresh rate is exactly integral. This matters more than it sounds: Sunshine samples the scanout on a timer, so a host running CVT's 89.91 Hz against a 90.000 fps capture clock has to skip a period roughly every 11 seconds. Removing that drift took measured capture jitter from 0.535 ms of standard deviation down to 0.204 ms with no interval more than 2 ms off period.
install-edid.sh enables remoteplay-dummy-edid@<connector>.service, which applies the EDID before display-manager.service. That ordering is the point — the override is a debugfs write, and doing it while a compositor is running reads as a hotplug and makes KWin re-lay-out every output.
Verified against the NVIDIA open kernel module 610.43.03, which honours the DRM EDID override. Check the result with modetest -M nvidia-drm -c rather than kscreen-doctor, which reports the compositor's cached mode list.
Mode selection
Two new scoring terms:
- Cadence — how close
refresh / client_fpsis to a whole number. Because Sunshine samples the scanout rather than the game's swapchain, a 119.88 Hz host feeding a 90 fps client repeats every fourth frame and judders at a steady 90 fps, untouchable by bitrate or codec. min_mode(default1920x1080) — never pick a host mode smaller than this. A game holding a saved render resolution keeps rendering an oversized swapchain when the host switches to the dummy, and the capture layer grabs that surface, so the stream shows a zoomed crop. The floor bounds how bad the mismatch can get.
on also accepts --client WxH@FPS to select a mode for a specific client explicitly.
Known limits
Steam cannot capture the desktop on KDE Wayland at all — only games, via its Vulkan layer. Everything else streams as Desktop Black Frame. Anything that needs a session running before a game launches has to go through Sunshine.
1.0.0
Routes Steam Remote Play sessions to a dummy HDMI plug on KDE Plasma Wayland, then restores the previous desktop layout exactly.
While a client is connected the dummy becomes the host's only output, at whatever mode best feeds that client. Real monitors go dark and their power state stops mattering — no more games rendering at 4K165 just to be scaled down to a Deck's 800p, and no more KWin re-laying-out your outputs underneath a running game when a display drops its link.
Session detection. Steam's streaming host loads a PipeWire null sink named steam-streaming-playback at session start and unloads it at session end. That sink is live system state rather than a log line, so it can be watched as an event and re-queried to recover from a missed event or a crash.
Per-client mode selection. Steam logs the client's capture ceiling a few milliseconds before the sink appears, so the dummy's mode is scored per client at session start — frame rate first, then aspect ratio, then resolution. A Steam Deck OLED asking for 1280x800@89 gets a 1920x1080@119.88 host mode.
Reliability. Layout changes are a single atomic kscreen-doctor transaction, verified by polling rather than assumed. Layouts are snapshotted as (width, height, refresh) and re-resolved to live mode ids at apply time, because KWin renumbers mode ids whenever connectors re-enumerate. Crash recovery reconciles stale state on start; SIGKILL and clean-stop paths both return the desktop. An idle/sleep inhibitor is held for the duration of a session so a blanked dummy can't turn the stream black. Idle costs no subprocesses.
Verified end to end on Arch, Plasma 6.7.3 Wayland, NVIDIA 610.43.03, streaming to a Steam Deck OLED — including the display-hotplug path.
See the README for install and configuration.