From 716b64f1573ff77d33ae464620a3362bb6c3280f Mon Sep 17 00:00:00 2001 From: Christian Maxwell Date: Wed, 8 Apr 2026 12:27:43 -0400 Subject: [PATCH 1/2] feat(linux/input): add wlr-virtual-pointer mouse backend 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 --- cmake/compile_definitions/linux.cmake | 4 +- src/config.cpp | 2 + src/config.h | 2 + src/platform/linux/input/inputtino_common.h | 19 +- src/platform/linux/input/inputtino_mouse.cpp | 31 +++ src/platform/linux/input/wl_mouse.cpp | 205 ++++++++++++++++++ src/platform/linux/input/wl_mouse.h | 51 +++++ src_assets/common/assets/web/config.html | 1 + .../common/assets/web/configs/tabs/Inputs.vue | 9 + .../assets/web/public/assets/locale/bg.json | 2 + .../assets/web/public/assets/locale/cs.json | 2 + .../assets/web/public/assets/locale/de.json | 2 + .../assets/web/public/assets/locale/en.json | 2 + .../web/public/assets/locale/en_GB.json | 2 + .../web/public/assets/locale/en_US.json | 2 + .../assets/web/public/assets/locale/es.json | 2 + .../assets/web/public/assets/locale/fr.json | 2 + .../assets/web/public/assets/locale/hu.json | 2 + .../assets/web/public/assets/locale/it.json | 2 + .../assets/web/public/assets/locale/ja.json | 2 + .../assets/web/public/assets/locale/ko.json | 2 + .../assets/web/public/assets/locale/pl.json | 2 + .../assets/web/public/assets/locale/pt.json | 2 + .../web/public/assets/locale/pt_BR.json | 2 + .../assets/web/public/assets/locale/ru.json | 2 + .../assets/web/public/assets/locale/sv.json | 2 + .../assets/web/public/assets/locale/tr.json | 2 + .../assets/web/public/assets/locale/uk.json | 2 + .../assets/web/public/assets/locale/vi.json | 2 + .../assets/web/public/assets/locale/zh.json | 2 + .../web/public/assets/locale/zh_TW.json | 2 + 31 files changed, 366 insertions(+), 2 deletions(-) create mode 100644 src/platform/linux/input/wl_mouse.cpp create mode 100644 src/platform/linux/input/wl_mouse.h diff --git a/cmake/compile_definitions/linux.cmake b/cmake/compile_definitions/linux.cmake index c40c5460b3b..26c8061068b 100644 --- a/cmake/compile_definitions/linux.cmake +++ b/cmake/compile_definitions/linux.cmake @@ -206,6 +206,7 @@ if(WAYLAND_FOUND) GEN_WAYLAND("${WAYLAND_PROTOCOLS_DIR}" "unstable/xdg-output" xdg-output-unstable-v1) GEN_WAYLAND("${WAYLAND_PROTOCOLS_DIR}" "unstable/linux-dmabuf" linux-dmabuf-unstable-v1) GEN_WAYLAND("${CMAKE_SOURCE_DIR}/third-party/wlr-protocols" "unstable" wlr-screencopy-unstable-v1) + GEN_WAYLAND("${CMAKE_SOURCE_DIR}/third-party/wlr-protocols" "unstable" wlr-virtual-pointer-unstable-v1) include_directories( SYSTEM @@ -217,7 +218,8 @@ if(WAYLAND_FOUND) list(APPEND PLATFORM_TARGET_FILES "${CMAKE_SOURCE_DIR}/src/platform/linux/wlgrab.cpp" "${CMAKE_SOURCE_DIR}/src/platform/linux/wayland.h" - "${CMAKE_SOURCE_DIR}/src/platform/linux/wayland.cpp") + "${CMAKE_SOURCE_DIR}/src/platform/linux/wayland.cpp" + "${CMAKE_SOURCE_DIR}/src/platform/linux/input/wl_mouse.cpp") endif() # x11 diff --git a/src/config.cpp b/src/config.cpp index c45f55ba0db..5c1c3e7bd1f 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -567,6 +567,7 @@ namespace config { true, // always send scancodes true, // high resolution scrolling true, // native pen/touch support + false, // wlr_virtual_mouse (use uinput by default) }; sunshine_t sunshine { @@ -1297,6 +1298,7 @@ namespace config { bool_f(vars, "high_resolution_scrolling", input.high_resolution_scrolling); bool_f(vars, "native_pen_touch", input.native_pen_touch); + bool_f(vars, "wlr_virtual_mouse", input.wlr_virtual_mouse); bool_f(vars, "notify_pre_releases", sunshine.notify_pre_releases); bool_f(vars, "system_tray", sunshine.system_tray); diff --git a/src/config.h b/src/config.h index eb778a3ac68..9a5c37e5ea4 100644 --- a/src/config.h +++ b/src/config.h @@ -215,6 +215,8 @@ namespace config { bool high_resolution_scrolling; bool native_pen_touch; + + bool wlr_virtual_mouse; ///< Linux/Wayland only: use wlr-virtual-pointer protocol instead of uinput }; namespace flag { diff --git a/src/platform/linux/input/inputtino_common.h b/src/platform/linux/input/inputtino_common.h index 855f88a4186..de844f4451d 100644 --- a/src/platform/linux/input/inputtino_common.h +++ b/src/platform/linux/input/inputtino_common.h @@ -14,6 +14,7 @@ #include "src/logging.h" #include "src/platform/common.h" #include "src/utility.h" +#include "wl_mouse.h" using namespace std::literals; @@ -48,14 +49,30 @@ namespace platf { if (!keyboard) { BOOST_LOG(warning) << "Unable to create virtual keyboard: " << keyboard.getErrorMessage(); } +#ifdef SUNSHINE_BUILD_WAYLAND + if (config::input.wlr_virtual_mouse) { + if (!wl_mouse.init()) { + BOOST_LOG(error) << "Failed to initialize wlr-virtual-pointer; " + << "check WAYLAND_DISPLAY and compositor support"; + } + } +#endif } - ~input_raw_t() = default; + ~input_raw_t() { +#ifdef SUNSHINE_BUILD_WAYLAND + wl_mouse.destroy(); +#endif + } // All devices are wrapped in Result because it might be that we aren't able to create them (ex: udev permission denied) inputtino::Result mouse; inputtino::Result keyboard; +#ifdef SUNSHINE_BUILD_WAYLAND + platf::wl_mouse::state_t wl_mouse; +#endif + /** * A list of gamepads that are currently connected. * The pointer is shared because that state will be shared with background threads that deal with rumble and LED diff --git a/src/platform/linux/input/inputtino_mouse.cpp b/src/platform/linux/input/inputtino_mouse.cpp index f8d822de22c..0cb11fd0188 100644 --- a/src/platform/linux/input/inputtino_mouse.cpp +++ b/src/platform/linux/input/inputtino_mouse.cpp @@ -14,24 +14,43 @@ #include "src/logging.h" #include "src/platform/common.h" #include "src/utility.h" +#include "wl_mouse.h" using namespace std::literals; namespace platf::mouse { void move(input_raw_t *raw, int deltaX, int deltaY) { +#ifdef SUNSHINE_BUILD_WAYLAND + if (config::input.wlr_virtual_mouse && raw->wl_mouse.pointer) { + platf::wl_mouse::move(&raw->wl_mouse, deltaX, deltaY); + return; + } +#endif if (raw->mouse) { (*raw->mouse).move(deltaX, deltaY); } } void move_abs(input_raw_t *raw, const touch_port_t &touch_port, float x, float y) { +#ifdef SUNSHINE_BUILD_WAYLAND + if (config::input.wlr_virtual_mouse && raw->wl_mouse.pointer) { + platf::wl_mouse::move_abs(&raw->wl_mouse, x, y, touch_port.width, touch_port.height); + return; + } +#endif if (raw->mouse) { (*raw->mouse).move_abs(x, y, touch_port.width, touch_port.height); } } void button(input_raw_t *raw, int button, bool release) { +#ifdef SUNSHINE_BUILD_WAYLAND + if (config::input.wlr_virtual_mouse && raw->wl_mouse.pointer) { + platf::wl_mouse::button(&raw->wl_mouse, button, release); + return; + } +#endif if (raw->mouse) { inputtino::Mouse::MOUSE_BUTTON btn_type; switch (button) { @@ -63,12 +82,24 @@ namespace platf::mouse { } void scroll(input_raw_t *raw, int high_res_distance) { +#ifdef SUNSHINE_BUILD_WAYLAND + if (config::input.wlr_virtual_mouse && raw->wl_mouse.pointer) { + platf::wl_mouse::scroll(&raw->wl_mouse, high_res_distance); + return; + } +#endif if (raw->mouse) { (*raw->mouse).vertical_scroll(high_res_distance); } } void hscroll(input_raw_t *raw, int high_res_distance) { +#ifdef SUNSHINE_BUILD_WAYLAND + if (config::input.wlr_virtual_mouse && raw->wl_mouse.pointer) { + platf::wl_mouse::hscroll(&raw->wl_mouse, high_res_distance); + return; + } +#endif if (raw->mouse) { (*raw->mouse).horizontal_scroll(high_res_distance); } diff --git a/src/platform/linux/input/wl_mouse.cpp b/src/platform/linux/input/wl_mouse.cpp new file mode 100644 index 00000000000..fb628216eba --- /dev/null +++ b/src/platform/linux/input/wl_mouse.cpp @@ -0,0 +1,205 @@ +/** + * @file src/platform/linux/input/wl_mouse.cpp + * @brief Definitions for wlr-virtual-pointer mouse input handling. + * + * Implements mouse movement, button, and scroll injection using the + * wlr-virtual-pointer-unstable-v1 Wayland protocol. Only compiled when + * SUNSHINE_BUILD_WAYLAND is defined (i.e. Wayland dev libraries are present). + */ + +#ifdef SUNSHINE_BUILD_WAYLAND + +// standard includes +#include + +// lib includes +#include + +// generated protocol bindings (produced by wayland-scanner at cmake configure time) +#include + +// local includes +#include "src/config.h" +#include "src/logging.h" +#include "src/platform/common.h" +#include "wl_mouse.h" + +namespace platf::wl_mouse { + + namespace { + + /** + * Return a monotonic timestamp in milliseconds, as required by Wayland + * input event timestamps. + */ + uint32_t + now_ms() { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return (uint32_t) (ts.tv_sec * 1000 + ts.tv_nsec / 1'000'000); + } + + // ------------------------------------------------------------------------- + // Registry listener — binds zwlr_virtual_pointer_manager_v1 when found + // ------------------------------------------------------------------------- + + void + registry_global(void *data, wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { + auto *state = reinterpret_cast(data); + if (strcmp(interface, zwlr_virtual_pointer_manager_v1_interface.name) == 0) { + uint32_t bind_version = std::min(version, (uint32_t) 2); + state->manager = reinterpret_cast( + wl_registry_bind(registry, name, &zwlr_virtual_pointer_manager_v1_interface, bind_version)); + } + } + + void + registry_global_remove(void * /*data*/, wl_registry * /*registry*/, uint32_t /*name*/) { + // nothing to do + } + + constexpr wl_registry_listener registry_listener = { + .global = registry_global, + .global_remove = registry_global_remove, + }; + + } // anonymous namespace + + // --------------------------------------------------------------------------- + // state_t lifecycle + // --------------------------------------------------------------------------- + + bool + state_t::init() { + display = wl_display_connect(nullptr); + if (!display) { + BOOST_LOG(error) << "wl_mouse: failed to connect to Wayland display (is WAYLAND_DISPLAY set?)"; + return false; + } + + registry = wl_display_get_registry(display); + wl_registry_add_listener(registry, ®istry_listener, this); + wl_display_roundtrip(display); // discover globals + + if (!manager) { + BOOST_LOG(error) << "wl_mouse: compositor does not support zwlr_virtual_pointer_manager_v1; " + << "ensure you are running a wlroots-based compositor (sway, Hyprland, labwc, …)"; + destroy(); + return false; + } + + // NULL seat = compositor picks the default seat + pointer = zwlr_virtual_pointer_manager_v1_create_virtual_pointer(manager, nullptr); + wl_display_roundtrip(display); // finish device creation + + BOOST_LOG(info) << "wl_mouse: virtual pointer created via wlr-virtual-pointer protocol"; + return true; + } + + void + state_t::destroy() { + if (pointer) { + zwlr_virtual_pointer_v1_destroy(pointer); + pointer = nullptr; + } + if (manager) { + zwlr_virtual_pointer_manager_v1_destroy(manager); + manager = nullptr; + } + if (registry) { + wl_registry_destroy(registry); + registry = nullptr; + } + if (display) { + wl_display_disconnect(display); + display = nullptr; + } + } + + // --------------------------------------------------------------------------- + // Mouse operations + // --------------------------------------------------------------------------- + + void + move(state_t *state, int deltaX, int deltaY) { + zwlr_virtual_pointer_v1_motion(state->pointer, now_ms(), + wl_fixed_from_int(deltaX), wl_fixed_from_int(deltaY)); + zwlr_virtual_pointer_v1_frame(state->pointer); + wl_display_flush(state->display); + } + + void + move_abs(state_t *state, float x, float y, uint32_t width, uint32_t height) { + // Protocol expects unsigned fixed-point coordinates in the range [0, extent) + zwlr_virtual_pointer_v1_motion_absolute(state->pointer, now_ms(), + (uint32_t) x, (uint32_t) y, width, height); + zwlr_virtual_pointer_v1_frame(state->pointer); + wl_display_flush(state->display); + } + + void + button(state_t *state, int btn, bool release) { + // Map Sunshine button constants to Linux evdev BTN_* codes + uint32_t code; + switch (btn) { + case BUTTON_LEFT: + code = 0x110; // BTN_LEFT + break; + case BUTTON_RIGHT: + code = 0x111; // BTN_RIGHT + break; + case BUTTON_MIDDLE: + code = 0x112; // BTN_MIDDLE + break; + case BUTTON_X1: + code = 0x113; // BTN_SIDE + break; + case BUTTON_X2: + code = 0x114; // BTN_EXTRA + break; + default: + BOOST_LOG(warning) << "wl_mouse: unknown button: " << btn; + return; + } + + uint32_t state_val = release ? WL_POINTER_BUTTON_STATE_RELEASED : WL_POINTER_BUTTON_STATE_PRESSED; + zwlr_virtual_pointer_v1_button(state->pointer, now_ms(), code, state_val); + zwlr_virtual_pointer_v1_frame(state->pointer); + wl_display_flush(state->display); + } + + void + scroll(state_t *state, int high_res_distance) { + // Sunshine uses 120 units per detent (same as Windows HID). + // Convert to a pixel-like wl_fixed_t value; 15 px per detent is a common desktop default. + wl_fixed_t value = wl_fixed_from_double(high_res_distance / 120.0 * 15.0); + int32_t discrete = high_res_distance / 120; // whole detents + + zwlr_virtual_pointer_v1_axis_source(state->pointer, WL_POINTER_AXIS_SOURCE_WHEEL); + zwlr_virtual_pointer_v1_axis(state->pointer, now_ms(), WL_POINTER_AXIS_VERTICAL_SCROLL, value); + if (discrete != 0) { + zwlr_virtual_pointer_v1_axis_discrete(state->pointer, now_ms(), + WL_POINTER_AXIS_VERTICAL_SCROLL, value, discrete); + } + zwlr_virtual_pointer_v1_frame(state->pointer); + wl_display_flush(state->display); + } + + void + hscroll(state_t *state, int high_res_distance) { + wl_fixed_t value = wl_fixed_from_double(high_res_distance / 120.0 * 15.0); + int32_t discrete = high_res_distance / 120; + + zwlr_virtual_pointer_v1_axis_source(state->pointer, WL_POINTER_AXIS_SOURCE_WHEEL); + zwlr_virtual_pointer_v1_axis(state->pointer, now_ms(), WL_POINTER_AXIS_HORIZONTAL_SCROLL, value); + if (discrete != 0) { + zwlr_virtual_pointer_v1_axis_discrete(state->pointer, now_ms(), + WL_POINTER_AXIS_HORIZONTAL_SCROLL, value, discrete); + } + zwlr_virtual_pointer_v1_frame(state->pointer); + wl_display_flush(state->display); + } + +} // namespace platf::wl_mouse + +#endif // SUNSHINE_BUILD_WAYLAND diff --git a/src/platform/linux/input/wl_mouse.h b/src/platform/linux/input/wl_mouse.h new file mode 100644 index 00000000000..55b3cc56bd3 --- /dev/null +++ b/src/platform/linux/input/wl_mouse.h @@ -0,0 +1,51 @@ +/** + * @file src/platform/linux/input/wl_mouse.h + * @brief Declarations for wlr-virtual-pointer mouse input handling. + */ +#pragma once + +#ifdef SUNSHINE_BUILD_WAYLAND + +// lib includes +#include + +// forward-declare generated protocol types so the header compiles without +// including the full generated header (which is only available at build time) +struct zwlr_virtual_pointer_manager_v1; +struct zwlr_virtual_pointer_v1; + +namespace platf::wl_mouse { + + /** + * Holds the Wayland connection and virtual pointer handles for the + * wlr-virtual-pointer-unstable-v1 protocol. + * + * One instance lives inside input_raw_t, initialized only when + * config::input.wlr_virtual_mouse is true. + */ + struct state_t { + wl_display *display = nullptr; + wl_registry *registry = nullptr; + zwlr_virtual_pointer_manager_v1 *manager = nullptr; + zwlr_virtual_pointer_v1 *pointer = nullptr; + + /** + * Connect to WAYLAND_DISPLAY, discover zwlr_virtual_pointer_manager_v1 + * via the registry, and create a virtual pointer device. + * @return true on success, false if the compositor does not support the protocol. + */ + bool init(); + + /** Release all Wayland resources and disconnect. */ + void destroy(); + }; + + void move(state_t *state, int deltaX, int deltaY); + void move_abs(state_t *state, float x, float y, uint32_t width, uint32_t height); + void button(state_t *state, int button, bool release); + void scroll(state_t *state, int high_res_distance); + void hscroll(state_t *state, int high_res_distance); + +} // namespace platf::wl_mouse + +#endif // SUNSHINE_BUILD_WAYLAND diff --git a/src_assets/common/assets/web/config.html b/src_assets/common/assets/web/config.html index b90a62145e3..0e96cd045f2 100644 --- a/src_assets/common/assets/web/config.html +++ b/src_assets/common/assets/web/config.html @@ -212,6 +212,7 @@

