|
8 | 8 | #include "Settings.hpp" |
9 | 9 | #include <borealis.hpp> |
10 | 10 | #include <streaming_view.hpp> |
| 11 | +#include <algorithm> |
11 | 12 | #include <chrono> |
12 | 13 | #include <cmath> |
| 14 | +#include <limits> |
13 | 15 |
|
14 | 16 | using namespace brls; |
15 | 17 |
|
16 | 18 | namespace { |
17 | 19 | constexpr float STICK_SCROLL_DEADZONE = 0.2f; |
| 20 | +constexpr float MOONLIGHT_WHEEL_DELTA = 120.0f; |
| 21 | +constexpr auto DESKTOP_SCROLL_GESTURE_TIMEOUT = |
| 22 | + std::chrono::milliseconds(600); |
18 | 23 |
|
19 | 24 | float applyStickScrollDeadzone(float axis, float configuredDeadzone) { |
20 | 25 | float deadzone = std::fmax(STICK_SCROLL_DEADZONE, configuredDeadzone); |
@@ -55,15 +60,7 @@ MoonlightInputManager::MoonlightInputManager() { |
55 | 60 | ->getMouseScrollOffsetChanged() |
56 | 61 | ->subscribe([this](brls::Point scroll) { |
57 | 62 | if (!inputEnabled) return; |
58 | | - |
59 | | - if (scroll.x != 0) { |
60 | | - brls::Logger::info("Mouse scroll X sended: {}", scroll.x); |
61 | | - LiSendHighResHScrollEvent( short(scroll.x)); |
62 | | - } |
63 | | - if (scroll.y != 0) { |
64 | | - brls::Logger::info("Mouse scroll Y sended: {}", scroll.y); |
65 | | - LiSendHighResScrollEvent( short(scroll.y)); |
66 | | - } |
| 63 | + handleDesktopMouseScroll(scroll); |
67 | 64 | }); |
68 | 65 |
|
69 | 66 | inputManager |
@@ -98,6 +95,81 @@ MoonlightInputManager::MoonlightInputManager() { |
98 | 95 | }); |
99 | 96 | } |
100 | 97 |
|
| 98 | +void MoonlightInputManager::handleDesktopMouseScroll(brls::Point scroll) { |
| 99 | + const auto now = std::chrono::steady_clock::now(); |
| 100 | + if (lastDesktopScrollEvent.time_since_epoch().count() == 0 || |
| 101 | + now - lastDesktopScrollEvent > DESKTOP_SCROLL_GESTURE_TIMEOUT) { |
| 102 | + desktopScrollAxis = DesktopScrollAxis::None; |
| 103 | + pendingHorizontalScroll = 0; |
| 104 | + pendingHorizontalScrollEvents = 0; |
| 105 | + } |
| 106 | + lastDesktopScrollEvent = now; |
| 107 | + |
| 108 | + // Trackpads frequently emit a small perpendicular component. Some virtual |
| 109 | + // mouse drivers quantize that component into a full wheel tick, so lock a |
| 110 | + // gesture to one axis instead of forwarding diagonal jitter to the host. |
| 111 | + if (scroll.y != 0 && |
| 112 | + (scroll.x == 0 || std::fabs(scroll.y) >= std::fabs(scroll.x))) { |
| 113 | + desktopScrollAxis = DesktopScrollAxis::Vertical; |
| 114 | + pendingHorizontalScroll = 0; |
| 115 | + pendingHorizontalScrollEvents = 0; |
| 116 | + sendDesktopMouseScroll({0, scroll.y}); |
| 117 | + return; |
| 118 | + } |
| 119 | + |
| 120 | + if (scroll.x == 0 || desktopScrollAxis == DesktopScrollAxis::Vertical) |
| 121 | + return; |
| 122 | + |
| 123 | + if (desktopScrollAxis == DesktopScrollAxis::None) { |
| 124 | + pendingHorizontalScroll += scroll.x; |
| 125 | + pendingHorizontalScrollEvents++; |
| 126 | + |
| 127 | + // A single isolated horizontal tick is normally diagonal jitter. |
| 128 | + // Buffer it until a second tick confirms an intentional gesture. |
| 129 | + if (pendingHorizontalScrollEvents < 2) |
| 130 | + return; |
| 131 | + |
| 132 | + desktopScrollAxis = DesktopScrollAxis::Horizontal; |
| 133 | + scroll.x = pendingHorizontalScroll; |
| 134 | + pendingHorizontalScroll = 0; |
| 135 | + pendingHorizontalScrollEvents = 0; |
| 136 | + } |
| 137 | + |
| 138 | + sendDesktopMouseScroll({scroll.x, 0}); |
| 139 | +} |
| 140 | + |
| 141 | +void MoonlightInputManager::sendDesktopMouseScroll(brls::Point scroll) { |
| 142 | + desktopScrollRemainder.x += scroll.x * MOONLIGHT_WHEEL_DELTA; |
| 143 | + desktopScrollRemainder.y += scroll.y * MOONLIGHT_WHEEL_DELTA; |
| 144 | + |
| 145 | + const int horizontal = std::clamp( |
| 146 | + static_cast<int>(std::trunc(desktopScrollRemainder.x)), |
| 147 | + static_cast<int>(std::numeric_limits<short>::min()), |
| 148 | + static_cast<int>(std::numeric_limits<short>::max())); |
| 149 | + const int vertical = std::clamp( |
| 150 | + static_cast<int>(std::trunc(desktopScrollRemainder.y)), |
| 151 | + static_cast<int>(std::numeric_limits<short>::min()), |
| 152 | + static_cast<int>(std::numeric_limits<short>::max())); |
| 153 | + |
| 154 | + if (horizontal != 0) { |
| 155 | + desktopScrollRemainder.x -= horizontal; |
| 156 | + const int result = |
| 157 | + LiSendHighResHScrollEvent(static_cast<short>(horizontal)); |
| 158 | + if (result < 0) |
| 159 | + brls::Logger::warning( |
| 160 | + "Failed to queue horizontal mouse scroll: {}", result); |
| 161 | + } |
| 162 | + |
| 163 | + if (vertical != 0) { |
| 164 | + desktopScrollRemainder.y -= vertical; |
| 165 | + const int result = |
| 166 | + LiSendHighResScrollEvent(static_cast<short>(vertical)); |
| 167 | + if (result < 0) |
| 168 | + brls::Logger::warning( |
| 169 | + "Failed to queue vertical mouse scroll: {}", result); |
| 170 | + } |
| 171 | +} |
| 172 | + |
101 | 173 | void MoonlightInputManager::sendRelativeMouseMove(brls::Point offset) { |
102 | 174 | desktopMouseRemainder.x += offset.x; |
103 | 175 | desktopMouseRemainder.y += offset.y; |
@@ -181,6 +253,11 @@ void MoonlightInputManager::dropInput() { |
181 | 253 | return; |
182 | 254 |
|
183 | 255 | desktopMouseRemainder = {0, 0}; |
| 256 | + desktopScrollRemainder = {0, 0}; |
| 257 | + pendingHorizontalScroll = 0; |
| 258 | + pendingHorizontalScrollEvents = 0; |
| 259 | + desktopScrollAxis = DesktopScrollAxis::None; |
| 260 | + lastDesktopScrollEvent = {}; |
184 | 261 |
|
185 | 262 | bool res = true; |
186 | 263 | // Drop gamepad state |
|
0 commit comments