diff --git a/docs/source/about/advanced_usage.rst b/docs/source/about/advanced_usage.rst index 72926557..c2726347 100644 --- a/docs/source/about/advanced_usage.rst +++ b/docs/source/about/advanced_usage.rst @@ -273,6 +273,22 @@ always_send_scancodes always_send_scancodes = enabled +high_resolution_scrolling +^^^^^^^^^^^^^^^^^^^^^^^^^ + +**Description** + When enabled, Sunshine will pass through high resolution scroll events from Moonlight clients. + + This can be useful to disable for older applications that scroll too fast with high resolution scroll events. + +**Default** + ``enabled`` + +**Example** + .. code-block:: text + + high_resolution_scrolling = enabled + keybindings ^^^^^^^^^^^ diff --git a/src/config.cpp b/src/config.cpp index 030bdb1d..c0c4d59f 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -427,6 +427,7 @@ namespace config { true, // mouse enabled true, // controller enabled true, // always send scancodes + true, // high resolution scrolling }; sunshine_t sunshine { @@ -1059,6 +1060,7 @@ namespace config { bool_f(vars, "always_send_scancodes", input.always_send_scancodes); + bool_f(vars, "high_resolution_scrolling", input.high_resolution_scrolling); int port = sunshine.port; int_between_f(vars, "port"s, port, { 1024 + nvhttp::PORT_HTTPS, 65535 - rtsp_stream::RTSP_SETUP_PORT }); sunshine.port = (std::uint16_t) port; diff --git a/src/config.h b/src/config.h index 9df7a144..ce1cc418 100644 --- a/src/config.h +++ b/src/config.h @@ -121,6 +121,8 @@ namespace config { bool controller; bool always_send_scancodes; + + bool high_resolution_scrolling; }; namespace flag { diff --git a/src/input.cpp b/src/input.cpp index a2c85256..63c10d8b 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -25,6 +25,11 @@ extern "C" { #include +// Win32 WHEEL_DELTA constant +#ifndef WHEEL_DELTA + #define WHEEL_DELTA 120 +#endif + using namespace std::literals; namespace input { @@ -160,7 +165,9 @@ namespace input { touch_port_event { std::move(touch_port_event) }, feedback_queue { std::move(feedback_queue) }, mouse_left_button_timeout {}, - touch_port { { 0, 0, 0, 0 }, 0, 0, 1.0f } {} + touch_port { { 0, 0, 0, 0 }, 0, 0, 1.0f }, + accumulated_vscroll_delta {}, + accumulated_hscroll_delta {} {} // Keep track of alt+ctrl+shift key combo int shortcutFlags; @@ -177,6 +184,9 @@ namespace input { thread_pool_util::ThreadPool::task_id_t mouse_left_button_timeout; input::touch_port_t touch_port; + + int32_t accumulated_vscroll_delta; + int32_t accumulated_hscroll_delta; }; /** @@ -790,22 +800,54 @@ namespace input { update_shortcutFlags(&input->shortcutFlags, map_keycode(keyCode), release); } + /** + * @brief Called to pass a vertical scroll message the platform backend. + * @param input The input context pointer. + * @param packet The scroll packet. + */ void - passthrough(PNV_SCROLL_PACKET packet) { + passthrough(std::shared_ptr &input, PNV_SCROLL_PACKET packet) { if (!config::input.mouse) { return; } - platf::scroll(platf_input, util::endian::big(packet->scrollAmt1)); + if (config::input.high_resolution_scrolling) { + platf::scroll(platf_input, util::endian::big(packet->scrollAmt1)); + } + else { + input->accumulated_vscroll_delta += util::endian::big(packet->scrollAmt1); + auto full_ticks = input->accumulated_vscroll_delta / WHEEL_DELTA; + if (full_ticks) { + // Send any full ticks that have accumulated and store the rest + platf::scroll(platf_input, full_ticks * WHEEL_DELTA); + input->accumulated_vscroll_delta -= full_ticks * WHEEL_DELTA; + } + } } + /** + * @brief Called to pass a horizontal scroll message the platform backend. + * @param input The input context pointer. + * @param packet The scroll packet. + */ void - passthrough(PSS_HSCROLL_PACKET packet) { + passthrough(std::shared_ptr &input, PSS_HSCROLL_PACKET packet) { if (!config::input.mouse) { return; } - platf::hscroll(platf_input, util::endian::big(packet->scrollAmount)); + if (config::input.high_resolution_scrolling) { + platf::hscroll(platf_input, util::endian::big(packet->scrollAmount)); + } + else { + input->accumulated_hscroll_delta += util::endian::big(packet->scrollAmount); + auto full_ticks = input->accumulated_hscroll_delta / WHEEL_DELTA; + if (full_ticks) { + // Send any full ticks that have accumulated and store the rest + platf::hscroll(platf_input, full_ticks * WHEEL_DELTA); + input->accumulated_hscroll_delta -= full_ticks * WHEEL_DELTA; + } + } } void @@ -1540,10 +1582,10 @@ namespace input { passthrough(input, (PNV_MOUSE_BUTTON_PACKET) payload); break; case SCROLL_MAGIC_GEN5: - passthrough((PNV_SCROLL_PACKET) payload); + passthrough(input, (PNV_SCROLL_PACKET) payload); break; case SS_HSCROLL_MAGIC: - passthrough((PSS_HSCROLL_PACKET) payload); + passthrough(input, (PSS_HSCROLL_PACKET) payload); break; case KEY_DOWN_EVENT_MAGIC: case KEY_UP_EVENT_MAGIC: diff --git a/src_assets/common/assets/web/config.html b/src_assets/common/assets/web/config.html index d49bee69..49a51dcf 100644 --- a/src_assets/common/assets/web/config.html +++ b/src_assets/common/assets/web/config.html @@ -392,6 +392,18 @@ In those cases it may be useful to make Sunshine think the Right Alt key is the Windows key + +
+ + +
+ When enabled, Sunshine will pass through high resolution scroll events from Moonlight clients.
+ This can be useful to disable for older applications that scroll too fast with high resolution scroll events. +
+
@@ -851,6 +863,7 @@ "amd_vbaq": "enabled", "capture": "", "controller": "enabled", + "high_resolution_scrolling": "enabled", "install_steam_audio_drivers": "enabled", "ds4_back_as_touchpad_click": "enabled", "dwmflush": "enabled",