Skip to content

Commit f3c3d0f

Browse files
committed
Linux build
1 parent a6883b8 commit f3c3d0f

10 files changed

Lines changed: 550 additions & 14 deletions

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,12 @@ make -C build/pc -j$(nproc)
172172

173173
Also, please note that the `resources` folder must be available in the working directory, otherwise the program will fail to find the shaders.
174174

175+
On Linux, enabling **Use hardware decoding** makes the FFmpeg decoder try
176+
VA-API, CUDA, and VDPAU in that order. The selected backend decodes into GPU
177+
surfaces and copies NV12, P010, or YUV420P frames back for the OpenGL renderer.
178+
If no compatible device or driver is available, Moonlight automatically falls
179+
back to software decoding.
180+
175181
#### Windows (MSYS2)
176182

177183
Windows desktop builds are supported through MSYS2 system packages for both x64 and ARM64.

app/src/settings_tab.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,11 @@ SettingsTab::SettingsTab() {
340340
hwDecoding->init("settings/use_hw_decoding"_i18n, Settings::instance().use_hw_decoding(),
341341
[](bool value) { Settings::instance().set_use_hw_decoding(value); });
342342

343+
#if defined(__linux__) && defined(PLATFORM_DESKTOP)
344+
hwDecoding->setEnabled(true);
345+
#else
343346
hwDecoding->setEnabled(false);
347+
#endif
344348

345349
#if defined(__PSV__)
346350
const float mbpsMaxLimit = 20000;

app/src/streaming/InputManager.cpp

Lines changed: 86 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,18 @@
88
#include "Settings.hpp"
99
#include <borealis.hpp>
1010
#include <streaming_view.hpp>
11+
#include <algorithm>
1112
#include <chrono>
1213
#include <cmath>
14+
#include <limits>
1315

1416
using namespace brls;
1517

1618
namespace {
1719
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);
1823

1924
float applyStickScrollDeadzone(float axis, float configuredDeadzone) {
2025
float deadzone = std::fmax(STICK_SCROLL_DEADZONE, configuredDeadzone);
@@ -55,15 +60,7 @@ MoonlightInputManager::MoonlightInputManager() {
5560
->getMouseScrollOffsetChanged()
5661
->subscribe([this](brls::Point scroll) {
5762
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);
6764
});
6865

6966
inputManager
@@ -98,6 +95,81 @@ MoonlightInputManager::MoonlightInputManager() {
9895
});
9996
}
10097

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+
101173
void MoonlightInputManager::sendRelativeMouseMove(brls::Point offset) {
102174
desktopMouseRemainder.x += offset.x;
103175
desktopMouseRemainder.y += offset.y;
@@ -181,6 +253,11 @@ void MoonlightInputManager::dropInput() {
181253
return;
182254

183255
desktopMouseRemainder = {0, 0};
256+
desktopScrollRemainder = {0, 0};
257+
pendingHorizontalScroll = 0;
258+
pendingHorizontalScrollEvents = 0;
259+
desktopScrollAxis = DesktopScrollAxis::None;
260+
lastDesktopScrollEvent = {};
184261

185262
bool res = true;
186263
// Drop gamepad state

app/src/streaming/InputManager.hpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "Singleton.hpp"
1111
#include "keyboard_view.hpp"
1212
#include <borealis.hpp>
13+
#include <chrono>
1314
#include <optional>
1415

1516
// Moonlight ready gamepad
@@ -61,18 +62,31 @@ class MoonlightInputManager : public Singleton<MoonlightInputManager> {
6162
static void rightMouseClick();
6263

6364
private:
65+
enum class DesktopScrollAxis {
66+
None,
67+
Horizontal,
68+
Vertical,
69+
};
70+
6471
RumbleValues rumbleCache[GAMEPADS_MAX];
6572
GamepadState lastGamepadStates[GAMEPADS_MAX];
6673
brls::ControllerButton mappingButtons[brls::_BUTTON_MAX];
6774
std::optional<brls::PanGestureStatus> panStatus;
6875
std::map<uint32_t, bool> activeTouchIDs;
6976
brls::Point desktopMouseRemainder = {0, 0};
77+
brls::Point desktopScrollRemainder = {0, 0};
78+
float pendingHorizontalScroll = 0;
79+
unsigned pendingHorizontalScrollEvents = 0;
80+
DesktopScrollAxis desktopScrollAxis = DesktopScrollAxis::None;
81+
std::chrono::steady_clock::time_point lastDesktopScrollEvent;
7082
bool inputDropped = false;
7183
bool inputEnabled = true;
7284

7385
brls::ControllerState mapController(brls::ControllerState controller);
7486
static short glfwKeyToVKKey(brls::BrlsKeyboardScancode key);
7587
void sendRelativeMouseMove(brls::Point offset);
88+
void handleDesktopMouseScroll(brls::Point scroll);
89+
void sendDesktopMouseScroll(brls::Point scroll);
7690

7791
GamepadState getControllerState(int controllerNum, bool specialKey);
7892
void handleControllers(bool specialKey);

0 commit comments

Comments
 (0)