{{ $t('config.configuration') }}

"mouse": "enabled", "high_resolution_scrolling": "enabled", "native_pen_touch": "enabled", + "wlr_virtual_mouse": "disabled", "keybindings": "[0x10,0xA0,0x11,0xA2,0x12,0xA4]", // todo: add this to UI }, }, diff --git a/src_assets/common/assets/web/configs/tabs/Inputs.vue b/src_assets/common/assets/web/configs/tabs/Inputs.vue index 7fa76a20721..e502e50af4d 100644 --- a/src_assets/common/assets/web/configs/tabs/Inputs.vue +++ b/src_assets/common/assets/web/configs/tabs/Inputs.vue @@ -181,6 +181,15 @@ const config = ref(props.config) v-model="config.native_pen_touch" default="true" > + + + diff --git a/src_assets/common/assets/web/public/assets/locale/bg.json b/src_assets/common/assets/web/public/assets/locale/bg.json index 04ca9b7c857..0074ee9577e 100644 --- a/src_assets/common/assets/web/public/assets/locale/bg.json +++ b/src_assets/common/assets/web/public/assets/locale/bg.json @@ -282,6 +282,8 @@ "mouse_desc": "Позволява на клиентите да управляват отдалечения компютър с мишка", "native_pen_touch": "Собствена поддръжка на писалка/докосване", "native_pen_touch_desc": "Когато е включено, Sunshine просто ще препредава командите идващи от писалка/докосване както са получени от клиентите използващи Moonlight. Може да е по-добре това да бъде изключено за по-старите приложения, които няма собствена поддръжка на писалка/докосване.", + "wlr_virtual_mouse": "WLR Virtual Mouse", + "wlr_virtual_mouse_desc": "Use the wlr-virtual-pointer Wayland protocol for mouse input instead of uinput. Requires a wlroots-based compositor (e.g. sway, Hyprland, labwc). Only change this if you have issues with uinput mouse input under Wayland.", "notify_pre_releases": "Известия за предварителни версии", "notify_pre_releases_desc": "Дали да бъдете уведомявани за нови предварителни версии на Sunshine, преди превръщането им в официални", "nvenc_h264_cavlc": "Предпочитане на CAVLC пред CABAC за H.264", diff --git a/src_assets/common/assets/web/public/assets/locale/cs.json b/src_assets/common/assets/web/public/assets/locale/cs.json index ec1f214a5dc..0d79ccf2e33 100644 --- a/src_assets/common/assets/web/public/assets/locale/cs.json +++ b/src_assets/common/assets/web/public/assets/locale/cs.json @@ -282,6 +282,8 @@ "mouse_desc": "Umožňuje hostům ovládat systém pomocí myši", "native_pen_touch": "Nativní podpora pera/dotyku", "native_pen_touch_desc": "Je-li tato funkce povolena, bude Sunshine předávat nativní události pera/dotyku z klientů Moonlight. To může být užitečné vypnout pro starší aplikace bez nativní podpory pera/dotyku.", + "wlr_virtual_mouse": "WLR Virtual Mouse", + "wlr_virtual_mouse_desc": "Use the wlr-virtual-pointer Wayland protocol for mouse input instead of uinput. Requires a wlroots-based compositor (e.g. sway, Hyprland, labwc). Only change this if you have issues with uinput mouse input under Wayland.", "notify_pre_releases": "Oznámení před vydáním", "notify_pre_releases_desc": "Zda chcete být informováni o nových předběžných verzích Sunshine", "nvenc_h264_cavlc": "Preferovat CAVLC před CABAC v H.264", diff --git a/src_assets/common/assets/web/public/assets/locale/de.json b/src_assets/common/assets/web/public/assets/locale/de.json index 361c37ce7ae..f88e61425d8 100644 --- a/src_assets/common/assets/web/public/assets/locale/de.json +++ b/src_assets/common/assets/web/public/assets/locale/de.json @@ -282,6 +282,8 @@ "mouse_desc": "Erlaubt Gästen das Host-System mit der Maus zu steuern", "native_pen_touch": "Native Pen/Touch Unterstützung", "native_pen_touch_desc": "Wenn aktiviert, durchläuft Sunshine natives Pen / Berühren von Moonlight-Clients. Dies kann nützlich sein, um ältere Anwendungen ohne nativen Stift-/Berührungs-Support zu deaktivieren.", + "wlr_virtual_mouse": "WLR Virtual Mouse", + "wlr_virtual_mouse_desc": "Use the wlr-virtual-pointer Wayland protocol for mouse input instead of uinput. Requires a wlroots-based compositor (e.g. sway, Hyprland, labwc). Only change this if you have issues with uinput mouse input under Wayland.", "notify_pre_releases": "Pre-Release-Benachrichtigungen", "notify_pre_releases_desc": "Ob über neue Versionen von Sunshine benachrichtigt werden soll", "nvenc_h264_cavlc": "CAVLC gegenüber CABAC in H.264 bevorzugen", diff --git a/src_assets/common/assets/web/public/assets/locale/en.json b/src_assets/common/assets/web/public/assets/locale/en.json index 53463b78d1e..fbd47ea36c5 100644 --- a/src_assets/common/assets/web/public/assets/locale/en.json +++ b/src_assets/common/assets/web/public/assets/locale/en.json @@ -285,6 +285,8 @@ "mouse_desc": "Allows guests to control the host system with the mouse", "native_pen_touch": "Native Pen/Touch Support", "native_pen_touch_desc": "When enabled, Sunshine will pass through native pen/touch events from Moonlight clients. This can be useful to disable for older applications without native pen/touch support.", + "wlr_virtual_mouse": "WLR Virtual Mouse", + "wlr_virtual_mouse_desc": "Use the wlr-virtual-pointer Wayland protocol for mouse input instead of uinput. Requires a wlroots-based compositor (e.g. sway, Hyprland, labwc). Only change this if you have issues with uinput mouse input under Wayland.", "notify_pre_releases": "PreRelease Notifications", "notify_pre_releases_desc": "Whether to be notified of new pre-release versions of Sunshine", "nvenc_h264_cavlc": "Prefer CAVLC over CABAC in H.264", diff --git a/src_assets/common/assets/web/public/assets/locale/en_GB.json b/src_assets/common/assets/web/public/assets/locale/en_GB.json index 8ee9d5f37ed..8b2eaedc33f 100644 --- a/src_assets/common/assets/web/public/assets/locale/en_GB.json +++ b/src_assets/common/assets/web/public/assets/locale/en_GB.json @@ -282,6 +282,8 @@ "mouse_desc": "Allows guests to control the host system with the mouse", "native_pen_touch": "Native Pen/Touch Support", "native_pen_touch_desc": "When enabled, Sunshine will pass through native pen/touch events from Moonlight clients. This can be useful to disable for older applications without native pen/touch support.", + "wlr_virtual_mouse": "WLR Virtual Mouse", + "wlr_virtual_mouse_desc": "Use the wlr-virtual-pointer Wayland protocol for mouse input instead of uinput. Requires a wlroots-based compositor (e.g. sway, Hyprland, labwc). Only change this if you have issues with uinput mouse input under Wayland.", "notify_pre_releases": "PreRelease Notifications", "notify_pre_releases_desc": "Whether to be notified of new pre-release versions of Sunshine", "nvenc_h264_cavlc": "Prefer CAVLC over CABAC in H.264", diff --git a/src_assets/common/assets/web/public/assets/locale/en_US.json b/src_assets/common/assets/web/public/assets/locale/en_US.json index f2cc95b383c..41398d20357 100644 --- a/src_assets/common/assets/web/public/assets/locale/en_US.json +++ b/src_assets/common/assets/web/public/assets/locale/en_US.json @@ -282,6 +282,8 @@ "mouse_desc": "Allows guests to control the host system with the mouse", "native_pen_touch": "Native Pen/Touch Support", "native_pen_touch_desc": "When enabled, Sunshine will pass through native pen/touch events from Moonlight clients. This can be useful to disable for older applications without native pen/touch support.", + "wlr_virtual_mouse": "WLR Virtual Mouse", + "wlr_virtual_mouse_desc": "Use the wlr-virtual-pointer Wayland protocol for mouse input instead of uinput. Requires a wlroots-based compositor (e.g. sway, Hyprland, labwc). Only change this if you have issues with uinput mouse input under Wayland.", "notify_pre_releases": "PreRelease Notifications", "notify_pre_releases_desc": "Whether to be notified of new pre-release versions of Sunshine", "nvenc_h264_cavlc": "Prefer CAVLC over CABAC in H.264", diff --git a/src_assets/common/assets/web/public/assets/locale/es.json b/src_assets/common/assets/web/public/assets/locale/es.json index d5d0ca0a48f..deac001475c 100644 --- a/src_assets/common/assets/web/public/assets/locale/es.json +++ b/src_assets/common/assets/web/public/assets/locale/es.json @@ -282,6 +282,8 @@ "mouse_desc": "Permite a los huéspedes controlar el sistema de host con el ratón", "native_pen_touch": "Soporte de lápiz/táctil nativo", "native_pen_touch_desc": "Cuando está activado, Sunshine pasará a través de eventos nativos de pluma/táctil de clientes de Sunshine. Esto puede ser útil para deshabilitar para aplicaciones antiguas sin soporte nativo de pluma/táctil.", + "wlr_virtual_mouse": "WLR Virtual Mouse", + "wlr_virtual_mouse_desc": "Use the wlr-virtual-pointer Wayland protocol for mouse input instead of uinput. Requires a wlroots-based compositor (e.g. sway, Hyprland, labwc). Only change this if you have issues with uinput mouse input under Wayland.", "notify_pre_releases": "Notificaciones de pre-lanzamiento", "notify_pre_releases_desc": "Si desea ser notificado de las nuevas versiones de Sunshine", "nvenc_h264_cavlc": "Preferir CAVLC sobre CABAC en H.264", diff --git a/src_assets/common/assets/web/public/assets/locale/fr.json b/src_assets/common/assets/web/public/assets/locale/fr.json index d12b41f2660..5b4e8a7e2dd 100644 --- a/src_assets/common/assets/web/public/assets/locale/fr.json +++ b/src_assets/common/assets/web/public/assets/locale/fr.json @@ -282,6 +282,8 @@ "mouse_desc": "Permet aux invités de contrôler le système hôte avec la souris", "native_pen_touch": "Prise en charge stylo/écran tactile native", "native_pen_touch_desc": "Lorsque cette option est activée, Sunshine transmet les événements stylo/touche natifs des clients Moonlight. Il peut être utile de désactiver cette fonction pour les applications plus anciennes qui ne prennent pas en charge le stylet et le tactile.", + "wlr_virtual_mouse": "WLR Virtual Mouse", + "wlr_virtual_mouse_desc": "Use the wlr-virtual-pointer Wayland protocol for mouse input instead of uinput. Requires a wlroots-based compositor (e.g. sway, Hyprland, labwc). Only change this if you have issues with uinput mouse input under Wayland.", "notify_pre_releases": "Notifications de pré-publication", "notify_pre_releases_desc": "Si vous voulez être informé des nouvelles versions de la pré-version de Sunshine", "nvenc_h264_cavlc": "Préférer CAVLC sur CABAC en H.264", diff --git a/src_assets/common/assets/web/public/assets/locale/hu.json b/src_assets/common/assets/web/public/assets/locale/hu.json index c694d156e40..76137573b84 100644 --- a/src_assets/common/assets/web/public/assets/locale/hu.json +++ b/src_assets/common/assets/web/public/assets/locale/hu.json @@ -282,6 +282,8 @@ "mouse_desc": "Lehetővé teszi a vendégeknek, hogy egérrel irányítsák a gazdagépet", "native_pen_touch": "Natív toll/érintés támogatás", "native_pen_touch_desc": "Ha engedélyezve van, a Sunshine átengedi a natív toll/érintés eseményeket a Moonlight kliensektől. Hasznos lehet ezt kikapcsolni régebbi alkalmazásoknál, amik nem támogatják a natív toll/érintést.", + "wlr_virtual_mouse": "WLR Virtual Mouse", + "wlr_virtual_mouse_desc": "Use the wlr-virtual-pointer Wayland protocol for mouse input instead of uinput. Requires a wlroots-based compositor (e.g. sway, Hyprland, labwc). Only change this if you have issues with uinput mouse input under Wayland.", "notify_pre_releases": "Előzetes verzió értesítések", "notify_pre_releases_desc": "Legyen-e értesítés a Sunshine új előzetes verzióiról", "nvenc_h264_cavlc": "CAVLC preferálása CABAC helyett H.264-ben", diff --git a/src_assets/common/assets/web/public/assets/locale/it.json b/src_assets/common/assets/web/public/assets/locale/it.json index 7f3cea03ea1..e9adf4ab11e 100644 --- a/src_assets/common/assets/web/public/assets/locale/it.json +++ b/src_assets/common/assets/web/public/assets/locale/it.json @@ -282,6 +282,8 @@ "mouse_desc": "Permette ai guest di controllare il sistema host con il mouse", "native_pen_touch": "Supporto Nativo Della Penna/Touch", "native_pen_touch_desc": "Se abilitato, Sunshine passerà direttamente gli eventi nativi della penna/touch dal client Moonlight. Può essere utile disabilitarlo per le applicazioni più vecchie senza supporto nativo della penna/touch.", + "wlr_virtual_mouse": "WLR Virtual Mouse", + "wlr_virtual_mouse_desc": "Use the wlr-virtual-pointer Wayland protocol for mouse input instead of uinput. Requires a wlroots-based compositor (e.g. sway, Hyprland, labwc). Only change this if you have issues with uinput mouse input under Wayland.", "notify_pre_releases": "Notifiche Pre-Rilascio", "notify_pre_releases_desc": "Indica se notificare o meno le nuove versioni pre-rilascio di Sunshine", "nvenc_h264_cavlc": "Preferisci CAVLC a CABAC in H.264", diff --git a/src_assets/common/assets/web/public/assets/locale/ja.json b/src_assets/common/assets/web/public/assets/locale/ja.json index 608e372d621..9fb60d6b87d 100644 --- a/src_assets/common/assets/web/public/assets/locale/ja.json +++ b/src_assets/common/assets/web/public/assets/locale/ja.json @@ -282,6 +282,8 @@ "mouse_desc": "ゲストがマウスでホストシステムを制御できるようにします", "native_pen_touch": "Native Pen/Touch サポート", "native_pen_touch_desc": "有効にすると、SunshineはMoonlightクライアントからネイティブのペン/タッチイベントを通過します。これはネイティブのペン/タッチサポートがない古いアプリケーションでは無効にするのに便利です。", + "wlr_virtual_mouse": "WLR Virtual Mouse", + "wlr_virtual_mouse_desc": "Use the wlr-virtual-pointer Wayland protocol for mouse input instead of uinput. Requires a wlroots-based compositor (e.g. sway, Hyprland, labwc). Only change this if you have issues with uinput mouse input under Wayland.", "notify_pre_releases": "プレリリース通知", "notify_pre_releases_desc": "Sunshineの新しいプレリリースバージョンを通知するかどうか", "nvenc_h264_cavlc": "H.264よりCAVLCを優先する", diff --git a/src_assets/common/assets/web/public/assets/locale/ko.json b/src_assets/common/assets/web/public/assets/locale/ko.json index 9b9babe7547..444bbde9414 100644 --- a/src_assets/common/assets/web/public/assets/locale/ko.json +++ b/src_assets/common/assets/web/public/assets/locale/ko.json @@ -282,6 +282,8 @@ "mouse_desc": "게스트가 마우스로 호스트 시스템을 제어할 수 있습니다.", "native_pen_touch": "기본 펜/터치 지원", "native_pen_touch_desc": "활성화하면 Sunshine은 Moonlight 클라이언트의 기본 펜/터치 이벤트를 통과합니다. 이 기능은 기본 펜/터치를 지원하지 않는 구형 애플리케이션에서 비활성화하면 유용할 수 있습니다.", + "wlr_virtual_mouse": "WLR Virtual Mouse", + "wlr_virtual_mouse_desc": "Use the wlr-virtual-pointer Wayland protocol for mouse input instead of uinput. Requires a wlroots-based compositor (e.g. sway, Hyprland, labwc). Only change this if you have issues with uinput mouse input under Wayland.", "notify_pre_releases": "사전 출시 알림", "notify_pre_releases_desc": "Sunshine의 새 사전 출시 버전에 대한 알림 여부입니다.", "nvenc_h264_cavlc": "H.264에서 CABAC보다 CAVLC 선호", diff --git a/src_assets/common/assets/web/public/assets/locale/pl.json b/src_assets/common/assets/web/public/assets/locale/pl.json index d64604d0147..fcb7082c95a 100644 --- a/src_assets/common/assets/web/public/assets/locale/pl.json +++ b/src_assets/common/assets/web/public/assets/locale/pl.json @@ -282,6 +282,8 @@ "mouse_desc": "Umożliwia gościom kontrolowanie systemu hosta za pomocą myszy", "native_pen_touch": "Natywna obsługa pióra/dotyku", "native_pen_touch_desc": "Po włączeniu Sunshine będzie przekazywać natywne zdarzenia pióra/dotyku z klienta Moonlight. Może to być przydatne do wyłączenia w starszych aplikacjach bez natywnej obsługi pióra/dotyku.", + "wlr_virtual_mouse": "WLR Virtual Mouse", + "wlr_virtual_mouse_desc": "Use the wlr-virtual-pointer Wayland protocol for mouse input instead of uinput. Requires a wlroots-based compositor (e.g. sway, Hyprland, labwc). Only change this if you have issues with uinput mouse input under Wayland.", "notify_pre_releases": "Powiadomienia o wydaniu wstępnym", "notify_pre_releases_desc": "Czy otrzymywać powiadomienia o nowych przedpremierowych wersjach Sunshine", "nvenc_h264_cavlc": "Preferowanie CAVLC nad CABAC w H.264", diff --git a/src_assets/common/assets/web/public/assets/locale/pt.json b/src_assets/common/assets/web/public/assets/locale/pt.json index 831bc28303c..82fcff4ef26 100644 --- a/src_assets/common/assets/web/public/assets/locale/pt.json +++ b/src_assets/common/assets/web/public/assets/locale/pt.json @@ -282,6 +282,8 @@ "mouse_desc": "Permite aos convidados controlar o sistema de host com o mouse", "native_pen_touch": "Suporte nativo para Pen/Toque", "native_pen_touch_desc": "Quando ativado, o Sunshine irá passar por eventos nativos de caneta/toque de clientes de lua. Isto pode ser útil para desativar aplicações mais antigas sem o suporte nativo ao canal/toque.", + "wlr_virtual_mouse": "WLR Virtual Mouse", + "wlr_virtual_mouse_desc": "Use the wlr-virtual-pointer Wayland protocol for mouse input instead of uinput. Requires a wlroots-based compositor (e.g. sway, Hyprland, labwc). Only change this if you have issues with uinput mouse input under Wayland.", "notify_pre_releases": "Pré-Lançar notificações", "notify_pre_releases_desc": "Se deve ser notificado de novas versões de lançamento do Sunshine", "nvenc_h264_cavlc": "Preferir CAVLC ao CABAC no H.264", diff --git a/src_assets/common/assets/web/public/assets/locale/pt_BR.json b/src_assets/common/assets/web/public/assets/locale/pt_BR.json index 02938627b9e..0c2c51dd696 100644 --- a/src_assets/common/assets/web/public/assets/locale/pt_BR.json +++ b/src_assets/common/assets/web/public/assets/locale/pt_BR.json @@ -282,6 +282,8 @@ "mouse_desc": "Permite que os convidados controlem o sistema host com o mouse", "native_pen_touch": "Suporte nativo a caneta/toque", "native_pen_touch_desc": "Quando ativado, o Sunshine transmitirá os eventos nativos de caneta/toque dos clientes Moonlight. Isso pode ser útil para desativar aplicativos mais antigos sem suporte nativo a caneta/toque.", + "wlr_virtual_mouse": "WLR Virtual Mouse", + "wlr_virtual_mouse_desc": "Use the wlr-virtual-pointer Wayland protocol for mouse input instead of uinput. Requires a wlroots-based compositor (e.g. sway, Hyprland, labwc). Only change this if you have issues with uinput mouse input under Wayland.", "notify_pre_releases": "Notificações de pré-lançamento", "notify_pre_releases_desc": "Se deseja ser notificado sobre novas versões de pré-lançamento do Sunshine", "nvenc_h264_cavlc": "Prefira o CAVLC ao CABAC em H.264", diff --git a/src_assets/common/assets/web/public/assets/locale/ru.json b/src_assets/common/assets/web/public/assets/locale/ru.json index cb5304ea576..1ad64d2fb88 100644 --- a/src_assets/common/assets/web/public/assets/locale/ru.json +++ b/src_assets/common/assets/web/public/assets/locale/ru.json @@ -282,6 +282,8 @@ "mouse_desc": "Позволяет гостям контролировать хост-систему мышкой", "native_pen_touch": "Нативная поддержка пера/сенсорного экрана", "native_pen_touch_desc": "Если включено, Sunshine будет перенаправлять нативные события пера/сенсорного экрана от клиентов Moonlight. Это может быть полезно для более старых приложений без поддержки пера/сенсорного экрана.", + "wlr_virtual_mouse": "WLR Virtual Mouse", + "wlr_virtual_mouse_desc": "Use the wlr-virtual-pointer Wayland protocol for mouse input instead of uinput. Requires a wlroots-based compositor (e.g. sway, Hyprland, labwc). Only change this if you have issues with uinput mouse input under Wayland.", "notify_pre_releases": "Уведомлять о пререлизах", "notify_pre_releases_desc": "Уведомлять ли о новых предварительных версиях Sunshine", "nvenc_h264_cavlc": "Предпочитать CAVLC поверх CABAC в H.264", diff --git a/src_assets/common/assets/web/public/assets/locale/sv.json b/src_assets/common/assets/web/public/assets/locale/sv.json index 12771acc9fd..d64fa464204 100644 --- a/src_assets/common/assets/web/public/assets/locale/sv.json +++ b/src_assets/common/assets/web/public/assets/locale/sv.json @@ -282,6 +282,8 @@ "mouse_desc": "Tillåter gäster att styra värdsystemet med musen", "native_pen_touch": "Stöd för infödda pen/tryck", "native_pen_touch_desc": "När aktiverad, kommer Sunshine att passera genom infödda penn/touch händelser från Moonlight klienter. Detta kan vara användbart för att inaktivera för äldre applikationer utan inbyggt penn/touch stöd.", + "wlr_virtual_mouse": "WLR Virtual Mouse", + "wlr_virtual_mouse_desc": "Use the wlr-virtual-pointer Wayland protocol for mouse input instead of uinput. Requires a wlroots-based compositor (e.g. sway, Hyprland, labwc). Only change this if you have issues with uinput mouse input under Wayland.", "notify_pre_releases": "PreRelease Notiser", "notify_pre_releases_desc": "Huruvida meddelas om nya förhandsversioner av Sunshine", "nvenc_h264_cavlc": "Föredrar CAVLC över CABAC i H.264", diff --git a/src_assets/common/assets/web/public/assets/locale/tr.json b/src_assets/common/assets/web/public/assets/locale/tr.json index e70a082028e..73144b4c6da 100644 --- a/src_assets/common/assets/web/public/assets/locale/tr.json +++ b/src_assets/common/assets/web/public/assets/locale/tr.json @@ -282,6 +282,8 @@ "mouse_desc": "Konukların ana sistemi fare ile kontrol etmesini sağlar", "native_pen_touch": "Kalem/Dokunma Desteği", "native_pen_touch_desc": "Etkinleştirildiğinde Sunshine, Moonlight istemcilerinden gelen yerel kalem/dokunma olaylarını aktarır. Bu, yerel kalem/dokunmatik desteği olmayan eski uygulamalar için devre dışı bırakmak için yararlı olabilir.", + "wlr_virtual_mouse": "WLR Virtual Mouse", + "wlr_virtual_mouse_desc": "Use the wlr-virtual-pointer Wayland protocol for mouse input instead of uinput. Requires a wlroots-based compositor (e.g. sway, Hyprland, labwc). Only change this if you have issues with uinput mouse input under Wayland.", "notify_pre_releases": "Yayın Öncesi Bildirimler", "notify_pre_releases_desc": "Sunshine'ın yeni yayın öncesi sürümlerinden haberdar edilip edilmeme", "nvenc_h264_cavlc": "H.264'te CAVLC'yi CABAC'a tercih edin", diff --git a/src_assets/common/assets/web/public/assets/locale/uk.json b/src_assets/common/assets/web/public/assets/locale/uk.json index b5d6a5f79bf..5aa7199844f 100644 --- a/src_assets/common/assets/web/public/assets/locale/uk.json +++ b/src_assets/common/assets/web/public/assets/locale/uk.json @@ -282,6 +282,8 @@ "mouse_desc": "Дозволяє гостям керувати хост-системою за допомогою миші", "native_pen_touch": "Вбудована підтримка пера/сенсорного вводу", "native_pen_touch_desc": "Якщо увімкнено, Sunshine передаватиме події нативного пера/дотику від клієнтів Moonlight. Для старих програм без підтримки нативного пера/дотику може бути корисним вимкнення цього налаштування.", + "wlr_virtual_mouse": "WLR Virtual Mouse", + "wlr_virtual_mouse_desc": "Use the wlr-virtual-pointer Wayland protocol for mouse input instead of uinput. Requires a wlroots-based compositor (e.g. sway, Hyprland, labwc). Only change this if you have issues with uinput mouse input under Wayland.", "notify_pre_releases": "PreRelease Сповіщення", "notify_pre_releases_desc": "Чи отримувати сповіщення про нові pre-release версії Sunshine", "nvenc_h264_cavlc": "Надайте перевагу CAVLC над CABAC в H.264", diff --git a/src_assets/common/assets/web/public/assets/locale/vi.json b/src_assets/common/assets/web/public/assets/locale/vi.json index ae10ef8ab00..997543a7745 100644 --- a/src_assets/common/assets/web/public/assets/locale/vi.json +++ b/src_assets/common/assets/web/public/assets/locale/vi.json @@ -282,6 +282,8 @@ "mouse_desc": "Cho phép khách truy cập điều khiển hệ thống chủ bằng chuột.", "native_pen_touch": "Hỗ trợ bút cảm ứng và chạm gốc", "native_pen_touch_desc": "Khi được bật, Sunshine sẽ truyền các sự kiện bút/chạm gốc từ các ứng dụng Moonlight. Tính năng này có thể hữu ích để tắt cho các ứng dụng cũ không hỗ trợ bút/chạm gốc.", + "wlr_virtual_mouse": "WLR Virtual Mouse", + "wlr_virtual_mouse_desc": "Use the wlr-virtual-pointer Wayland protocol for mouse input instead of uinput. Requires a wlroots-based compositor (e.g. sway, Hyprland, labwc). Only change this if you have issues with uinput mouse input under Wayland.", "notify_pre_releases": "Thông báo trước khi phát hành", "notify_pre_releases_desc": "Có muốn nhận thông báo về các phiên bản thử nghiệm mới của Sunshine không?", "nvenc_h264_cavlc": "Ưu tiên CAVLC hơn CABAC trong H.264", diff --git a/src_assets/common/assets/web/public/assets/locale/zh.json b/src_assets/common/assets/web/public/assets/locale/zh.json index 432ff807df5..e76a96d504a 100644 --- a/src_assets/common/assets/web/public/assets/locale/zh.json +++ b/src_assets/common/assets/web/public/assets/locale/zh.json @@ -282,6 +282,8 @@ "mouse_desc": "允许访客使用鼠标控制主机系统", "native_pen_touch": "原生笔/触摸支持", "native_pen_touch_desc": "启用后,Sunshine 将透传来自 Moonlight 客户端的原生笔/触控事件。对于不支持原生笔/触控的旧版应用程序来说,禁用此功能非常有用。", + "wlr_virtual_mouse": "WLR Virtual Mouse", + "wlr_virtual_mouse_desc": "Use the wlr-virtual-pointer Wayland protocol for mouse input instead of uinput. Requires a wlroots-based compositor (e.g. sway, Hyprland, labwc). Only change this if you have issues with uinput mouse input under Wayland.", "notify_pre_releases": "预发布通知", "notify_pre_releases_desc": "是否接收 Sunshine 新预发布版本的通知", "nvenc_h264_cavlc": "在 H.264 中,偏向 CAVLC 而不是 CABAC", diff --git a/src_assets/common/assets/web/public/assets/locale/zh_TW.json b/src_assets/common/assets/web/public/assets/locale/zh_TW.json index 28534675708..d3c1df4488f 100644 --- a/src_assets/common/assets/web/public/assets/locale/zh_TW.json +++ b/src_assets/common/assets/web/public/assets/locale/zh_TW.json @@ -282,6 +282,8 @@ "mouse_desc": "允許訪客使用滑鼠控制主機系統", "native_pen_touch": "原生筆/觸控支援", "native_pen_touch_desc": "啟用後,Sunshine 將從 Moonlight 用戶端傳遞本機筆觸事件。對於沒有原生筆/觸控支援的舊版應用程式來說,停用此功能可能很有用。", + "wlr_virtual_mouse": "WLR Virtual Mouse", + "wlr_virtual_mouse_desc": "Use the wlr-virtual-pointer Wayland protocol for mouse input instead of uinput. Requires a wlroots-based compositor (e.g. sway, Hyprland, labwc). Only change this if you have issues with uinput mouse input under Wayland.", "notify_pre_releases": "發佈前通知", "notify_pre_releases_desc": "是否接收 Sunshine 的新預發佈版本通知", "nvenc_h264_cavlc": "在 H.264 中選擇 CAVLC 而非 CABAC", From 11c3fd3d478223e012b62413f86e3d106f32d557 Mon Sep 17 00:00:00 2001 From: Christian Maxwell Date: Fri, 10 Apr 2026 12:52:46 -0400 Subject: [PATCH 2/2] fix(linux/input): bind wlr virtual pointer to captured output 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 --- src/platform/linux/input/wl_mouse.cpp | 44 ++++++++++++++++++++++++--- src/platform/linux/input/wl_mouse.h | 11 ++++++- 2 files changed, 49 insertions(+), 6 deletions(-) diff --git a/src/platform/linux/input/wl_mouse.cpp b/src/platform/linux/input/wl_mouse.cpp index fb628216eba..188ed99794e 100644 --- a/src/platform/linux/input/wl_mouse.cpp +++ b/src/platform/linux/input/wl_mouse.cpp @@ -22,6 +22,7 @@ #include "src/config.h" #include "src/logging.h" #include "src/platform/common.h" +#include "src/utility.h" #include "wl_mouse.h" namespace platf::wl_mouse { @@ -40,7 +41,7 @@ namespace platf::wl_mouse { } // ------------------------------------------------------------------------- - // Registry listener — binds zwlr_virtual_pointer_manager_v1 when found + // Registry listener — binds zwlr_virtual_pointer_manager_v1 and wl_output // ------------------------------------------------------------------------- void @@ -51,6 +52,13 @@ namespace platf::wl_mouse { state->manager = reinterpret_cast( wl_registry_bind(registry, name, &zwlr_virtual_pointer_manager_v1_interface, bind_version)); } + else if (strcmp(interface, "wl_output") == 0) { + // Collect outputs in announcement order — same ordering wlgrab uses for interface.monitors, + // so index N here corresponds to the same physical output as display_name "N" in capture config. + auto *out = reinterpret_cast( + wl_registry_bind(registry, name, &wl_output_interface, 1)); + state->outputs.push_back(out); + } } void @@ -88,11 +96,32 @@ namespace platf::wl_mouse { return false; } - // NULL seat = compositor picks the default seat - pointer = zwlr_virtual_pointer_manager_v1_create_virtual_pointer(manager, nullptr); - wl_display_roundtrip(display); // finish device creation + // Select the output that matches the configured capture monitor (same index logic as wlgrab). + // Binding the pointer to an output makes motion_absolute coordinates relative to that + // output's local frame instead of the full global compositor space, which prevents + // absolute inputs from mapping across multiple monitors. + int monitor_idx = 0; + if (!config::video.capture.empty()) { + auto idx = util::from_view(config::video.capture); + if (idx >= 0 && idx < (int) outputs.size()) { + monitor_idx = (int) idx; + } + } - BOOST_LOG(info) << "wl_mouse: virtual pointer created via wlr-virtual-pointer protocol"; + if (!outputs.empty()) { + bound_output = outputs[monitor_idx]; + pointer = zwlr_virtual_pointer_manager_v1_create_virtual_pointer_with_output( + manager, nullptr, bound_output); + BOOST_LOG(info) << "wl_mouse: virtual pointer bound to output index " << monitor_idx + << " (matches capture display)"; + } + else { + BOOST_LOG(warning) << "wl_mouse: no wl_output globals found; " + << "absolute coordinates will use global compositor space"; + pointer = zwlr_virtual_pointer_manager_v1_create_virtual_pointer(manager, nullptr); + } + + wl_display_roundtrip(display); // finish device creation return true; } @@ -106,6 +135,11 @@ namespace platf::wl_mouse { zwlr_virtual_pointer_manager_v1_destroy(manager); manager = nullptr; } + for (auto *out : outputs) { + wl_output_destroy(out); + } + outputs.clear(); + bound_output = nullptr; if (registry) { wl_registry_destroy(registry); registry = nullptr; diff --git a/src/platform/linux/input/wl_mouse.h b/src/platform/linux/input/wl_mouse.h index 55b3cc56bd3..50e041a696b 100644 --- a/src/platform/linux/input/wl_mouse.h +++ b/src/platform/linux/input/wl_mouse.h @@ -6,6 +6,9 @@ #ifdef SUNSHINE_BUILD_WAYLAND +// standard includes +#include + // lib includes #include @@ -29,9 +32,15 @@ namespace platf::wl_mouse { zwlr_virtual_pointer_manager_v1 *manager = nullptr; zwlr_virtual_pointer_v1 *pointer = nullptr; + /** All wl_output globals discovered during registry enumeration, in announcement order. */ + std::vector outputs; + /** The output this pointer is bound to (nullptr if creation fell back to unbound). */ + wl_output *bound_output = nullptr; + /** * Connect to WAYLAND_DISPLAY, discover zwlr_virtual_pointer_manager_v1 - * via the registry, and create a virtual pointer device. + * via the registry, and create a virtual pointer device bound to the + * output that matches config::video.capture (same index logic as wlgrab). * @return true on success, false if the compositor does not support the protocol. */ bool init();