fix(linux): Add optional wlroots (wlr-virtual-pointer) backend for mouse input#4972
fix(linux): Add optional wlroots (wlr-virtual-pointer) backend for mouse input#4972Cm4nXD wants to merge 3 commits into
Conversation
Adds a configurable alternative to uinput for mouse input on Wayland. When enabled, mouse movement, buttons, and scroll are injected via the zwlr_virtual_pointer_v1 Wayland protocol instead of /dev/uinput. - Generate wlr-virtual-pointer-unstable-v1 bindings via wayland-scanner - New wl_mouse.h / wl_mouse.cpp implement the Wayland virtual pointer - input_raw_t initialises and owns the wl_mouse state when enabled - Each platf::mouse:: function routes through wl_mouse when active - config::input.wlr_virtual_mouse (default: false) controls the toggle - Linux-only checkbox added to the web UI Inputs tab - Locale strings added to all 22 locale files Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Use create_virtual_pointer_with_output() so that motion_absolute coordinates are interpreted in the captured output's local coordinate frame rather than the full global compositor space. Previously, without an output binding, absolute mouse events were mapped across all monitors in the compositor layout, causing inputs to split across the real and headless virtual displays in a nested sway session. The output is selected using the same index as config::video.capture so the input and capture sides always refer to the same physical output. Falls back to the unbound creation path if no wl_output globals are discovered. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
I'm also interested in this functionality and commenting for visibility as having zwlr_virtual_pointer_v1 as well as zwp_virtual_keyboard_v1 would allow for really cool multiple headless sessions on a single GPU with sunshine. As stated by the PR author this is currently impossible as the current |
There was a problem hiding this comment.
I notice your PR modifies localization files other than the default language (en). We do not accept contributions on GitHub for these files, but you can contribute on Crowdin. Please see https://docs.lizardbyte.dev/latest/developers/contributing.html#localization for more information.
| "${CMAKE_SOURCE_DIR}/src/platform/linux/wayland.cpp" | ||
| "${CMAKE_SOURCE_DIR}/src/platform/linux/input/wl_mouse.cpp") |
There was a problem hiding this comment.
| "${CMAKE_SOURCE_DIR}/src/platform/linux/wayland.cpp" | |
| "${CMAKE_SOURCE_DIR}/src/platform/linux/input/wl_mouse.cpp") | |
| "${CMAKE_SOURCE_DIR}/src/platform/linux/wayland.cpp" | |
| "${CMAKE_SOURCE_DIR}/src/platform/linux/input/wl_mouse.cpp" | |
| ) |
The whole list should probably be alphabetical as well.
| // ------------------------------------------------------------------------- | ||
| // Registry listener — binds zwlr_virtual_pointer_manager_v1 and wl_output | ||
| // ------------------------------------------------------------------------- | ||
|
|
||
| void | ||
| registry_global(void *data, wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { |
There was a problem hiding this comment.
Use doxygen style documentation block, instead of this AI style. Comment applies in multiple places.
NOTE: If already documented in header, do not need to document again in cpp files.
| /** All wl_output globals discovered during registry enumeration, in announcement order. */ | ||
| std::vector<wl_output *> outputs; | ||
| /** The output this pointer is bound to (nullptr if creation fell back to unbound). */ | ||
| wl_output *bound_output = nullptr; |
There was a problem hiding this comment.
For single line comments, use inline style: https://docs.lizardbyte.dev/projects/sunshine/master/md_third-party_2doxyconfig_2docs_2source__code.html#doxygen-comments




Description
This PR introduces an optional wlroots-based input backend for mouse events, allowing Sunshine to forward pointer input using the wlr-virtual-pointer protocol instead of the existing uinput path.
This is implemented as a runtime toggle, preserving full backward compatibility with the current behavior.
The primary motivation for this change is to support nested Wayland compositors, specifically:
Running a nested Sway session inside Gamescope (SteamOS GameMode)
Using a headless/virtual output to render secondary content (e.g., Wii U gamepad view from Cemu)
Streaming that headless output via Sunshine to a Moonlight client
In this configuration, the existing uinput backend causes incorrect behavior:
Input events are injected at the system level
The host compositor (Gamescope) receives the events
Gamescope is unaware of the nested compositor's virtual outputs
As a result, mouse input is mapped to the wrong display
By contrast, using wlr-virtual-pointer:
Injects input directly into the target Wayland compositor (Sway)
Allows proper interaction with headless / virtual outputs
Ensures mouse input maps correctly to the streamed surface
Currently this doesn't map keyboard inputs, but it does map to just the selected display using create_virtual_pointer_with_output, but I'd be willing to try.
I've worked very hard, and am perfectly willing to make any adjustments needed to implement this feature! I've never contributed to an open source project or tried to tackle anything even remotely this complicated before as far as coding. 😅
I did use AI to help me, if I hadn't I'm not sure I could even have attempted this. But I did test it on my system, and filmed a demo showing it in action.
Screenshot
Issues Fixed or Closed
I'm not sure....
Roadmap Issues
I'm not sure
Type of Change
Checklist
AI Usage
output.mp4