Compare commits

...
37 Commits
Author SHA1 Message Date
Cameron Gutman 231a67946d WIP SDL3 compatibility 2025-01-31 01:13:17 -06:00
Cameron Gutman 5290305944 Abstract SDL success/failure checks inside a compat macro 2025-01-30 23:36:46 -06:00
Cameron Gutman 3a22a01aef Move window event handling into separate functions 2025-01-30 22:58:02 -06:00
Cameron Gutman d1f3b98a1f Abstract window creation into compat function 2025-01-30 22:41:44 -06:00
Cameron Gutman a37020b974 Abstract fullscreen details into SDLC functions 2025-01-30 22:41:44 -06:00
Cameron Gutman 13b2b28c2f Abstract SDL_SysWM into compat functions 2025-01-30 22:41:40 -06:00
Cameron Gutman dd2a99a96b Prepare for SDL3 support 2025-01-25 16:20:20 -06:00
Cameron Gutman edd7a134d8 Fix MSVC binary path in CI for Qt 6.8 2025-01-13 20:11:06 -06:00
Cameron Gutman ffa87c5f01 Update Windows AppVeyor builds to Qt 6.8 2025-01-13 19:48:47 -06:00
Cameron Gutman 15e337fff8 Only call SDL_NumJoysticks() once per loop
It does non-trivial work when using sdl2-compat.
2024-11-12 23:52:31 -06:00
Cameron Gutman 707dd3cb83 Checkout libs submodule for Steam Link too 2024-11-08 21:32:32 -06:00
Cameron Gutman 98f6a09991 Use a Ne10-optimized libopus build for Steam Link
Steam Link is an incredibly CPU-constrained platform, so it needs
all the help it can get to avoid audio underruns.
2024-11-08 21:26:45 -06:00
Cameron Gutman 103f988dbf Move logging into a separate thread 2024-10-20 21:52:15 -05:00
Cameron Gutman 2257cb0cef Update Windows, macOS, and AppImage libraries
SDL -> 86fd4ed
FFmpeg -> 7.1 (87ccf995c)
dav1d -> 1.5.0
2024-10-20 13:46:05 -05:00
Cameron Gutman b6a3369243 Rework window focus tracking for gamepad navigation 2024-10-18 21:20:56 -05:00
Cameron Gutman 4af9623727 Replace C++ VLAs with std::vector 2024-10-15 22:41:21 -05:00
Cameron Gutman 515db03fe5 Add missing stdbool.h include 2024-10-15 22:38:06 -05:00
Matthias Küch e44d097683 Add stderr log for Steam Link 2024-10-12 00:14:51 -05:00
Cameron Gutman 9936085aee Fix incorrect selection if pix_fmt is overridden before ffGetFormat()
The DRM renderer does this for the out-of-tree v4l2m2m patches to ensure
we get NV12 buffers, but it ends up clobbering our own preference for
DRM_PRIME frames in the process.
2024-10-03 20:28:51 -05:00
Cameron Gutman 3279d9c3f6 Fix QML component versioning error on Qt 5 2024-10-03 00:15:53 -05:00
Cameron Gutman 3531fe0a4f Fix typo in version check for new Vulkan queue API 2024-10-02 00:37:22 -05:00
Cameron Gutman 6ce02616f0 Use new Vulkan queue API in FFmpeg 7.1 2024-09-29 23:04:23 -05:00
Cameron Gutman 13880353d8 Use new avcodec_get_supported_config() API in FFmpeg 7.1 2024-09-29 22:23:37 -05:00
Cameron Gutman ec69dad8d7 Fix import incorrectly removed by 2a63ad5 2024-09-28 00:09:21 -05:00
Cameron Gutman 72ae324d71 Fix unused parameter warnings 2024-09-28 00:08:35 -05:00
Cameron Gutman 901cbd255c Disable Vulkan debug layers by default
The vast majority of Vulkan code running in Moonlight
is part of FFmpeg or libplacebo, so the debug layers
really just slow things down without finding any bugs
in our code.

Additionally, there are some overzealous checks firing
constantly on libplacebo and FFmpeg with certain Vulkan
drivers that we can't do anything about.
2024-09-28 00:05:17 -05:00
Cameron Gutman 2a63ad53d7 Don't poll gamepad input when the GUI is not focused/visible 2024-09-26 19:24:29 -05:00
Cameron Gutman 9b3d4c1ad7 Free the old CRTC connectors array 2024-09-23 22:25:51 -05:00
Cameron Gutman 054e334066 Allow Qt to borrow DRM master from SDL to update the UI 2024-09-23 22:15:31 -05:00
Cameron Gutman 6d023c2dfa Defer launch warnings until after launch validation
We need to destroy the SDL window before we can be sure that Qt
can draw to the screen if we used KMSDRM.
2024-09-23 21:48:43 -05:00
Cameron Gutman 0e2d5bf441 Fix EGLFS state restoration after Vulkan rendering 2024-09-23 21:32:09 -05:00
Cameron Gutman 023b6b2772 Fix DRM FD leak with Vulkan windows 2024-09-21 21:41:43 -05:00
Cameron Gutman 6f39d120cb Unify handling of DRM devices between DRM and VAAPI
SDL may not be able to give us a DRM FD for Vulkan windows.
2024-09-21 20:46:11 -05:00
Cameron Gutman 9cf305865b Add support for managing multiple SDL DRM FDs
This is required for Vulkan+KMSDRM rendering.
2024-09-21 20:41:43 -05:00
Cameron Gutman 6b11f43302 Don't set the output rect until after modesetting
It's possible that modesetting will also change the resolution
not just the refresh rate. This can happen in cases where the
CRTC is currently set to 4K 30 Hz and we choose 1080p 60 Hz as
a better mode match for displaying a 1080p 60 FPS stream.
2024-09-19 22:43:34 -05:00
Cameron Gutman 5a1ef55767 Update README.md
Add note about YUV 4:4:4 and update build requirements
2024-09-18 22:16:33 -05:00
Matt Tannahill 76deafbd7b Fix build for Xcode < 14 2024-09-18 22:12:04 -05:00
65 changed files with 2182 additions and 1249 deletions
+4 -3
View File
@@ -13,6 +13,7 @@ You can follow development on our [Discord server](https://moonlight-stream.org/
## Features
- Hardware accelerated video decoding on Windows, Mac, and Linux
- H.264, HEVC, and AV1 codec support (AV1 requires Sunshine and a supported host GPU)
- YUV 4:4:4 support (Sunshine only)
- HDR streaming support
- 7.1 surround sound audio support
- 10-point multitouch support (Sunshine only)
@@ -39,7 +40,7 @@ Hosting for Moonlight's Debian and L4T package repositories is graciously provid
## Building
### Windows Build Requirements
* Qt 5.15 SDK or later. Qt 6 is also supported for x64 and ARM64 builds.
* Qt 6.7 SDK or later (earlier versions may work but are not officially supported)
* [Visual Studio 2022](https://visualstudio.microsoft.com/downloads/) (Community edition is fine)
* Select **MSVC** option during Qt installation. MinGW is not supported.
* [7-Zip](https://www.7-zip.org/) (only if building installers for non-development PCs)
@@ -48,8 +49,8 @@ Hosting for Moonlight's Debian and L4T package repositories is graciously provid
* Alternatively, run `dism /online /add-capability /capabilityname:Tools.Graphics.DirectX~~~~0.0.1.0` and reboot.
### macOS Build Requirements
* Qt 6.4 SDK or later
* Xcode 13 or later
* Qt 6.7 SDK or later (earlier versions may work but are not officially supported)
* Xcode 14 or later (earlier versions may work but are not officially supported)
* [create-dmg](https://github.com/sindresorhus/create-dmg) (only if building DMGs for use on non-development Macs)
### Linux/Unix Build Requirements
+320
View File
@@ -0,0 +1,320 @@
#include "SDL_compat.h"
#include <SDL_syswm.h>
void* SDLC_Win32_GetHwnd(SDL_Window* window)
{
#ifdef SDL_VIDEO_DRIVER_WINDOWS
SDL_SysWMinfo info;
SDL_VERSION(&info.version);
if (!SDL_GetWindowWMInfo(window, &info)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_GetWindowWMInfo() failed: %s",
SDL_GetError());
return NULL;
}
if (info.subsystem == SDL_SYSWM_WINDOWS) {
return info.info.win.window;
}
#else
(void)window;
#endif
return NULL;
}
void* SDLC_MacOS_GetWindow(SDL_Window* window)
{
#ifdef SDL_VIDEO_DRIVER_COCOA
SDL_SysWMinfo info;
SDL_VERSION(&info.version);
if (!SDL_GetWindowWMInfo(window, &info)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_GetWindowWMInfo() failed: %s",
SDL_GetError());
return NULL;
}
if (info.subsystem == SDL_SYSWM_COCOA) {
return info.info.cocoa.window;
}
#else
(void)window;
#endif
return NULL;
}
void* SDLC_X11_GetDisplay(SDL_Window* window)
{
#ifdef SDL_VIDEO_DRIVER_X11
SDL_SysWMinfo info;
SDL_VERSION(&info.version);
if (!SDL_GetWindowWMInfo(window, &info)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_GetWindowWMInfo() failed: %s",
SDL_GetError());
return NULL;
}
if (info.subsystem == SDL_SYSWM_X11) {
return info.info.x11.display;
}
#else
(void)window;
#endif
return NULL;
}
unsigned long SDLC_X11_GetWindow(SDL_Window* window)
{
#ifdef SDL_VIDEO_DRIVER_X11
SDL_SysWMinfo info;
SDL_VERSION(&info.version);
if (!SDL_GetWindowWMInfo(window, &info)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_GetWindowWMInfo() failed: %s",
SDL_GetError());
return 0;
}
if (info.subsystem == SDL_SYSWM_X11) {
return info.info.x11.window;
}
#else
(void)window;
#endif
return 0;
}
void* SDLC_Wayland_GetDisplay(SDL_Window* window)
{
#ifdef SDL_VIDEO_DRIVER_WAYLAND
SDL_SysWMinfo info;
SDL_VERSION(&info.version);
if (!SDL_GetWindowWMInfo(window, &info)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_GetWindowWMInfo() failed: %s",
SDL_GetError());
return NULL;
}
if (info.subsystem == SDL_SYSWM_WAYLAND) {
return info.info.wl.display;
}
#else
(void)window;
#endif
return NULL;
}
void* SDLC_Wayland_GetSurface(SDL_Window* window)
{
#ifdef SDL_VIDEO_DRIVER_WAYLAND
SDL_SysWMinfo info;
SDL_VERSION(&info.version);
if (!SDL_GetWindowWMInfo(window, &info)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_GetWindowWMInfo() failed: %s",
SDL_GetError());
return NULL;
}
if (info.subsystem == SDL_SYSWM_WAYLAND) {
return info.info.wl.surface;
}
#else
(void)window;
#endif
return NULL;
}
int SDLC_KMSDRM_GetFd(SDL_Window* window)
{
#if defined(SDL_VIDEO_DRIVER_KMSDRM) && SDL_VERSION_ATLEAST(2, 0, 15)
SDL_SysWMinfo info;
SDL_VERSION(&info.version);
if (!SDL_GetWindowWMInfo(window, &info)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_GetWindowWMInfo() failed: %s",
SDL_GetError());
return -1;
}
if (info.subsystem == SDL_SYSWM_KMSDRM) {
return info.info.kmsdrm.drm_fd;
}
#else
(void)window;
#endif
return -1;
}
int SDLC_KMSDRM_GetDevIndex(SDL_Window* window)
{
#if defined(SDL_VIDEO_DRIVER_KMSDRM) && SDL_VERSION_ATLEAST(2, 0, 15)
SDL_SysWMinfo info;
SDL_VERSION(&info.version);
if (!SDL_GetWindowWMInfo(window, &info)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_GetWindowWMInfo() failed: %s",
SDL_GetError());
return -1;
}
if (info.subsystem == SDL_SYSWM_KMSDRM) {
return info.info.kmsdrm.dev_index;
}
#else
(void)window;
#endif
return -1;
}
SDLC_VideoDriver SDLC_GetVideoDriver(void)
{
#if defined(SDL_VIDEO_DRIVER_WINDOWS)
return SDLC_VIDEO_WIN32;
#elif defined(SDL_VIDEO_DRIVER_COCOA)
return SDLC_VIDEO_MACOS;
#else
const char* videoDriver = SDL_GetCurrentVideoDriver();
if (SDL_strcmp(videoDriver, "x11") == 0) {
return SDLC_VIDEO_X11;
}
else if (SDL_strcmp(videoDriver, "wayland") == 0) {
return SDLC_VIDEO_WAYLAND;
}
else if (SDL_strcmp(videoDriver, "kmsdrm") == 0) {
return SDLC_VIDEO_KMSDRM;
}
else {
SDL_assert(0);
return SDLC_VIDEO_UNKNOWN;
}
#endif
}
bool SDLC_IsFullscreen(SDL_Window* window)
{
return !!(SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN);
}
bool SDLC_IsFullscreenExclusive(SDL_Window* window)
{
return (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN;
}
bool SDLC_IsFullscreenDesktop(SDL_Window* window)
{
return (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP;
}
void SDLC_EnterFullscreen(SDL_Window* window, bool exclusive)
{
SDL_SetWindowFullscreen(window, exclusive ? SDL_WINDOW_FULLSCREEN : SDL_WINDOW_FULLSCREEN_DESKTOP);
}
void SDLC_LeaveFullscreen(SDL_Window* window)
{
SDL_SetWindowFullscreen(window, 0);
}
SDL_Window* SDLC_CreateWindowWithFallback(const char *title,
int x, int y, int w, int h,
Uint32 requiredFlags,
Uint32 optionalFlags)
{
SDL_Window* window = SDL_CreateWindow(title, x, y, w, h, requiredFlags | optionalFlags);
if (!window) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Failed to create window with optional flags: %s",
SDL_GetError());
// Try the fallback flags now
window = SDL_CreateWindow(title, x, y, w, h, requiredFlags);
}
return window;
}
void SDLC_FlushWindowEvents(void)
{
SDL_FlushEvent(SDL_WINDOWEVENT);
}
SDL_JoystickID* SDL_GetGamepads(int *count)
{
int numJoysticks = SDL_NumJoysticks();
SDL_JoystickID* ids = SDL_calloc(numJoysticks + 1, sizeof(SDL_JoystickID));
int numGamepads = 0;
for (int i = 0; i < numJoysticks; i++) {
if (SDL_IsGameController(i)) {
ids[numGamepads++] = i;
}
}
if (count != NULL) {
*count = numGamepads;
}
return ids;
}
SDL_JoystickID* SDL_GetJoysticks(int *count)
{
int numJoysticks = SDL_NumJoysticks();
SDL_JoystickID* ids = SDL_calloc(numJoysticks + 1, sizeof(SDL_JoystickID));
for (int i = 0; i < numJoysticks; i++) {
ids[i] = i;
}
if (count != NULL) {
*count = numJoysticks;
}
return ids;
}
SDL_DisplayID * SDL_GetDisplays(int *count)
{
int numDisplays = SDL_GetNumVideoDisplays();
SDL_DisplayID* ids = SDL_calloc(numDisplays + 1, sizeof(SDL_DisplayID));
for (int i = 0; i < numDisplays; i++) {
ids[i] = i;
}
if (count != NULL) {
*count = numDisplays;
}
return ids;
}
const SDL_DisplayMode * SDL_GetWindowFullscreenMode(SDL_Window *window)
{
static SDL_DisplayMode mode;
if (SDL_GetWindowDisplayMode(window, &mode) == 0) {
return &mode;
}
else {
return NULL;
}
}
+303
View File
@@ -0,0 +1,303 @@
//
// Compatibility header for older versions of SDL.
// Include this instead of SDL.h directly.
//
#pragma once
#include <SDL.h>
#include <stdbool.h>
// This is a pure C header for compatibility with SDL.h
#ifdef __cplusplus
extern "C" {
#endif
// These SLDC_* functions and constants are special SDL-like things
// used to abstract certain SDL2 vs SDL3 differences.
void* SDLC_Win32_GetHwnd(SDL_Window* window);
void* SDLC_MacOS_GetWindow(SDL_Window* window);
void* SDLC_X11_GetDisplay(SDL_Window* window);
unsigned long SDLC_X11_GetWindow(SDL_Window* window);
void* SDLC_Wayland_GetDisplay(SDL_Window* window);
void* SDLC_Wayland_GetSurface(SDL_Window* window);
int SDLC_KMSDRM_GetFd(SDL_Window* window);
int SDLC_KMSDRM_GetDevIndex(SDL_Window* window);
typedef enum {
SDLC_VIDEO_UNKNOWN,
SDLC_VIDEO_WIN32,
SDLC_VIDEO_MACOS,
SDLC_VIDEO_X11,
SDLC_VIDEO_WAYLAND,
SDLC_VIDEO_KMSDRM,
} SDLC_VideoDriver;
SDLC_VideoDriver SDLC_GetVideoDriver();
bool SDLC_IsFullscreen(SDL_Window* window);
bool SDLC_IsFullscreenExclusive(SDL_Window* window);
bool SDLC_IsFullscreenDesktop(SDL_Window* window);
void SDLC_EnterFullscreen(SDL_Window* window, bool exclusive);
void SDLC_LeaveFullscreen(SDL_Window* window);
SDL_Window* SDLC_CreateWindowWithFallback(const char *title,
int x, int y, int w, int h,
Uint32 requiredFlags,
Uint32 optionalFlags);
void SDLC_FlushWindowEvents();
#define SDLC_SUCCESS(x) ((x) == 0)
#define SDLC_FAILURE(x) ((x) != 0)
// SDL_FRect wasn't added until 2.0.10
#if !SDL_VERSION_ATLEAST(2, 0, 10)
typedef struct SDL_FRect
{
float x;
float y;
float w;
float h;
} SDL_FRect;
#endif
#ifndef SDL_THREAD_PRIORITY_TIME_CRITICAL
#define SDL_THREAD_PRIORITY_TIME_CRITICAL SDL_THREAD_PRIORITY_HIGH
#endif
#ifndef SDL_HINT_VIDEO_X11_FORCE_EGL
#define SDL_HINT_VIDEO_X11_FORCE_EGL "SDL_VIDEO_X11_FORCE_EGL"
#endif
#ifndef SDL_HINT_KMSDRM_REQUIRE_DRM_MASTER
#define SDL_HINT_KMSDRM_REQUIRE_DRM_MASTER "SDL_KMSDRM_REQUIRE_DRM_MASTER"
#endif
#ifndef SDL_HINT_ALLOW_ALT_TAB_WHILE_GRABBED
#define SDL_HINT_ALLOW_ALT_TAB_WHILE_GRABBED "SDL_ALLOW_ALT_TAB_WHILE_GRABBED"
#endif
#ifndef SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE
#define SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE "SDL_JOYSTICK_HIDAPI_PS4_RUMBLE"
#endif
#ifndef SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE
#define SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE "SDL_JOYSTICK_HIDAPI_PS5_RUMBLE"
#endif
#ifndef SDL_HINT_WINDOWS_USE_D3D9EX
#define SDL_HINT_WINDOWS_USE_D3D9EX "SDL_WINDOWS_USE_D3D9EX"
#endif
#ifndef SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS
#define SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS "SDL_GAMECONTROLLER_USE_BUTTON_LABELS"
#endif
#ifndef SDL_HINT_MOUSE_RELATIVE_SCALING
#define SDL_HINT_MOUSE_RELATIVE_SCALING "SDL_MOUSE_RELATIVE_SCALING"
#endif
#ifndef SDL_HINT_AUDIO_DEVICE_APP_NAME
#define SDL_HINT_AUDIO_DEVICE_APP_NAME "SDL_AUDIO_DEVICE_APP_NAME"
#endif
#ifndef SDL_HINT_APP_NAME
#define SDL_HINT_APP_NAME "SDL_APP_NAME"
#endif
#ifndef SDL_HINT_MOUSE_AUTO_CAPTURE
#define SDL_HINT_MOUSE_AUTO_CAPTURE "SDL_MOUSE_AUTO_CAPTURE"
#endif
#ifndef SDL_HINT_VIDEO_WAYLAND_EMULATE_MOUSE_WARP
#define SDL_HINT_VIDEO_WAYLAND_EMULATE_MOUSE_WARP "SDL_VIDEO_WAYLAND_EMULATE_MOUSE_WARP"
#endif
// SDL3 renamed hints
#define SDL_HINT_VIDEO_FORCE_EGL SDL_HINT_VIDEO_X11_FORCE_EGL
// Events
#define SDL_EVENT_QUIT SDL_QUIT
#define SDL_EVENT_CLIPBOARD_UPDATE SDL_CLIPBOARDUPDATE
#define SDL_EVENT_GAMEPAD_ADDED SDL_CONTROLLERDEVICEADDED
#define SDL_EVENT_GAMEPAD_REMAPPED SDL_CONTROLLERDEVICEREMAPPED
#define SDL_EVENT_GAMEPAD_REMOVED SDL_CONTROLLERDEVICEREMOVED
#define SDL_EVENT_GAMEPAD_SENSOR_UPDATE SDL_CONTROLLERSENSORUPDATE
#define SDL_EVENT_GAMEPAD_BUTTON_DOWN SDL_CONTROLLERBUTTONDOWN
#define SDL_EVENT_GAMEPAD_BUTTON_UP SDL_CONTROLLERBUTTONUP
#define SDL_EVENT_GAMEPAD_AXIS_MOTION SDL_CONTROLLERAXISMOTION
#define SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN SDL_CONTROLLERTOUCHPADDOWN
#define SDL_EVENT_GAMEPAD_TOUCHPAD_UP SDL_CONTROLLERTOUCHPADUP
#define SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION SDL_CONTROLLERTOUCHPADMOTION
#define SDL_EVENT_JOYSTICK_ADDED SDL_JOYDEVICEADDED
#define SDL_EVENT_JOYSTICK_REMOVED SDL_JOYDEVICEREMOVED
#define SDL_EVENT_JOYSTICK_BATTERY_UPDATED SDL_JOYBATTERYUPDATED
#define SDL_EVENT_FINGER_DOWN SDL_FINGERDOWN
#define SDL_EVENT_FINGER_MOTION SDL_FINGERMOTION
#define SDL_EVENT_FINGER_UP SDL_FINGERUP
#define SDL_EVENT_KEY_DOWN SDL_KEYDOWN
#define SDL_EVENT_KEY_UP SDL_KEYUP
#define SDL_EVENT_MOUSE_BUTTON_DOWN SDL_MOUSEBUTTONDOWN
#define SDL_EVENT_MOUSE_BUTTON_UP SDL_MOUSEBUTTONUP
#define SDL_EVENT_MOUSE_MOTION SDL_MOUSEMOTION
#define SDL_EVENT_MOUSE_WHEEL SDL_MOUSEWHEEL
#define SDL_EVENT_RENDER_DEVICE_RESET SDL_RENDER_DEVICE_RESET
#define SDL_EVENT_RENDER_TARGETS_RESET SDL_RENDER_TARGETS_RESET
#define SDL_EVENT_USER SDL_USEREVENT
#define SDL_EVENT_WINDOW_FOCUS_GAINED SDL_WINDOWEVENT_FOCUS_GAINED
#define SDL_EVENT_WINDOW_FOCUS_LOST SDL_WINDOWEVENT_FOCUS_LOST
#define SDL_EVENT_WINDOW_MOUSE_ENTER SDL_WINDOWEVENT_ENTER
#define SDL_EVENT_WINDOW_MOUSE_LEAVE SDL_WINDOWEVENT_LEAVE
#define SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED SDL_WINDOWEVENT_SIZE_CHANGED
#define SDL_EVENT_WINDOW_SHOWN SDL_WINDOWEVENT_SHOWN
#define SDL_EVENT_WINDOW_DISPLAY_CHANGED SDL_WINDOWEVENT_DISPLAY_CHANGED
#define SDL_BUTTON_MASK(x) SDL_BUTTON(x)
#define gbutton cbutton
#define gaxis caxis
#define gdevice cdevice
#define gsensor csensor
#define gtouchpad ctouchpad
#define fingerID fingerId
#define touchID touchId
#define KEY_DOWN(x) ((x)->state == SDL_PRESSED)
#define KEY_KEY(x) ((x)->keysym.sym)
#define KEY_MOD(x) ((x)->keysym.mod)
#define KEY_SCANCODE(x) ((x)->keysym.scancode)
// Gamepad
#define SDL_INIT_GAMEPAD SDL_INIT_GAMECONTROLLER
#define SDL_GAMEPAD_BUTTON_SOUTH SDL_CONTROLLER_BUTTON_A
#define SDL_GAMEPAD_BUTTON_EAST SDL_CONTROLLER_BUTTON_B
#define SDL_GAMEPAD_BUTTON_WEST SDL_CONTROLLER_BUTTON_X
#define SDL_GAMEPAD_BUTTON_NORTH SDL_CONTROLLER_BUTTON_Y
#define SDL_GAMEPAD_BUTTON_LEFT_SHOULDER SDL_CONTROLLER_BUTTON_LEFTSHOULDER
#define SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER SDL_CONTROLLER_BUTTON_RIGHTSHOULDER
#define SDL_GAMEPAD_BUTTON_DPAD_UP SDL_CONTROLLER_BUTTON_DPAD_UP
#define SDL_GAMEPAD_BUTTON_DPAD_DOWN SDL_CONTROLLER_BUTTON_DPAD_DOWN
#define SDL_GAMEPAD_BUTTON_DPAD_LEFT SDL_CONTROLLER_BUTTON_DPAD_LEFT
#define SDL_GAMEPAD_BUTTON_DPAD_RIGHT SDL_CONTROLLER_BUTTON_DPAD_RIGHT
#define SDL_GAMEPAD_BUTTON_START SDL_CONTROLLER_BUTTON_START
#define SDL_GAMEPAD_BUTTON_TOUCHPAD SDL_CONTROLLER_BUTTON_TOUCHPAD
#define SDL_GAMEPAD_BINDTYPE_AXIS SDL_CONTROLLER_BINDTYPE_AXIS
#define SDL_GAMEPAD_BINDTYPE_BUTTON SDL_CONTROLLER_BINDTYPE_BUTTON
#define SDL_GAMEPAD_BINDTYPE_HAT SDL_CONTROLLER_BINDTYPE_HAT
#define SDL_GAMEPAD_BINDTYPE_NONE SDL_CONTROLLER_BINDTYPE_NONE
#define SDL_GAMEPAD_AXIS_LEFTX SDL_CONTROLLER_AXIS_LEFTX
#define SDL_GAMEPAD_AXIS_LEFTY SDL_CONTROLLER_AXIS_LEFTY
#define SDL_GAMEPAD_AXIS_RIGHTX SDL_CONTROLLER_AXIS_RIGHTX
#define SDL_GAMEPAD_AXIS_RIGHTY SDL_CONTROLLER_AXIS_RIGHTY
#define SDL_GAMEPAD_AXIS_LEFT_TRIGGER SDL_CONTROLLER_AXIS_TRIGGERLEFT
#define SDL_GAMEPAD_AXIS_RIGHT_TRIGGER SDL_CONTROLLER_AXIS_TRIGGERRIGHT
// SDL_OpenGamepad() not defined due to differing semantics
SDL_JoystickID* SDL_GetGamepads(int *count);
#define SDL_OpenGamepad(x) SDL_GameControllerOpen(x)
#define SDL_CloseGamepad(x) SDL_GameControllerClose(x)
#define SDL_GetGamepadMapping(x) SDL_GameControllerMapping(x)
#define SDL_GetGamepadName(x) SDL_GameControllerName(x)
#define SDL_GetGamepadVendor(x) SDL_GameControllerGetVendor(x)
#define SDL_GetGamepadProduct(x) SDL_GameControllerGetProduct(x)
#define SDL_GetGamepadJoystick(x) SDL_GameControllerGetJoystick(x)
#define SDL_GamepadHasButton(x, y) SDL_GameControllerHasButton(x, y)
#define SDL_GamepadHasSensor(x, y) SDL_GameControllerHasSensor(x, y)
#define SDL_SetGamepadPlayerIndex(x, y) SDL_GameControllerSetPlayerIndex(x, y)
#define SDL_GamepadSensorEnabled(x, y) SDL_GameControllerIsSensorEnabled(x, y)
#define SDL_SetGamepadSensorEnabled(x, y, z) SDL_GameControllerSetSensorEnabled(x, y, z)
#define SDL_GetNumGamepadTouchpads(x) SDL_GameControllerGetNumTouchpads(x)
#define SDL_SetGamepadLED(x, r, g, b) SDL_GameControllerSetLED(x, r, g, b)
#define SDL_RumbleGamepad(x, y, z, w) SDL_GameControllerRumble(x, y, z, w)
#define SDL_RumbleGamepadTriggers(x, y, z, w) SDL_GameControllerRumbleTriggers(x, y, z, w)
#define SDL_GetGamepadAxis(x, y) SDL_GameControllerGetAxis(x, y)
#define SDL_GetGamepadType(x) SDL_GameControllerGetType(x)
#define SDL_IsGamepad(x) SDL_IsGameController(x)
#define SDL_GAMEPAD_TYPE_STANDARD SDL_CONTROLLER_TYPE_UNKNOWN
#define SDL_GAMEPAD_TYPE_VIRTUAL SDL_CONTROLLER_TYPE_VIRTUAL
#define SDL_GAMEPAD_TYPE_XBOX360 SDL_CONTROLLER_TYPE_XBOX360
#define SDL_GAMEPAD_TYPE_XBOXONE SDL_CONTROLLER_TYPE_XBOXONE
#define SDL_GAMEPAD_TYPE_PS3 SDL_CONTROLLER_TYPE_PS3
#define SDL_GAMEPAD_TYPE_PS4 SDL_CONTROLLER_TYPE_PS4
#define SDL_GAMEPAD_TYPE_PS5 SDL_CONTROLLER_TYPE_PS5
#define SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_LEFT
#define SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_PAIR SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_PAIR
#define SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT
#define SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO
// SDL_OpenGamepad() not defined due to differing semantics
SDL_JoystickID* SDL_GetJoysticks(int *count);
#define SDL_OpenJoystick(x) SDL_JoystickOpen(x)
#define SDL_CloseJoystick(x) SDL_JoystickClose(x)
#define SDL_GetJoystickID(x) SDL_JoystickInstanceID(x)
#define SDL_GetJoystickGUID(x) SDL_JoystickGetGUID(x)
#define SDL_GetJoystickPowerLevel(x) SDL_JoystickCurrentPowerLevel(x)
#define SDL_GetNumJoystickAxes(x) SDL_JoystickNumAxes(x)
#define SDL_GetNumJoystickBalls(x) SDL_JoystickNumBalls(x)
#define SDL_GetNumJoystickButtons(x) SDL_JoystickNumButtons(x)
#define SDL_GetNumJoystickHats(x) SDL_JoystickNumHats(x)
#define SDL_GetJoystickGUIDForID(x) SDL_JoystickGetDeviceGUID(x)
typedef SDL_ControllerAxisEvent SDL_GamepadAxisEvent;
typedef SDL_ControllerButtonEvent SDL_GamepadButtonEvent;
typedef SDL_ControllerSensorEvent SDL_GamepadSensorEvent;
typedef SDL_ControllerTouchpadEvent SDL_GamepadTouchpadEvent;
typedef SDL_ControllerDeviceEvent SDL_GamepadDeviceEvent;
typedef SDL_GameController SDL_Gamepad;
typedef SDL_GameControllerButton SDL_GamepadButton;
// Audio
#define SDL_AUDIO_F32 AUDIO_F32SYS
#define SDL_ResumeAudioDevice(x) SDL_PauseAudioDevice(x, 0)
// Atomics
#define SDL_GetAtomicInt(x) SDL_AtomicGet(x)
#define SDL_SetAtomicInt(x, y) SDL_AtomicSet(x, y)
#define SDL_GetAtomicPointer(x) SDL_AtomicGetPtr(x)
#define SDL_SetAtomicPointer(x, y) SDL_AtomicSetPtr(x, y)
#define SDL_LockSpinlock(x) SDL_AtomicLock(x)
#define SDL_TryLockSpinlock(x) SDL_AtomicTryLock(x)
#define SDL_UnlockSpinlock(x) SDL_AtomicUnlock(x)
typedef SDL_atomic_t SDL_AtomicInt;
// Video
#define SDL_KMOD_CTRL KMOD_CTRL
#define SDL_KMOD_ALT KMOD_ALT
#define SDL_KMOD_SHIFT KMOD_SHIFT
#define SDL_KMOD_GUI KMOD_GUI
#define SDL_WINDOW_HIGH_PIXEL_DENSITY SDL_WINDOW_ALLOW_HIGHDPI
#define SDL_SetWindowFullscreenMode(x, y) SDL_SetWindowDisplayMode(x, y)
#define SDL_GetDisplayForWindow(x) SDL_GetWindowDisplayIndex(x)
#define SDL_GetRenderViewport(x, y) SDL_RenderGetViewport(x, y)
#define SDL_DestroySurface(x) SDL_FreeSurface(x)
#define SDL_RenderTexture(x, y, z, w) SDL_RenderCopy(x, y, z, w)
#define SDL_GetPrimaryDisplay() (0)
#define SDL_CreateSurfaceFrom(w, h, fmt, pixels, pitch) SDL_CreateRGBSurfaceWithFormatFrom(pixels, w, h, SDL_BITSPERPIXEL(fmt), pitch, fmt)
#define SDLC_DEFAULT_RENDER_DRIVER -1
typedef int SDL_DisplayID;
SDL_DisplayID * SDL_GetDisplays(int *count);
const SDL_DisplayMode * SDL_GetWindowFullscreenMode(SDL_Window *window);
// Misc
#define SDL_GetNumLogicalCPUCores() SDL_GetCPUCount()
#define SDL_IOFromConstMem(x, y) SDL_RWFromConstMem(x, y)
#define SDL_SetCurrentThreadPriority(x) SDL_SetThreadPriority(x)
#define SDL_GUIDToString(x, y, z) SDL_JoystickGetGUIDString(x, y, z)
#ifdef __cplusplus
}
#endif
+15 -1
View File
@@ -69,7 +69,12 @@ macx:!disable-prebuilts {
unix:if(!macx|disable-prebuilts) {
CONFIG += link_pkgconfig
PKGCONFIG += openssl sdl2 SDL2_ttf opus
PKGCONFIG += openssl sdl2 SDL2_ttf
# We have our own optimized libopus.a for Steam Link
if(!config_SL|disable-prebuilts) {
PKGCONFIG += opus
}
!disable-ffmpeg {
packagesExist(libavcodec) {
@@ -169,6 +174,7 @@ macx {
}
SOURCES += \
SDL_compat.c \
backend/nvaddress.cpp \
backend/nvapp.cpp \
cli/pair.cpp \
@@ -209,6 +215,7 @@ SOURCES += \
wm.cpp
HEADERS += \
SDL_compat.h \
backend/nvaddress.h \
backend/nvapp.h \
cli/pair.h \
@@ -364,6 +371,13 @@ config_EGL {
config_SL {
message(Steam Link build configuration selected)
!disable-prebuilts {
# Link against our NEON-optimized libopus build
LIBS += -L$$PWD/../libs/steamlink/lib
INCLUDEPATH += $$PWD/../libs/steamlink/include
LIBS += -lopus -larmasm -lNE10
}
DEFINES += EMBEDDED_BUILD STEAM_LINK HAVE_SLVIDEO HAVE_SLAUDIO
LIBS += -lSLVideo -lSLAudio
+21 -22
View File
@@ -128,7 +128,7 @@ void SystemProperties::querySdlVideoInfoInternal()
{
hasHardwareAcceleration = false;
if (SDL_InitSubSystem(SDL_INIT_VIDEO) != 0) {
if (SDLC_FAILURE(SDL_InitSubSystem(SDL_INIT_VIDEO))) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_InitSubSystem(SDL_INIT_VIDEO) failed: %s",
SDL_GetError());
@@ -139,21 +139,15 @@ void SystemProperties::querySdlVideoInfoInternal()
// We call the internal variant because we're already in a safe thread context.
refreshDisplaysInternal();
SDL_Window* testWindow = SDL_CreateWindow("", 0, 0, 1280, 720,
SDL_WINDOW_HIDDEN | StreamUtils::getPlatformWindowFlags());
SDL_Window* testWindow = SDLC_CreateWindowWithFallback("", 0, 0, 1280, 720,
SDL_WINDOW_HIDDEN,
StreamUtils::getPlatformWindowFlags());
if (!testWindow) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Failed to create test window with platform flags: %s",
SDL_GetError());
testWindow = SDL_CreateWindow("", 0, 0, 1280, 720, SDL_WINDOW_HIDDEN);
if (!testWindow) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Failed to create window for hardware decode test: %s",
SDL_GetError());
SDL_QuitSubSystem(SDL_INIT_VIDEO);
return;
}
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Failed to create window for hardware decode test: %s",
SDL_GetError());
SDL_QuitSubSystem(SDL_INIT_VIDEO);
return;
}
Session::getDecoderInfo(testWindow, hasHardwareAcceleration, rendererAlwaysFullScreen, supportsHdr, maximumResolution);
@@ -194,7 +188,7 @@ void SystemProperties::refreshDisplays()
void SystemProperties::refreshDisplaysInternal()
{
if (SDL_InitSubSystem(SDL_INIT_VIDEO) != 0) {
if (SDLC_FAILURE(SDL_InitSubSystem(SDL_INIT_VIDEO))) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_InitSubSystem(SDL_INIT_VIDEO) failed: %s",
SDL_GetError());
@@ -203,15 +197,18 @@ void SystemProperties::refreshDisplaysInternal()
monitorNativeResolutions.clear();
int numDisplays = 0;
SDL_DisplayID *displays = SDL_GetDisplays(&numDisplays);
SDL_DisplayMode bestMode;
for (int displayIndex = 0; displayIndex < SDL_GetNumVideoDisplays(); displayIndex++) {
for (int i = 0; i < numDisplays; i++) {
SDL_DisplayMode desktopMode;
SDL_Rect safeArea;
if (StreamUtils::getNativeDesktopMode(displayIndex, &desktopMode, &safeArea)) {
if (StreamUtils::getNativeDesktopMode(displays[i], &desktopMode, &safeArea)) {
if (desktopMode.w <= 8192 && desktopMode.h <= 8192) {
monitorNativeResolutions.insert(displayIndex, QRect(0, 0, desktopMode.w, desktopMode.h));
monitorSafeAreaResolutions.insert(displayIndex, QRect(0, 0, safeArea.w, safeArea.h));
monitorNativeResolutions.insert(i, QRect(0, 0, desktopMode.w, desktopMode.h));
monitorSafeAreaResolutions.insert(i, QRect(0, 0, safeArea.w, safeArea.h));
}
else {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
@@ -221,9 +218,10 @@ void SystemProperties::refreshDisplaysInternal()
// Start at desktop mode and work our way up
bestMode = desktopMode;
for (int i = 0; i < SDL_GetNumDisplayModes(displayIndex); i++) {
int numDisplayModes = SDL_GetNumDisplayModes(displays[i]);
for (int i = 0; i < numDisplayModes; i++) {
SDL_DisplayMode mode;
if (SDL_GetDisplayMode(displayIndex, i, &mode) == 0) {
if (SDL_GetDisplayMode(displays[i], i, &mode) == 0) {
if (mode.w == desktopMode.w && mode.h == desktopMode.h) {
if (mode.refresh_rate > bestMode.refresh_rate) {
bestMode = mode;
@@ -246,5 +244,6 @@ void SystemProperties::refreshDisplaysInternal()
}
}
SDL_free(displays);
SDL_QuitSubSystem(SDL_INIT_VIDEO);
}
+1 -1
View File
@@ -14,4 +14,4 @@ renice -10 -p $(pidof PE_Single_CPU)
# Renice Moonlight itself to avoid preemption by background tasks
# Write output to a logfile in /tmp
exec nice -n -10 ./bin/moonlight > /tmp/moonlight.log
exec nice -n -10 ./bin/moonlight > /tmp/moonlight.log 2>&1
-5
View File
@@ -2,7 +2,6 @@ import QtQuick 2.0
import QtQuick.Controls 2.2
import ComputerManager 1.0
import SdlGamepadKeyNavigation 1.0
Item {
function onSearchingComputer() {
@@ -39,10 +38,6 @@ Item {
if (!launcher.isExecuted()) {
toolBar.visible = false
// Normally this is enabled by PcView, but we will won't
// load PcView when streaming from the command-line.
SdlGamepadKeyNavigation.enable()
launcher.searchingComputer.connect(onSearchingComputer)
launcher.pairing.connect(onPairing)
launcher.failed.connect(onFailed)
-5
View File
@@ -2,7 +2,6 @@ import QtQuick 2.0
import QtQuick.Controls 2.2
import ComputerManager 1.0
import SdlGamepadKeyNavigation 1.0
Item {
function onSearchingComputer() {
@@ -38,10 +37,6 @@ Item {
if (!launcher.isExecuted()) {
toolBar.visible = false
// Normally this is enabled by PcView, but we will won't
// load PcView when streaming from the command-line.
SdlGamepadKeyNavigation.enable()
launcher.searchingComputer.connect(onSearchingComputer)
launcher.searchingApp.connect(onSearchingApp)
launcher.sessionCreated.connect(onSessionCreated)
-5
View File
@@ -34,11 +34,6 @@ CenteredGridView {
// Setup signals on CM
ComputerManager.computerAddCompleted.connect(addComplete)
// This is a bit of a hack to do this here as opposed to main.qml, but
// we need it enabled before calling getConnectedGamepads() and PcView
// is never destroyed, so it should be okay.
SdlGamepadKeyNavigation.enable()
// Highlight the first item if a gamepad is connected
if (currentIndex == -1 && SdlGamepadKeyNavigation.getConnectedGamepads() > 0) {
currentIndex = 0
+2 -2
View File
@@ -76,7 +76,7 @@ Item {
streamSegueErrorDialog.text += "\n\n" + qsTr("This PC's Internet connection is blocking Moonlight. Streaming over the Internet may not work while connected to this network.")
}
// Enable GUI gamepad usage now
// Re-enable GUI gamepad usage now
SdlGamepadKeyNavigation.enable()
if (quitAfter) {
@@ -119,7 +119,7 @@ Item {
// Show the toolbar again when popped off the stack
toolBar.visible = true
// Enable GUI gamepad usage now
// Re-enable GUI gamepad usage now
SdlGamepadKeyNavigation.enable()
}
+19 -2
View File
@@ -22,7 +22,8 @@ ApplicationWindow {
width: 1280
height: 600
Component.onCompleted: {
// This function runs prior to creation of the initial StackView item
function doEarlyInit() {
// Override the background color to Material 2 colors for Qt 6.5+
// in order to improve contrast between GFE's placeholder box art
// and the background of the app grid.
@@ -30,6 +31,10 @@ ApplicationWindow {
Material.background = "#303030"
}
SdlGamepadKeyNavigation.enable()
}
Component.onCompleted: {
// Show the window according to the user's preferences
if (SystemProperties.hasDesktopEnvironment) {
if (StreamingPreferences.uiDisplayMode == StreamingPreferences.UI_MAXIMIZED) {
@@ -81,10 +86,16 @@ ApplicationWindow {
StackView {
id: stackView
initialItem: initialView
anchors.fill: parent
focus: true
Component.onCompleted: {
// Perform our early initialization before constructing
// the initial view and pushing it to the StackView
doEarlyInit()
push(initialView)
}
onCurrentItemChanged: {
// Ensure focus travels to the next view when going back
if (currentItem) {
@@ -158,6 +169,9 @@ ApplicationWindow {
pollingActive = true
}
}
// Poll for gamepad input only when the window is in focus
SdlGamepadKeyNavigation.notifyWindowFocus(visible && active)
}
onActiveChanged: {
@@ -176,6 +190,9 @@ ApplicationWindow {
// if focus does not return within a few minutes.
inactivityTimer.restart()
}
// Poll for gamepad input only when the window is in focus
SdlGamepadKeyNavigation.notifyWindowFocus(visible && active)
}
// Workaround for lack of instanceof in Qt 5.9.
+77 -55
View File
@@ -13,6 +13,7 @@ SdlGamepadKeyNavigation::SdlGamepadKeyNavigation(StreamingPreferences* prefs)
m_Enabled(false),
m_UiNavMode(false),
m_FirstPoll(false),
m_HasFocus(false),
m_LastAxisNavigationEventTime(0)
{
m_PollingTimer = new QTimer(this);
@@ -36,7 +37,7 @@ void SdlGamepadKeyNavigation::enable()
// arrival events. Additionally, there's a race condition between
// our QML objects being destroyed and SDL being deinitialized that
// this solves too.
if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) != 0) {
if (SDLC_FAILURE(SDL_InitSubSystem(SDL_INIT_GAMEPAD))) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) failed: %s",
SDL_GetError());
@@ -51,25 +52,23 @@ void SdlGamepadKeyNavigation::enable()
// overlapping lifetimes of SdlGamepadKeyNavigation instances, so we
// will attach ourselves.
SDL_PumpEvents();
SDL_FlushEvent(SDL_CONTROLLERDEVICEADDED);
SDL_FlushEvent(SDL_EVENT_GAMEPAD_ADDED);
// Open all currently attached game controllers
for (int i = 0; i < SDL_NumJoysticks(); i++) {
if (SDL_IsGameController(i)) {
SDL_GameController* gc = SDL_GameControllerOpen(i);
if (gc != nullptr) {
m_Gamepads.append(gc);
}
int numGamepads = 0;
SDL_JoystickID* gamepads = SDL_GetGamepads(&numGamepads);
for (int i = 0; i < numGamepads; i++) {
SDL_Gamepad * gc = SDL_OpenGamepad(i);
if (gc != nullptr) {
m_Gamepads.append(gc);
}
}
// Flush events on the first poll
m_FirstPoll = true;
// Poll every 50 ms for a new joystick event
m_PollingTimer->start(50);
SDL_free(gamepads);
m_Enabled = true;
// Start the polling timer if the window is focused
updateTimerState();
}
void SdlGamepadKeyNavigation::disable()
@@ -78,16 +77,22 @@ void SdlGamepadKeyNavigation::disable()
return;
}
m_PollingTimer->stop();
m_Enabled = false;
updateTimerState();
Q_ASSERT(!m_PollingTimer->isActive());
while (!m_Gamepads.isEmpty()) {
SDL_GameControllerClose(m_Gamepads[0]);
SDL_CloseGamepad(m_Gamepads[0]);
m_Gamepads.removeAt(0);
}
SDL_QuitSubSystem(SDL_INIT_GAMECONTROLLER);
SDL_QuitSubSystem(SDL_INIT_GAMEPAD);
}
m_Enabled = false;
void SdlGamepadKeyNavigation::notifyWindowFocus(bool hasFocus)
{
m_HasFocus = hasFocus;
updateTimerState();
}
void SdlGamepadKeyNavigation::onPollingTimerFired()
@@ -98,47 +103,47 @@ void SdlGamepadKeyNavigation::onPollingTimerFired()
// stale input data from the stream session (like the quit combo).
if (m_FirstPoll) {
SDL_PumpEvents();
SDL_FlushEvent(SDL_CONTROLLERBUTTONDOWN);
SDL_FlushEvent(SDL_CONTROLLERBUTTONUP);
SDL_FlushEvent(SDL_EVENT_GAMEPAD_BUTTON_DOWN);
SDL_FlushEvent(SDL_EVENT_GAMEPAD_BUTTON_UP);
m_FirstPoll = false;
}
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_QUIT:
case SDL_EVENT_QUIT :
// SDL may send us a quit event since we initialize
// the video subsystem on startup. If we get one,
// forward it on for Qt to take care of.
QCoreApplication::instance()->quit();
break;
case SDL_CONTROLLERBUTTONDOWN:
case SDL_CONTROLLERBUTTONUP:
case SDL_EVENT_GAMEPAD_BUTTON_DOWN :
case SDL_EVENT_GAMEPAD_BUTTON_UP :
{
QEvent::Type type =
event.type == SDL_CONTROLLERBUTTONDOWN ?
event.type == SDL_EVENT_GAMEPAD_BUTTON_DOWN ?
QEvent::Type::KeyPress : QEvent::Type::KeyRelease;
// Swap face buttons if needed
if (m_Prefs->swapFaceButtons) {
switch (event.cbutton.button) {
case SDL_CONTROLLER_BUTTON_A:
event.cbutton.button = SDL_CONTROLLER_BUTTON_B;
switch (event.gbutton.button) {
case SDL_GAMEPAD_BUTTON_SOUTH :
event.gbutton.button = SDL_GAMEPAD_BUTTON_EAST;
break;
case SDL_CONTROLLER_BUTTON_B:
event.cbutton.button = SDL_CONTROLLER_BUTTON_A;
case SDL_GAMEPAD_BUTTON_EAST :
event.gbutton.button = SDL_GAMEPAD_BUTTON_SOUTH;
break;
case SDL_CONTROLLER_BUTTON_X:
event.cbutton.button = SDL_CONTROLLER_BUTTON_Y;
case SDL_GAMEPAD_BUTTON_WEST :
event.gbutton.button = SDL_GAMEPAD_BUTTON_NORTH;
break;
case SDL_CONTROLLER_BUTTON_Y:
event.cbutton.button = SDL_CONTROLLER_BUTTON_X;
case SDL_GAMEPAD_BUTTON_NORTH :
event.gbutton.button = SDL_GAMEPAD_BUTTON_WEST;
break;
}
}
switch (event.cbutton.button) {
case SDL_CONTROLLER_BUTTON_DPAD_UP:
switch (event.gbutton.button) {
case SDL_GAMEPAD_BUTTON_DPAD_UP :
if (m_UiNavMode) {
// Back-tab
sendKey(type, Qt::Key_Tab, Qt::ShiftModifier);
@@ -147,7 +152,7 @@ void SdlGamepadKeyNavigation::onPollingTimerFired()
sendKey(type, Qt::Key_Up);
}
break;
case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
case SDL_GAMEPAD_BUTTON_DPAD_DOWN :
if (m_UiNavMode) {
sendKey(type, Qt::Key_Tab);
}
@@ -155,13 +160,13 @@ void SdlGamepadKeyNavigation::onPollingTimerFired()
sendKey(type, Qt::Key_Down);
}
break;
case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
case SDL_GAMEPAD_BUTTON_DPAD_LEFT :
sendKey(type, Qt::Key_Left);
break;
case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
case SDL_GAMEPAD_BUTTON_DPAD_RIGHT :
sendKey(type, Qt::Key_Right);
break;
case SDL_CONTROLLER_BUTTON_A:
case SDL_GAMEPAD_BUTTON_SOUTH :
if (m_UiNavMode) {
sendKey(type, Qt::Key_Space);
}
@@ -169,14 +174,14 @@ void SdlGamepadKeyNavigation::onPollingTimerFired()
sendKey(type, Qt::Key_Return);
}
break;
case SDL_CONTROLLER_BUTTON_B:
case SDL_GAMEPAD_BUTTON_EAST :
sendKey(type, Qt::Key_Escape);
break;
case SDL_CONTROLLER_BUTTON_X:
case SDL_GAMEPAD_BUTTON_WEST :
sendKey(type, Qt::Key_Menu);
break;
case SDL_CONTROLLER_BUTTON_Y:
case SDL_CONTROLLER_BUTTON_START:
case SDL_GAMEPAD_BUTTON_NORTH :
case SDL_GAMEPAD_BUTTON_START :
// HACK: We use this keycode to inform main.qml
// to show the settings when Key_Menu is handled
// by the control in focus.
@@ -187,11 +192,18 @@ void SdlGamepadKeyNavigation::onPollingTimerFired()
}
break;
}
case SDL_CONTROLLERDEVICEADDED:
SDL_GameController* gc = SDL_GameControllerOpen(event.cdevice.which);
case SDL_EVENT_GAMEPAD_ADDED :
#if SDL_VERSION_ATLEAST(3, 0, 0)
SDL_Gamepad* gc = SDL_OpenGamepad(event.gdevice.which);
if (gc != nullptr) {
SDL_assert(!m_Gamepads.contains(gc));
m_Gamepads.append(gc);
}
#else
SDL_Gamepad* gc = SDL_GameControllerOpen(event.cdevice.which);
if (gc != nullptr) {
// SDL_CONTROLLERDEVICEADDED can be reported multiple times for the same
// gamepad in rare cases, because SDL doesn't fixup the device index in
// gamepad in rare cases, because SDL2 doesn't fixup the device index in
// the SDL_CONTROLLERDEVICEADDED event if an unopened gamepad disappears
// before we've processed the add event.
if (!m_Gamepads.contains(gc)) {
@@ -199,17 +211,18 @@ void SdlGamepadKeyNavigation::onPollingTimerFired()
}
else {
// We already have this game controller open
SDL_GameControllerClose(gc);
SDL_CloseGamepad(gc);
}
}
#endif
break;
}
}
// Handle analog sticks by polling
for (auto gc : m_Gamepads) {
short leftX = SDL_GameControllerGetAxis(gc, SDL_CONTROLLER_AXIS_LEFTX);
short leftY = SDL_GameControllerGetAxis(gc, SDL_CONTROLLER_AXIS_LEFTY);
short leftX = SDL_GetGamepadAxis(gc, SDL_GAMEPAD_AXIS_LEFTX);
short leftY = SDL_GetGamepadAxis(gc, SDL_GAMEPAD_AXIS_LEFTY);
if (SDL_GetTicks() - m_LastAxisNavigationEventTime < AXIS_NAVIGATION_REPEAT_DELAY) {
// Do nothing
}
@@ -261,6 +274,20 @@ void SdlGamepadKeyNavigation::sendKey(QEvent::Type type, Qt::Key key, Qt::Keyboa
}
}
void SdlGamepadKeyNavigation::updateTimerState()
{
if (m_PollingTimer->isActive() && (!m_HasFocus || !m_Enabled)) {
m_PollingTimer->stop();
}
else if (!m_PollingTimer->isActive() && m_HasFocus && m_Enabled) {
// Flush events on the first poll
m_FirstPoll = true;
// Poll every 50 ms for a new joystick event
m_PollingTimer->start(50);
}
}
void SdlGamepadKeyNavigation::setUiNavMode(bool uiNavMode)
{
m_UiNavMode = uiNavMode;
@@ -271,11 +298,6 @@ int SdlGamepadKeyNavigation::getConnectedGamepads()
Q_ASSERT(m_Enabled);
int count = 0;
for (int i = 0; i < SDL_NumJoysticks(); i++) {
if (SDL_IsGameController(i)) {
count++;
}
}
SDL_free(SDL_GetGamepads(&count));
return count;
}
+7 -2
View File
@@ -3,7 +3,7 @@
#include <QTimer>
#include <QEvent>
#include <SDL.h>
#include "SDL_compat.h"
#include "settings/streamingpreferences.h"
@@ -20,6 +20,8 @@ public:
Q_INVOKABLE void disable();
Q_INVOKABLE void notifyWindowFocus(bool hasFocus);
Q_INVOKABLE void setUiNavMode(bool settingsMode);
Q_INVOKABLE int getConnectedGamepads();
@@ -27,15 +29,18 @@ public:
private:
void sendKey(QEvent::Type type, Qt::Key key, Qt::KeyboardModifiers modifiers = Qt::NoModifier);
void updateTimerState();
private slots:
void onPollingTimerFired();
private:
StreamingPreferences* m_Prefs;
QTimer* m_PollingTimer;
QList<SDL_GameController*> m_Gamepads;
QList<SDL_Gamepad*> m_Gamepads;
bool m_Enabled;
bool m_UiNavMode;
bool m_FirstPoll;
bool m_HasFocus;
Uint32 m_LastAxisNavigationEventTime;
};
+46 -26
View File
@@ -17,7 +17,7 @@
// doing the same thing. This needs to be before any headers
// that might include SDL.h themselves.
#define SDL_MAIN_HANDLED
#include <SDL.h>
#include "SDL_compat.h"
#ifdef HAVE_FFMPEG
#include "streaming/video/ffmpeg.h"
@@ -62,22 +62,37 @@
static QElapsedTimer s_LoggerTime;
static QTextStream s_LoggerStream(stderr);
static QMutex s_LoggerLock;
static QThreadPool s_LoggerThread;
static bool s_SuppressVerboseOutput;
static QRegularExpression k_RikeyRegex("&rikey=\\w+");
static QRegularExpression k_RikeyIdRegex("&rikeyid=[\\d-]+");
#ifdef LOG_TO_FILE
// Max log file size of 10 MB
#define MAX_LOG_SIZE_BYTES (10 * 1024 * 1024)
static int s_LogBytesWritten = 0;
static bool s_LogLimitReached = false;
static const uint64_t k_MaxLogSizeBytes = 10 * 1024 * 1024;
static QAtomicInteger<uint64_t> s_LogBytesWritten = 0;
static QFile* s_LoggerFile;
#endif
class LoggerTask : public QRunnable
{
public:
LoggerTask(const QString& msg) : m_Msg(msg)
{
setAutoDelete(true);
}
void run() override
{
s_LoggerStream << m_Msg;
s_LoggerStream.flush();
}
private:
QString m_Msg;
};
void logToLoggerStream(QString& message)
{
QMutexLocker lock(&s_LoggerLock);
#if defined(QT_DEBUG) && defined(Q_OS_WIN32)
// Output log messages to a debugger if attached
if (IsDebuggerPresent()) {
@@ -95,26 +110,25 @@ void logToLoggerStream(QString& message)
message.replace(k_RikeyIdRegex, "&rikeyid=REDACTED");
#ifdef LOG_TO_FILE
if (s_LogLimitReached) {
auto oldLogSize = s_LogBytesWritten.fetchAndAddRelaxed(message.size());
if (oldLogSize >= k_MaxLogSizeBytes) {
return;
}
else if (s_LogBytesWritten >= MAX_LOG_SIZE_BYTES) {
else if (oldLogSize >= k_MaxLogSizeBytes - message.size()) {
s_LoggerThread.waitForDone();
s_LoggerStream << "Log size limit reached!";
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
s_LoggerStream << Qt::endl;
#else
s_LoggerStream << endl;
#endif
s_LogLimitReached = true;
s_LoggerStream.flush();
return;
}
else {
s_LogBytesWritten += message.size();
}
#endif
s_LoggerStream << message;
s_LoggerStream.flush();
// Queue the log message to be written asynchronously
s_LoggerThread.start(new LoggerTask(message));
}
void sdlLogToDiskHandler(void*, int category, SDL_LogPriority priority, const char* message)
@@ -344,6 +358,9 @@ int main(int argc, char *argv[])
}
#endif
// Serialize log messages on a single thread
s_LoggerThread.setMaxThreadCount(1);
s_LoggerTime.start();
qInstallMessageHandler(qtLogToDiskHandler);
SDL_LogSetOutputFunction(sdlLogToDiskHandler, nullptr);
@@ -435,7 +452,7 @@ int main(int argc, char *argv[])
#endif
}
#if !defined(Q_PROCESSOR_X86) && defined(SDL_HINT_VIDEO_X11_FORCE_EGL)
#ifndef Q_PROCESSOR_X86
// Some ARM and RISC-V embedded devices don't have working GLX which can cause
// SDL to fail to find a working OpenGL implementation at all. Let's force EGL
// on non-x86 platforms, since GLX is deprecated anyway.
@@ -491,14 +508,14 @@ int main(int argc, char *argv[])
SDL_SetHint(SDL_HINT_VIDEO_ALLOW_SCREENSAVER, "1");
// We use MMAL to render on Raspberry Pi, so we do not require DRM master.
SDL_SetHint("SDL_KMSDRM_REQUIRE_DRM_MASTER", "0");
SDL_SetHint(SDL_HINT_KMSDRM_REQUIRE_DRM_MASTER, "0");
// Use Direct3D 9Ex to avoid a deadlock caused by the D3D device being reset when
// the user triggers a UAC prompt. This option controls the software/SDL renderer.
// The DXVA2 renderer uses Direct3D 9Ex itself directly.
SDL_SetHint("SDL_WINDOWS_USE_D3D9EX", "1");
SDL_SetHint(SDL_HINT_WINDOWS_USE_D3D9EX, "1");
if (SDL_InitSubSystem(SDL_INIT_TIMER) != 0) {
if (SDLC_FAILURE(SDL_InitSubSystem(SDL_INIT_TIMER))) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_InitSubSystem(SDL_INIT_TIMER) failed: %s",
SDL_GetError());
@@ -508,7 +525,7 @@ int main(int argc, char *argv[])
#ifdef STEAM_LINK
// Steam Link requires that we initialize video before creating our
// QGuiApplication in order to configure the framebuffer correctly.
if (SDL_InitSubSystem(SDL_INIT_VIDEO) != 0) {
if (SDLC_FAILURE(SDL_InitSubSystem(SDL_INIT_VIDEO)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_InitSubSystem(SDL_INIT_VIDEO) failed: %s",
SDL_GetError());
@@ -531,28 +548,28 @@ int main(int argc, char *argv[])
// SDL 2.0.12 changes the default behavior to use the button label rather than the button
// position as most other software does. Set this back to 0 to stay consistent with prior
// releases of Moonlight.
SDL_SetHint("SDL_GAMECONTROLLER_USE_BUTTON_LABELS", "0");
SDL_SetHint(SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS, "0");
// Disable relative mouse scaling to renderer size or logical DPI. We want to send
// the mouse motion exactly how it was given to us.
SDL_SetHint("SDL_MOUSE_RELATIVE_SCALING", "0");
SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_SCALING, "0");
// Set our app name for SDL to use with PulseAudio and PipeWire. This matches what we
// provide as our app name to libsoundio too. On SDL 2.0.18+, SDL_APP_NAME is also used
// for screensaver inhibitor reporting.
SDL_SetHint("SDL_AUDIO_DEVICE_APP_NAME", "Moonlight");
SDL_SetHint("SDL_APP_NAME", "Moonlight");
SDL_SetHint(SDL_HINT_AUDIO_DEVICE_APP_NAME, "Moonlight");
SDL_SetHint(SDL_HINT_APP_NAME, "Moonlight");
// We handle capturing the mouse ourselves when it leaves the window, so we don't need
// SDL doing it for us behind our backs.
SDL_SetHint("SDL_MOUSE_AUTO_CAPTURE", "0");
SDL_SetHint(SDL_HINT_MOUSE_AUTO_CAPTURE, "0");
// SDL will try to lock the mouse cursor on Wayland if it's not visible in order to
// support applications that assume they can warp the cursor (which isn't possible
// on Wayland). We don't want this behavior because it interferes with seamless mouse
// mode when toggling between windowed and fullscreen modes by unexpectedly locking
// the mouse cursor.
SDL_SetHint("SDL_VIDEO_WAYLAND_EMULATE_MOUSE_WARP", "0");
SDL_SetHint(SDL_HINT_VIDEO_WAYLAND_EMULATE_MOUSE_WARP, "0");
#ifdef QT_DEBUG
// Allow thread naming using exceptions on debug builds. SDL doesn't use SEH
@@ -784,6 +801,9 @@ int main(int argc, char *argv[])
// sometimes freezing and blocking process exit.
QThreadPool::globalInstance()->waitForDone(30000);
// Wait for pending log messages to be printed
s_LoggerThread.waitForDone();
#ifdef Q_OS_WIN32
// Without an explicit flush, console redirection for the list command
// doesn't work reliably (sometimes the target file contains no text).
+83 -15
View File
@@ -15,10 +15,12 @@
// redirection that happens when _FILE_OFFSET_BITS=64!
// See masterhook_internal.c for details.
#include <SDL.h>
#include "SDL_compat.h"
#include <stdlib.h>
#include <dlfcn.h>
#include <unistd.h>
#include <errno.h>
#include <stdbool.h>
#include <sys/stat.h>
#include <xf86drm.h>
@@ -36,8 +38,13 @@
int g_QtDrmMasterFd = -1;
struct stat g_DrmMasterStat;
// The DRM master FD created for SDL
int g_SdlDrmMasterFd = -1;
// Last CRTC state for us to restore later
drmModeCrtcPtr g_QtCrtcState;
uint32_t* g_QtCrtcConnectors;
int g_QtCrtcConnectorCount;
bool removeSdlFd(int fd);
int takeMasterFromSdlFd(void);
int drmIsMaster(int fd)
{
@@ -62,7 +69,41 @@ int drmModeSetCrtc(int fd, uint32_t crtcId, uint32_t bufferId,
}
// Call into the real thing
return ((typeof(drmModeSetCrtc)*)dlsym(RTLD_NEXT, __FUNCTION__))(fd, crtcId, bufferId, x, y, connectors, count, mode);
int err = ((typeof(drmModeSetCrtc)*)dlsym(RTLD_NEXT, __FUNCTION__))(fd, crtcId, bufferId, x, y, connectors, count, mode);
if (err == 0 && fd == g_QtDrmMasterFd) {
// Free old CRTC state (if any)
if (g_QtCrtcState) {
drmModeFreeCrtc(g_QtCrtcState);
}
if (g_QtCrtcConnectors) {
free(g_QtCrtcConnectors);
}
// Store the CRTC configuration so we can restore it later
g_QtCrtcState = drmModeGetCrtc(fd, crtcId);
g_QtCrtcConnectors = calloc(count, sizeof(*g_QtCrtcConnectors));
memcpy(g_QtCrtcConnectors, connectors, count * sizeof(*connectors));
g_QtCrtcConnectorCount = count;
}
return err;
}
// This hook will temporarily retake DRM master to allow Qt to render while SDL has a DRM FD open
int drmModePageFlip(int fd, uint32_t crtc_id, uint32_t fb_id, uint32_t flags, void *user_data)
{
// Call into the real thing
int err = ((typeof(drmModePageFlip)*)dlsym(RTLD_NEXT, __FUNCTION__))(fd, crtc_id, fb_id, flags, user_data);
if (err == -EACCES && fd == g_QtDrmMasterFd) {
// If SDL took master from us, try to grab it back temporarily
int oldMasterFd = takeMasterFromSdlFd();
drmSetMaster(fd);
err = ((typeof(drmModePageFlip)*)dlsym(RTLD_NEXT, __FUNCTION__))(fd, crtc_id, fb_id, flags, user_data);
drmDropMaster(fd);
if (oldMasterFd != -1) {
drmSetMaster(oldMasterFd);
}
}
return err;
}
// This hook will handle atomic DRM rendering
@@ -80,7 +121,18 @@ int drmModeAtomicCommit(int fd, drmModeAtomicReqPtr req,
}
// Call into the real thing
return ((typeof(drmModeAtomicCommit)*)dlsym(RTLD_NEXT, __FUNCTION__))(fd, req, flags, user_data);
int err = ((typeof(drmModeAtomicCommit)*)dlsym(RTLD_NEXT, __FUNCTION__))(fd, req, flags, user_data);
if (err == -EACCES && fd == g_QtDrmMasterFd) {
// If SDL took master from us, try to grab it back temporarily
int oldMasterFd = takeMasterFromSdlFd();
drmSetMaster(fd);
err = ((typeof(drmModeAtomicCommit)*)dlsym(RTLD_NEXT, __FUNCTION__))(fd, req, flags, user_data);
drmDropMaster(fd);
if (oldMasterFd != -1) {
drmSetMaster(oldMasterFd);
}
}
return err;
}
// This hook will handle SDL's open() on the DRM device. We just need to
@@ -111,23 +163,39 @@ int open64(const char *pathname, int flags, ...)
// after SDL closes its DRM FD.
int close(int fd)
{
// Remove this entry from the SDL FD table
bool lastSdlFd = removeSdlFd(fd);
// Call the real thing
int ret = ((typeof(close)*)dlsym(RTLD_NEXT, __FUNCTION__))(fd);
if (ret == 0) {
// If we just closed the SDL DRM master FD, restore master
// to the Qt DRM FD. This works because the Qt DRM master FD
// was master once before, so we can set it as master again
// using drmSetMaster() without CAP_SYS_ADMIN.
if (g_SdlDrmMasterFd != -1 && fd == g_SdlDrmMasterFd) {
if (drmSetMaster(g_QtDrmMasterFd) < 0) {
// If we closed the last SDL FD, restore master to the Qt FD
if (ret == 0 && lastSdlFd) {
if (drmSetMaster(g_QtDrmMasterFd) < 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Failed to restore master to Qt DRM FD: %d",
errno);
}
// Reset the CRTC state to how Qt configured it
if (g_QtCrtcState) {
int err = ((typeof(drmModeSetCrtc)*)dlsym(RTLD_NEXT, "drmModeSetCrtc"))(g_QtDrmMasterFd,
g_QtCrtcState->crtc_id,
g_QtCrtcState->buffer_id,
g_QtCrtcState->x,
g_QtCrtcState->y,
g_QtCrtcConnectors,
g_QtCrtcConnectorCount,
g_QtCrtcState->mode_valid ?
&g_QtCrtcState->mode : NULL);
if (err < 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Failed to restore master to Qt DRM FD: %d",
"Failed to restore CRTC state to Qt DRM FD: %d",
errno);
}
g_SdlDrmMasterFd = -1;
}
}
return ret;
}
+120 -18
View File
@@ -4,11 +4,12 @@
#define _GNU_SOURCE
#include <SDL.h>
#include "SDL_compat.h"
#include <dlfcn.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <stdbool.h>
#include <sys/stat.h>
#include <xf86drm.h>
@@ -28,7 +29,73 @@
extern int g_QtDrmMasterFd;
extern struct stat g_DrmMasterStat;
extern int g_SdlDrmMasterFd;
#define MAX_SDL_FD_COUNT 8
int g_SdlDrmMasterFds[MAX_SDL_FD_COUNT];
int g_SdlDrmMasterFdCount = 0;
SDL_SpinLock g_FdTableLock = 0;
// Caller must hold g_FdTableLock
int getSdlFdEntryIndex(bool unused)
{
for (int i = 0; i < MAX_SDL_FD_COUNT; i++) {
// We slightly bend the FD rules here by treating 0
// as invalid since that's our global default value.
if (unused && g_SdlDrmMasterFds[i] <= 0) {
return i;
}
else if (!unused && g_SdlDrmMasterFds[i] > 0) {
return i;
}
}
return -1;
}
// Returns true if the final SDL FD was removed
bool removeSdlFd(int fd)
{
SDL_LockSpinlock(&g_FdTableLock);
if (g_SdlDrmMasterFdCount != 0) {
// Clear the entry for this fd from the table
for (int i = 0; i < MAX_SDL_FD_COUNT; i++) {
if (fd == g_SdlDrmMasterFds[i]) {
g_SdlDrmMasterFds[i] = -1;
g_SdlDrmMasterFdCount--;
break;
}
}
if (g_SdlDrmMasterFdCount == 0) {
SDL_UnlockSpinlock(&g_FdTableLock);
return true;
}
}
SDL_UnlockSpinlock(&g_FdTableLock);
return false;
}
// Returns the previous master FD or -1 if none
int takeMasterFromSdlFd()
{
int fd = -1;
// Since all SDL FDs are actually dups of each other
// we can take master from any one of them.
SDL_LockSpinlock(&g_FdTableLock);
int fdIndex = getSdlFdEntryIndex(false);
if (fdIndex != -1) {
fd = g_SdlDrmMasterFds[fdIndex];
}
SDL_UnlockSpinlock(&g_FdTableLock);
if (fd >= 0 && drmDropMaster(fd) == 0) {
return fd;
}
else {
return -1;
}
}
int openHook(const char *funcname, const char *pathname, int flags, va_list va)
{
@@ -55,32 +122,67 @@ int openHook(const char *funcname, const char *pathname, int flags, va_list va)
fstat(fd, &fdstat);
if (g_DrmMasterStat.st_dev == fdstat.st_dev &&
g_DrmMasterStat.st_ino == fdstat.st_ino) {
int freeFdIndex;
int allocatedFdIndex;
// It is our device. Time to do the magic!
SDL_LockSpinlock(&g_FdTableLock);
// This code assumes SDL only ever opens a single FD
// for a given DRM device.
SDL_assert(g_SdlDrmMasterFd == -1);
// Drop master on Qt's FD so we can pick it up for SDL.
if (drmDropMaster(g_QtDrmMasterFd) < 0) {
// Get a free index for us to put the new entry
freeFdIndex = getSdlFdEntryIndex(true);
if (freeFdIndex < 0) {
SDL_UnlockSpinlock(&g_FdTableLock);
SDL_assert(freeFdIndex >= 0);
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Failed to drop master on Qt DRM FD: %d",
errno);
"No unused SDL FD table entries!");
// Hope for the best
return fd;
}
// We are not allowed to call drmSetMaster() without CAP_SYS_ADMIN,
// but since we just dropped the master, we can become master by
// simply creating a new FD. Let's do it.
close(fd);
if (__OPEN_NEEDS_MODE(flags)) {
fd = ((typeof(open)*)dlsym(RTLD_NEXT, funcname))(pathname, flags, mode);
// Check if we have an allocated entry already
allocatedFdIndex = getSdlFdEntryIndex(false);
if (allocatedFdIndex >= 0) {
// Close fd that we opened earlier (skipping our close() hook)
((typeof(close)*)dlsym(RTLD_NEXT, "close"))(fd);
// dup() an existing FD into the unused slot
fd = dup(g_SdlDrmMasterFds[allocatedFdIndex]);
}
else {
fd = ((typeof(open)*)dlsym(RTLD_NEXT, funcname))(pathname, flags);
// Drop master on Qt's FD so we can pick it up for SDL.
if (drmDropMaster(g_QtDrmMasterFd) < 0) {
SDL_UnlockSpinlock(&g_FdTableLock);
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Failed to drop master on Qt DRM FD: %d",
errno);
// Hope for the best
return fd;
}
// Close fd that we opened earlier (skipping our close() hook)
((typeof(close)*)dlsym(RTLD_NEXT, "close"))(fd);
// We are not allowed to call drmSetMaster() without CAP_SYS_ADMIN,
// but since we just dropped the master, we can become master by
// simply creating a new FD. Let's do it.
if (__OPEN_NEEDS_MODE(flags)) {
fd = ((typeof(open)*)dlsym(RTLD_NEXT, funcname))(pathname, flags, mode);
}
else {
fd = ((typeof(open)*)dlsym(RTLD_NEXT, funcname))(pathname, flags);
}
}
g_SdlDrmMasterFd = fd;
if (fd >= 0) {
// Start with DRM master on the new FD
drmSetMaster(fd);
// Insert the FD into the table
g_SdlDrmMasterFds[freeFdIndex] = fd;
g_SdlDrmMasterFdCount++;
}
SDL_UnlockSpinlock(&g_FdTableLock);
}
}
}
+1 -1
View File
@@ -3,7 +3,7 @@
#include <QDir>
#include <SDL.h>
#include "SDL_compat.h"
#define SER_GAMEPADMAPPING "gcmapping"
+1 -1
View File
@@ -174,7 +174,7 @@ void Session::arDecodeAndPlaySample(char* sampleData, int sampleLength)
// of other threads due to severely restricted CPU time available,
// so we will skip it on that platform.
if (s_ActiveSession->m_AudioSampleCount == 0) {
if (SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH) < 0) {
if (SDLC_FAILURE(SDL_SetCurrentThreadPriority(SDL_THREAD_PRIORITY_HIGH))) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Unable to set audio thread to high priority: %s",
SDL_GetError());
+1 -1
View File
@@ -1,7 +1,7 @@
#pragma once
#include "renderer.h"
#include <SDL.h>
#include "SDL_compat.h"
class SdlAudioRenderer : public IAudioRenderer
{
+3 -4
View File
@@ -1,7 +1,6 @@
#include "sdl.h"
#include <Limelight.h>
#include <SDL.h>
SdlAudioRenderer::SdlAudioRenderer()
: m_AudioDevice(0),
@@ -9,7 +8,7 @@ SdlAudioRenderer::SdlAudioRenderer()
{
SDL_assert(!SDL_WasInit(SDL_INIT_AUDIO));
if (SDL_InitSubSystem(SDL_INIT_AUDIO) != 0) {
if (SDLC_FAILURE(SDL_InitSubSystem(SDL_INIT_AUDIO))) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_InitSubSystem(SDL_INIT_AUDIO) failed: %s",
SDL_GetError());
@@ -23,7 +22,7 @@ bool SdlAudioRenderer::prepareForPlayback(const OPUS_MULTISTREAM_CONFIGURATION*
SDL_zero(want);
want.freq = opusConfig->sampleRate;
want.format = AUDIO_F32SYS;
want.format = SDL_AUDIO_F32;
want.channels = opusConfig->channelCount;
// On PulseAudio systems, setting a value too small can cause underruns for other
@@ -74,7 +73,7 @@ bool SdlAudioRenderer::prepareForPlayback(const OPUS_MULTISTREAM_CONFIGURATION*
SDL_GetCurrentAudioDriver());
// Start playback
SDL_PauseAudioDevice(m_AudioDevice, 0);
SDL_ResumeAudioDevice(m_AudioDevice);
return true;
}
+1 -1
View File
@@ -1,6 +1,6 @@
#include "slaud.h"
#include <SDL.h>
#include "SDL_compat.h"
SLAudioRenderer::SLAudioRenderer()
: m_AudioContext(nullptr),
@@ -1,6 +1,6 @@
#include "soundioaudiorenderer.h"
#include <SDL.h>
#include "SDL_compat.h"
#include <QtGlobal>
+38 -40
View File
@@ -1,10 +1,14 @@
#include "input.h"
#include <Limelight.h>
#include <SDL.h>
#include <SDL_syswm.h>
#include "SDL_compat.h"
#include "streaming/streamutils.h"
#ifdef Q_OS_WIN32
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#endif
#include <QtMath>
// How long the fingers must be stationary to start a right click
@@ -30,33 +34,27 @@ Uint32 SdlInputHandler::longPressTimerCallback(Uint32, void*)
void SdlInputHandler::disableTouchFeedback()
{
SDL_SysWMinfo info;
SDL_VERSION(&info.version);
SDL_GetWindowWMInfo(m_Window, &info);
#ifdef Q_OS_WIN32
if (info.subsystem == SDL_SYSWM_WINDOWS) {
auto fnSetWindowFeedbackSetting = (decltype(SetWindowFeedbackSetting)*)GetProcAddress(GetModuleHandleW(L"user32.dll"), "SetWindowFeedbackSetting");
if (fnSetWindowFeedbackSetting) {
constexpr FEEDBACK_TYPE feedbackTypes[] = {
FEEDBACK_TOUCH_CONTACTVISUALIZATION,
FEEDBACK_PEN_BARRELVISUALIZATION,
FEEDBACK_PEN_TAP,
FEEDBACK_PEN_DOUBLETAP,
FEEDBACK_PEN_PRESSANDHOLD,
FEEDBACK_PEN_RIGHTTAP,
FEEDBACK_TOUCH_TAP,
FEEDBACK_TOUCH_DOUBLETAP,
FEEDBACK_TOUCH_PRESSANDHOLD,
FEEDBACK_TOUCH_RIGHTTAP,
FEEDBACK_GESTURE_PRESSANDTAP,
};
auto fnSetWindowFeedbackSetting = (decltype(SetWindowFeedbackSetting)*)GetProcAddress(GetModuleHandleW(L"user32.dll"), "SetWindowFeedbackSetting");
if (fnSetWindowFeedbackSetting) {
constexpr FEEDBACK_TYPE feedbackTypes[] = {
FEEDBACK_TOUCH_CONTACTVISUALIZATION,
FEEDBACK_PEN_BARRELVISUALIZATION,
FEEDBACK_PEN_TAP,
FEEDBACK_PEN_DOUBLETAP,
FEEDBACK_PEN_PRESSANDHOLD,
FEEDBACK_PEN_RIGHTTAP,
FEEDBACK_TOUCH_TAP,
FEEDBACK_TOUCH_DOUBLETAP,
FEEDBACK_TOUCH_PRESSANDHOLD,
FEEDBACK_TOUCH_RIGHTTAP,
FEEDBACK_GESTURE_PRESSANDTAP,
};
for (FEEDBACK_TYPE ft : feedbackTypes) {
BOOL val = FALSE;
fnSetWindowFeedbackSetting(info.info.win.window, ft, 0, sizeof(val), &val);
}
HWND window = (HWND)SDLC_Win32_GetHwnd(m_Window);
for (FEEDBACK_TYPE ft : feedbackTypes) {
BOOL val = FALSE;
fnSetWindowFeedbackSetting(window, ft, 0, sizeof(val), &val);
}
}
#endif
@@ -84,13 +82,13 @@ void SdlInputHandler::handleAbsoluteFingerEvent(SDL_TouchFingerEvent* event)
uint8_t eventType;
switch (event->type) {
case SDL_FINGERDOWN:
case SDL_EVENT_FINGER_DOWN :
eventType = LI_TOUCH_EVENT_DOWN;
break;
case SDL_FINGERMOTION:
case SDL_EVENT_FINGER_MOTION :
eventType = LI_TOUCH_EVENT_MOVE;
break;
case SDL_FINGERUP:
case SDL_EVENT_FINGER_UP :
eventType = LI_TOUCH_EVENT_UP;
break;
default:
@@ -100,16 +98,16 @@ void SdlInputHandler::handleAbsoluteFingerEvent(SDL_TouchFingerEvent* event)
uint32_t pointerId;
// If the pointer ID is larger than we can fit, just CRC it and use that as the ID.
if ((uint64_t)event->fingerId > UINT32_MAX) {
if ((uint64_t) event->fingerID > UINT32_MAX) {
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
QByteArrayView bav((char*)&event->fingerId, sizeof(event->fingerId));
QByteArrayView bav((char*)&event->fingerID, sizeof(event->fingerID));
pointerId = qChecksum(bav);
#else
pointerId = qChecksum((char*)&event->fingerId, sizeof(event->fingerId));
pointerId = qChecksum((char*)&event->fingerID, sizeof(event->fingerID));
#endif
}
else {
pointerId = (uint32_t)event->fingerId;
pointerId = (uint32_t) event->fingerID;
}
// Try to send it as a native pen/touch event, otherwise fall back to our touch emulation
@@ -119,7 +117,7 @@ void SdlInputHandler::handleAbsoluteFingerEvent(SDL_TouchFingerEvent* event)
int numTouchDevices = SDL_GetNumTouchDevices();
for (int i = 0; i < numTouchDevices; i++) {
if (event->touchId == SDL_GetTouchDevice(i)) {
if (event->touchID == SDL_GetTouchDevice(i)) {
const char* touchName = SDL_GetTouchName(i);
// SDL will report "pen" as the name of pen input devices on Windows.
@@ -160,12 +158,12 @@ void SdlInputHandler::emulateAbsoluteFingerEvent(SDL_TouchFingerEvent* event)
// dx and dy are deltas from the last touch event, not the first touch down.
// Ignore touch down events with more than one finger
if (event->type == SDL_FINGERDOWN && SDL_GetNumTouchFingers(event->touchId) > 1) {
if (event->type == SDL_EVENT_FINGER_DOWN && SDL_GetNumTouchFingers(event->touchID) > 1) {
return;
}
// Ignore touch move and touch up events from the non-primary finger
if (event->type != SDL_FINGERDOWN && event->fingerId != m_LastTouchDownEvent.fingerId) {
if (event->type != SDL_EVENT_FINGER_DOWN && event->fingerID != m_LastTouchDownEvent.fingerID) {
return;
}
@@ -192,7 +190,7 @@ void SdlInputHandler::emulateAbsoluteFingerEvent(SDL_TouchFingerEvent* event)
}
// Don't reposition for finger down events within the deadzone. This makes double-clicking easier.
if (event->type != SDL_FINGERDOWN ||
if (event->type != SDL_EVENT_FINGER_DOWN ||
event->timestamp - m_LastTouchUpEvent.timestamp > DOUBLE_TAP_DEAD_ZONE_DELAY ||
qSqrt(qPow(event->x - m_LastTouchUpEvent.x, 2) + qPow(event->y - m_LastTouchUpEvent.y, 2)) > DOUBLE_TAP_DEAD_ZONE_DELTA) {
// Scale window-relative events to be video-relative and clamp to video region
@@ -203,7 +201,7 @@ void SdlInputHandler::emulateAbsoluteFingerEvent(SDL_TouchFingerEvent* event)
LiSendMousePositionEvent(x - dst.x, y - dst.y, dst.w, dst.h);
}
if (event->type == SDL_FINGERDOWN) {
if (event->type == SDL_EVENT_FINGER_DOWN) {
m_LastTouchDownEvent = *event;
// Start/restart the long press timer
@@ -215,7 +213,7 @@ void SdlInputHandler::emulateAbsoluteFingerEvent(SDL_TouchFingerEvent* event)
// Left button down on finger down
LiSendMouseButtonEvent(BUTTON_ACTION_PRESS, BUTTON_LEFT);
}
else if (event->type == SDL_FINGERUP) {
else if (event->type == SDL_EVENT_FINGER_UP) {
m_LastTouchUpEvent = *event;
// Cancel the long press timer
+132 -127
View File
@@ -1,7 +1,7 @@
#include "streaming/session.h"
#include <Limelight.h>
#include <SDL.h>
#include "SDL_compat.h"
#include "settings/mappingmanager.h"
#include <QtMath>
@@ -188,7 +188,7 @@ Uint32 SdlInputHandler::mouseEmulationTimerCallback(Uint32 interval, void *param
return interval;
}
void SdlInputHandler::handleControllerAxisEvent(SDL_ControllerAxisEvent* event)
void SdlInputHandler::handleControllerAxisEvent(SDL_GamepadAxisEvent * event)
{
SDL_JoystickID gameControllerId = event->which;
GamepadState* state = findStateForGamepad(gameControllerId);
@@ -201,10 +201,10 @@ void SdlInputHandler::handleControllerAxisEvent(SDL_ControllerAxisEvent* event)
for (;;) {
switch (event->axis)
{
case SDL_CONTROLLER_AXIS_LEFTX:
case SDL_GAMEPAD_AXIS_LEFTX :
state->lsX = event->value;
break;
case SDL_CONTROLLER_AXIS_LEFTY:
case SDL_GAMEPAD_AXIS_LEFTY :
// Signed values have one more negative value than
// positive value, so inverting the sign on -32768
// could actually cause the value to overflow and
@@ -212,16 +212,16 @@ void SdlInputHandler::handleControllerAxisEvent(SDL_ControllerAxisEvent* event)
// capping the value at 32767.
state->lsY = -qMax(event->value, (short)-32767);
break;
case SDL_CONTROLLER_AXIS_RIGHTX:
case SDL_GAMEPAD_AXIS_RIGHTX :
state->rsX = event->value;
break;
case SDL_CONTROLLER_AXIS_RIGHTY:
case SDL_GAMEPAD_AXIS_RIGHTY :
state->rsY = -qMax(event->value, (short)-32767);
break;
case SDL_CONTROLLER_AXIS_TRIGGERLEFT:
case SDL_GAMEPAD_AXIS_LEFT_TRIGGER :
state->lt = (unsigned char)(event->value * 255UL / 32767);
break;
case SDL_CONTROLLER_AXIS_TRIGGERRIGHT:
case SDL_GAMEPAD_AXIS_RIGHT_TRIGGER :
state->rt = (unsigned char)(event->value * 255UL / 32767);
break;
default:
@@ -232,18 +232,18 @@ void SdlInputHandler::handleControllerAxisEvent(SDL_ControllerAxisEvent* event)
}
// Check for another event to batch with
if (SDL_PeepEvents(&nextEvent, 1, SDL_PEEKEVENT, SDL_CONTROLLERAXISMOTION, SDL_CONTROLLERAXISMOTION) <= 0) {
if (SDL_PeepEvents(&nextEvent, 1, SDL_PEEKEVENT, SDL_EVENT_GAMEPAD_AXIS_MOTION, SDL_EVENT_GAMEPAD_AXIS_MOTION) <= 0) {
break;
}
event = &nextEvent.caxis;
event = &nextEvent.gaxis;
if (event->which != gameControllerId) {
// Stop batching if a different gamepad interrupts us
break;
}
// Remove the next event to batch
SDL_PeepEvents(&nextEvent, 1, SDL_GETEVENT, SDL_CONTROLLERAXISMOTION, SDL_CONTROLLERAXISMOTION);
SDL_PeepEvents(&nextEvent, 1, SDL_GETEVENT, SDL_EVENT_GAMEPAD_AXIS_MOTION, SDL_EVENT_GAMEPAD_AXIS_MOTION);
}
// Only send the gamepad state to the host if it's not in mouse emulation mode
@@ -252,7 +252,7 @@ void SdlInputHandler::handleControllerAxisEvent(SDL_ControllerAxisEvent* event)
}
}
void SdlInputHandler::handleControllerButtonEvent(SDL_ControllerButtonEvent* event)
void SdlInputHandler::handleControllerButtonEvent(SDL_GamepadButtonEvent * event)
{
if (event->button >= SDL_arraysize(k_ButtonMap)) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
@@ -268,53 +268,53 @@ void SdlInputHandler::handleControllerButtonEvent(SDL_ControllerButtonEvent* eve
if (m_SwapFaceButtons) {
switch (event->button) {
case SDL_CONTROLLER_BUTTON_A:
event->button = SDL_CONTROLLER_BUTTON_B;
case SDL_GAMEPAD_BUTTON_SOUTH :
event->button = SDL_GAMEPAD_BUTTON_EAST;
break;
case SDL_CONTROLLER_BUTTON_B:
event->button = SDL_CONTROLLER_BUTTON_A;
case SDL_GAMEPAD_BUTTON_EAST :
event->button = SDL_GAMEPAD_BUTTON_SOUTH;
break;
case SDL_CONTROLLER_BUTTON_X:
event->button = SDL_CONTROLLER_BUTTON_Y;
case SDL_GAMEPAD_BUTTON_WEST :
event->button = SDL_GAMEPAD_BUTTON_NORTH;
break;
case SDL_CONTROLLER_BUTTON_Y:
event->button = SDL_CONTROLLER_BUTTON_X;
case SDL_GAMEPAD_BUTTON_NORTH :
event->button = SDL_GAMEPAD_BUTTON_WEST;
break;
}
}
if (event->state == SDL_PRESSED) {
if (event->state == true) {
state->buttons |= k_ButtonMap[event->button];
if (event->button == SDL_CONTROLLER_BUTTON_START) {
if (event->button == SDL_GAMEPAD_BUTTON_START) {
state->lastStartDownTime = SDL_GetTicks();
}
else if (state->mouseEmulationTimer != 0) {
if (event->button == SDL_CONTROLLER_BUTTON_A) {
if (event->button == SDL_GAMEPAD_BUTTON_SOUTH) {
LiSendMouseButtonEvent(BUTTON_ACTION_PRESS, BUTTON_LEFT);
}
else if (event->button == SDL_CONTROLLER_BUTTON_B) {
else if (event->button == SDL_GAMEPAD_BUTTON_EAST) {
LiSendMouseButtonEvent(BUTTON_ACTION_PRESS, BUTTON_RIGHT);
}
else if (event->button == SDL_CONTROLLER_BUTTON_X) {
else if (event->button == SDL_GAMEPAD_BUTTON_WEST) {
LiSendMouseButtonEvent(BUTTON_ACTION_PRESS, BUTTON_MIDDLE);
}
else if (event->button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER) {
else if (event->button == SDL_GAMEPAD_BUTTON_LEFT_SHOULDER) {
LiSendMouseButtonEvent(BUTTON_ACTION_PRESS, BUTTON_X1);
}
else if (event->button == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER) {
else if (event->button == SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER) {
LiSendMouseButtonEvent(BUTTON_ACTION_PRESS, BUTTON_X2);
}
else if (event->button == SDL_CONTROLLER_BUTTON_DPAD_UP) {
else if (event->button == SDL_GAMEPAD_BUTTON_DPAD_UP) {
LiSendScrollEvent(1);
}
else if (event->button == SDL_CONTROLLER_BUTTON_DPAD_DOWN) {
else if (event->button == SDL_GAMEPAD_BUTTON_DPAD_DOWN) {
LiSendScrollEvent(-1);
}
else if (event->button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT) {
else if (event->button == SDL_GAMEPAD_BUTTON_DPAD_RIGHT) {
LiSendHScrollEvent(1);
}
else if (event->button == SDL_CONTROLLER_BUTTON_DPAD_LEFT) {
else if (event->button == SDL_GAMEPAD_BUTTON_DPAD_LEFT) {
LiSendHScrollEvent(-1);
}
}
@@ -322,7 +322,7 @@ void SdlInputHandler::handleControllerButtonEvent(SDL_ControllerButtonEvent* eve
else {
state->buttons &= ~k_ButtonMap[event->button];
if (event->button == SDL_CONTROLLER_BUTTON_START) {
if (event->button == SDL_GAMEPAD_BUTTON_START) {
if (SDL_GetTicks() - state->lastStartDownTime > MOUSE_EMULATION_LONG_PRESS_TIME) {
if (state->mouseEmulationTimer != 0) {
SDL_RemoveTimer(state->mouseEmulationTimer);
@@ -345,19 +345,19 @@ void SdlInputHandler::handleControllerButtonEvent(SDL_ControllerButtonEvent* eve
}
}
else if (state->mouseEmulationTimer != 0) {
if (event->button == SDL_CONTROLLER_BUTTON_A) {
if (event->button == SDL_GAMEPAD_BUTTON_SOUTH) {
LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, BUTTON_LEFT);
}
else if (event->button == SDL_CONTROLLER_BUTTON_B) {
else if (event->button == SDL_GAMEPAD_BUTTON_EAST) {
LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, BUTTON_RIGHT);
}
else if (event->button == SDL_CONTROLLER_BUTTON_X) {
else if (event->button == SDL_GAMEPAD_BUTTON_WEST) {
LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, BUTTON_MIDDLE);
}
else if (event->button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER) {
else if (event->button == SDL_GAMEPAD_BUTTON_LEFT_SHOULDER) {
LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, BUTTON_X1);
}
else if (event->button == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER) {
else if (event->button == SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER) {
LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, BUTTON_X2);
}
}
@@ -370,7 +370,7 @@ void SdlInputHandler::handleControllerButtonEvent(SDL_ControllerButtonEvent* eve
// Push a quit event to the main loop
SDL_Event event;
event.type = SDL_QUIT;
event.type = SDL_EVENT_QUIT;
event.quit.timestamp = SDL_GetTicks();
SDL_PushEvent(&event);
@@ -403,7 +403,7 @@ void SdlInputHandler::handleControllerButtonEvent(SDL_ControllerButtonEvent* eve
#if SDL_VERSION_ATLEAST(2, 0, 14)
void SdlInputHandler::handleControllerSensorEvent(SDL_ControllerSensorEvent* event)
void SdlInputHandler::handleControllerSensorEvent(SDL_GamepadSensorEvent * event)
{
GamepadState* state = findStateForGamepad(event->which);
if (state == NULL) {
@@ -438,7 +438,7 @@ void SdlInputHandler::handleControllerSensorEvent(SDL_ControllerSensorEvent* eve
}
}
void SdlInputHandler::handleControllerTouchpadEvent(SDL_ControllerTouchpadEvent* event)
void SdlInputHandler::handleControllerTouchpadEvent(SDL_GamepadTouchpadEvent * event)
{
GamepadState* state = findStateForGamepad(event->which);
if (state == NULL) {
@@ -447,13 +447,13 @@ void SdlInputHandler::handleControllerTouchpadEvent(SDL_ControllerTouchpadEvent*
uint8_t eventType;
switch (event->type) {
case SDL_CONTROLLERTOUCHPADDOWN:
case SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN :
eventType = LI_TOUCH_EVENT_DOWN;
break;
case SDL_CONTROLLERTOUCHPADUP:
case SDL_EVENT_GAMEPAD_TOUCHPAD_UP :
eventType = LI_TOUCH_EVENT_UP;
break;
case SDL_CONTROLLERTOUCHPADMOTION:
case SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION :
eventType = LI_TOUCH_EVENT_MOVE;
break;
default:
@@ -479,18 +479,21 @@ void SdlInputHandler::handleJoystickBatteryEvent(SDL_JoyBatteryEvent* event)
#endif
void SdlInputHandler::handleControllerDeviceEvent(SDL_ControllerDeviceEvent* event)
void SdlInputHandler::handleControllerDeviceEvent(SDL_GamepadDeviceEvent * event)
{
GamepadState* state;
if (event->type == SDL_CONTROLLERDEVICEADDED) {
if (event->type == SDL_EVENT_GAMEPAD_ADDED) {
int i;
const char* name;
SDL_GameController* controller;
SDL_Gamepad * controller;
const char* mapping;
char guidStr[33];
uint32_t hapticCaps;
#if SDL_VERSION_ATLEAST(3, 0, 0)
controller = SDL_OpenGamepad(event->which);
#else
controller = SDL_GameControllerOpen(event->which);
if (controller == NULL) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
@@ -500,7 +503,7 @@ void SdlInputHandler::handleControllerDeviceEvent(SDL_ControllerDeviceEvent* eve
}
// SDL_CONTROLLERDEVICEADDED can be reported multiple times for the same
// gamepad in rare cases, because SDL doesn't fixup the device index in
// gamepad in rare cases, because SDL2 doesn't fixup the device index in
// the SDL_CONTROLLERDEVICEADDED event if an unopened gamepad disappears
// before we've processed the add event.
for (int i = 0; i < MAX_GAMEPADS; i++) {
@@ -508,10 +511,11 @@ void SdlInputHandler::handleControllerDeviceEvent(SDL_ControllerDeviceEvent* eve
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Received duplicate add event for controller index: %d",
event->which);
SDL_GameControllerClose(controller);
SDL_CloseGamepad(controller);
return;
}
}
#endif
// We used to use SDL_GameControllerGetPlayerIndex() here but that
// can lead to strange issues due to bugs in Windows where an Xbox
@@ -531,18 +535,18 @@ void SdlInputHandler::handleControllerDeviceEvent(SDL_ControllerDeviceEvent* eve
if (i == MAX_GAMEPADS) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"No open gamepad slots found!");
SDL_GameControllerClose(controller);
SDL_CloseGamepad(controller);
return;
}
SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(SDL_GameControllerGetJoystick(controller)),
SDL_JoystickGetGUIDString(SDL_GetJoystickGUID(SDL_GetGamepadJoystick(controller)),
guidStr, sizeof(guidStr));
if (m_IgnoreDeviceGuids.contains(guidStr, Qt::CaseInsensitive))
{
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Skipping ignored device with GUID: %s",
guidStr);
SDL_GameControllerClose(controller);
SDL_CloseGamepad(controller);
return;
}
@@ -554,7 +558,7 @@ void SdlInputHandler::handleControllerDeviceEvent(SDL_ControllerDeviceEvent* eve
// This will change indicators on the controller to show the assigned
// player index. For Xbox 360 controllers, that means updating the LED
// ring to light up the corresponding quadrant for this player.
SDL_GameControllerSetPlayerIndex(controller, state->index);
SDL_SetGamepadPlayerIndex(controller, state->index);
#endif
}
else {
@@ -563,7 +567,7 @@ void SdlInputHandler::handleControllerDeviceEvent(SDL_ControllerDeviceEvent* eve
}
state->controller = controller;
state->jsId = SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(state->controller));
state->jsId = SDL_GetJoystickID(SDL_GetGamepadJoystick(state->controller));
hapticCaps = 0;
#if SDL_VERSION_ATLEAST(2, 0, 18)
@@ -573,9 +577,9 @@ void SdlInputHandler::handleControllerDeviceEvent(SDL_ControllerDeviceEvent* eve
// Perform a tiny rumbles to see if haptics are supported.
// NB: We cannot use zeros for rumble intensity or SDL will not actually call the JS driver
// and we'll get a (potentially false) success value returned.
hapticCaps |= SDL_GameControllerRumble(controller, 1, 1, 1) == 0 ? ML_HAPTIC_GC_RUMBLE : 0;
hapticCaps |= SDL_RumbleGamepad(controller, 1, 1, 1) ? ML_HAPTIC_GC_RUMBLE : 0;
#if SDL_VERSION_ATLEAST(2, 0, 14)
hapticCaps |= SDL_GameControllerRumbleTriggers(controller, 1, 1, 1) == 0 ? ML_HAPTIC_GC_TRIGGER_RUMBLE : 0;
hapticCaps |= SDL_RumbleGamepadTriggers(controller, 1, 1, 1) ? ML_HAPTIC_GC_TRIGGER_RUMBLE : 0;
#endif
#else
state->haptic = SDL_HapticOpenFromJoystick(SDL_GameControllerGetJoystick(state->controller));
@@ -606,11 +610,11 @@ void SdlInputHandler::handleControllerDeviceEvent(SDL_ControllerDeviceEvent* eve
}
#endif
mapping = SDL_GameControllerMapping(state->controller);
name = SDL_GameControllerName(state->controller);
mapping = SDL_GetGamepadMapping(state->controller);
name = SDL_GetGamepadName(state->controller);
uint16_t vendorId = SDL_GameControllerGetVendor(state->controller);
uint16_t productId = SDL_GameControllerGetProduct(state->controller);
uint16_t vendorId = SDL_GetGamepadVendor(state->controller);
uint16_t productId = SDL_GetGamepadProduct(state->controller);
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Gamepad %d (player %d) is: %s (VID/PID: 0x%.4x/0x%.4x) (haptic capabilities: 0x%x) (mapping: %s -> %s)",
i,
@@ -636,21 +640,21 @@ void SdlInputHandler::handleControllerDeviceEvent(SDL_ControllerDeviceEvent* eve
SDL_assert(m_GamepadMask == 0x1);
}
SDL_JoystickPowerLevel powerLevel = SDL_JoystickCurrentPowerLevel(SDL_GameControllerGetJoystick(state->controller));
SDL_JoystickPowerLevel powerLevel = SDL_GetJoystickPowerLevel(SDL_GetGamepadJoystick(state->controller));
#if SDL_VERSION_ATLEAST(2, 0, 14)
// On SDL 2.0.14 and later, we can provide enhanced controller information to the host PC
// for it to use as a hint for the type of controller to emulate.
uint32_t supportedButtonFlags = 0;
for (int i = 0; i < (int)SDL_arraysize(k_ButtonMap); i++) {
if (SDL_GameControllerHasButton(state->controller, (SDL_GameControllerButton)i)) {
if (SDL_GamepadHasButton(state->controller, (SDL_GamepadButton)i)) {
supportedButtonFlags |= k_ButtonMap[i];
}
}
uint32_t capabilities = 0;
if (SDL_GameControllerGetBindForAxis(state->controller, SDL_CONTROLLER_AXIS_TRIGGERLEFT).bindType == SDL_CONTROLLER_BINDTYPE_AXIS ||
SDL_GameControllerGetBindForAxis(state->controller, SDL_CONTROLLER_AXIS_TRIGGERRIGHT).bindType == SDL_CONTROLLER_BINDTYPE_AXIS) {
if (SDL_GameControllerGetBindForAxis(state->controller, SDL_GAMEPAD_AXIS_LEFT_TRIGGER).bindType == SDL_GAMEPAD_BINDTYPE_AXIS ||
SDL_GameControllerGetBindForAxis(state->controller, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER).bindType == SDL_GAMEPAD_BINDTYPE_AXIS) {
// We assume these are analog triggers if the binding is to an axis rather than a button
capabilities |= LI_CCAP_ANALOG_TRIGGERS;
}
@@ -660,13 +664,13 @@ void SdlInputHandler::handleControllerDeviceEvent(SDL_ControllerDeviceEvent* eve
if (hapticCaps & ML_HAPTIC_GC_TRIGGER_RUMBLE) {
capabilities |= LI_CCAP_TRIGGER_RUMBLE;
}
if (SDL_GameControllerGetNumTouchpads(state->controller) > 0) {
if (SDL_GetNumGamepadTouchpads(state->controller) > 0) {
capabilities |= LI_CCAP_TOUCHPAD;
}
if (SDL_GameControllerHasSensor(state->controller, SDL_SENSOR_ACCEL)) {
if (SDL_GamepadHasSensor(state->controller, SDL_SENSOR_ACCEL)) {
capabilities |= LI_CCAP_ACCEL;
}
if (SDL_GameControllerHasSensor(state->controller, SDL_SENSOR_GYRO)) {
if (SDL_GamepadHasSensor(state->controller, SDL_SENSOR_GYRO)) {
capabilities |= LI_CCAP_GYRO;
}
if (powerLevel != SDL_JOYSTICK_POWER_UNKNOWN || SDL_VERSION_ATLEAST(2, 24, 0)) {
@@ -677,21 +681,21 @@ void SdlInputHandler::handleControllerDeviceEvent(SDL_ControllerDeviceEvent* eve
}
uint8_t type;
switch (SDL_GameControllerGetType(state->controller)) {
case SDL_CONTROLLER_TYPE_XBOX360:
case SDL_CONTROLLER_TYPE_XBOXONE:
switch (SDL_GetGamepadType(state->controller)) {
case SDL_GAMEPAD_TYPE_XBOX360 :
case SDL_GAMEPAD_TYPE_XBOXONE :
type = LI_CTYPE_XBOX;
break;
case SDL_CONTROLLER_TYPE_PS3:
case SDL_CONTROLLER_TYPE_PS4:
case SDL_CONTROLLER_TYPE_PS5:
case SDL_GAMEPAD_TYPE_PS3 :
case SDL_GAMEPAD_TYPE_PS4 :
case SDL_GAMEPAD_TYPE_PS5 :
type = LI_CTYPE_PS;
break;
case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO:
case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO :
#if SDL_VERSION_ATLEAST(2, 24, 0)
case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_LEFT:
case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT:
case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_PAIR:
case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT :
case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT :
case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_PAIR :
#endif
type = LI_CTYPE_NINTENDO;
break;
@@ -704,7 +708,7 @@ void SdlInputHandler::handleControllerDeviceEvent(SDL_ControllerDeviceEvent* eve
// we'll allow the Select+PS button combo to act as the touchpad.
state->clickpadButtonEmulationEnabled =
#if SDL_VERSION_ATLEAST(2, 0, 14)
SDL_GameControllerGetBindForButton(state->controller, SDL_CONTROLLER_BUTTON_TOUCHPAD).bindType == SDL_CONTROLLER_BINDTYPE_NONE &&
SDL_GameControllerGetBindForButton(state->controller, SDL_GAMEPAD_BUTTON_TOUCHPAD).bindType == SDL_GAMEPAD_BINDTYPE_NONE &&
#endif
type == LI_CTYPE_PS;
@@ -720,7 +724,7 @@ void SdlInputHandler::handleControllerDeviceEvent(SDL_ControllerDeviceEvent* eve
sendGamepadBatteryState(state, powerLevel);
}
}
else if (event->type == SDL_CONTROLLERDEVICEREMOVED) {
else if (event->type == SDL_EVENT_GAMEPAD_REMOVED) {
state = findStateForGamepad(event->which);
if (state != NULL) {
if (state->mouseEmulationTimer != 0) {
@@ -728,7 +732,7 @@ void SdlInputHandler::handleControllerDeviceEvent(SDL_ControllerDeviceEvent* eve
SDL_RemoveTimer(state->mouseEmulationTimer);
}
SDL_GameControllerClose(state->controller);
SDL_CloseGamepad(state->controller);
#if !SDL_VERSION_ATLEAST(2, 0, 9)
if (state->haptic != nullptr) {
@@ -761,24 +765,23 @@ void SdlInputHandler::handleControllerDeviceEvent(SDL_ControllerDeviceEvent* eve
void SdlInputHandler::handleJoystickArrivalEvent(SDL_JoyDeviceEvent* event)
{
SDL_assert(event->type == SDL_JOYDEVICEADDED);
SDL_assert(event->type == SDL_EVENT_JOYSTICK_ADDED);
if (!SDL_IsGameController(event->which)) {
char guidStr[33];
SDL_JoystickGetGUIDString(SDL_JoystickGetDeviceGUID(event->which),
guidStr, sizeof(guidStr));
const char* name = SDL_JoystickNameForIndex(event->which);
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Joystick discovered with no mapping: %s %s",
name ? name : "<UNKNOWN>",
guidStr);
SDL_Joystick* joy = SDL_JoystickOpen(event->which);
if (!SDL_IsGamepad(event->which)) {
SDL_Joystick* joy = SDL_OpenJoystick(event->which);
if (joy != nullptr) {
char guidStr[33];
SDL_GUIDToString(SDL_JoystickGetGUID(joy), guidStr, sizeof(guidStr));
const char* name = SDL_JoystickName(joy);
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Unmapped joystick: %s %s",
name ? name : "<UNKNOWN>",
guidStr);
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Number of axes: %d | Number of buttons: %d | Number of hats: %d",
SDL_JoystickNumAxes(joy), SDL_JoystickNumButtons(joy),
SDL_JoystickNumHats(joy));
SDL_JoystickClose(joy);
SDL_GetNumJoystickAxes(joy), SDL_GetNumJoystickButtons(joy),
SDL_GetNumJoystickHats(joy));
SDL_CloseJoystick(joy);
}
else {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
@@ -797,7 +800,7 @@ void SdlInputHandler::rumble(unsigned short controllerNumber, unsigned short low
#if SDL_VERSION_ATLEAST(2, 0, 9)
if (m_GamepadState[controllerNumber].controller != nullptr) {
SDL_GameControllerRumble(m_GamepadState[controllerNumber].controller, lowFreqMotor, highFreqMotor, 30000);
SDL_RumbleGamepad(m_GamepadState[controllerNumber].controller, lowFreqMotor, highFreqMotor, 30000);
}
#else
// Check if the controller supports haptics (and if the controller exists at all)
@@ -855,7 +858,7 @@ void SdlInputHandler::rumbleTriggers(uint16_t controllerNumber, uint16_t leftTri
#if SDL_VERSION_ATLEAST(2, 0, 14)
if (m_GamepadState[controllerNumber].controller != nullptr) {
SDL_GameControllerRumbleTriggers(m_GamepadState[controllerNumber].controller, leftTrigger, rightTrigger, 30000);
SDL_RumbleGamepadTriggers(m_GamepadState[controllerNumber].controller, leftTrigger, rightTrigger, 30000);
}
#endif
}
@@ -874,12 +877,12 @@ void SdlInputHandler::setMotionEventState(uint16_t controllerNumber, uint8_t mot
switch (motionType) {
case LI_MOTION_TYPE_ACCEL:
m_GamepadState[controllerNumber].accelReportPeriodMs = reportPeriodMs;
SDL_GameControllerSetSensorEnabled(m_GamepadState[controllerNumber].controller, SDL_SENSOR_ACCEL, reportRateHz ? SDL_TRUE : SDL_FALSE);
SDL_SetGamepadSensorEnabled(m_GamepadState[controllerNumber].controller, SDL_SENSOR_ACCEL, reportRateHz ? SDL_TRUE : SDL_FALSE);
break;
case LI_MOTION_TYPE_GYRO:
m_GamepadState[controllerNumber].gyroReportPeriodMs = reportPeriodMs;
SDL_GameControllerSetSensorEnabled(m_GamepadState[controllerNumber].controller, SDL_SENSOR_GYRO, reportRateHz ? SDL_TRUE : SDL_FALSE);
SDL_SetGamepadSensorEnabled(m_GamepadState[controllerNumber].controller, SDL_SENSOR_GYRO, reportRateHz ? SDL_TRUE : SDL_FALSE);
break;
}
}
@@ -895,7 +898,7 @@ void SdlInputHandler::setControllerLED(uint16_t controllerNumber, uint8_t r, uin
#if SDL_VERSION_ATLEAST(2, 0, 14)
if (m_GamepadState[controllerNumber].controller != nullptr) {
SDL_GameControllerSetLED(m_GamepadState[controllerNumber].controller, r, g, b);
SDL_SetGamepadLED(m_GamepadState[controllerNumber].controller, r, g, b);
}
#endif
}
@@ -904,30 +907,32 @@ QString SdlInputHandler::getUnmappedGamepads()
{
QString ret;
if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) != 0) {
if (SDLC_FAILURE(SDL_InitSubSystem(SDL_INIT_GAMEPAD))) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) failed: %s",
"SDL_InitSubSystem(SDL_INIT_GAMEPAD) failed: %s",
SDL_GetError());
}
MappingManager mappingManager;
mappingManager.applyMappings();
for (int i = 0; i < SDL_NumJoysticks(); i++) {
if (!SDL_IsGameController(i)) {
char guidStr[33];
SDL_JoystickGetGUIDString(SDL_JoystickGetDeviceGUID(i),
guidStr, sizeof(guidStr));
const char* name = SDL_JoystickNameForIndex(i);
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Unmapped joystick: %s %s",
name ? name : "<UNKNOWN>",
guidStr);
SDL_Joystick* joy = SDL_JoystickOpen(i);
int numJoysticks = 0;
SDL_JoystickID* joysticks = SDL_GetJoysticks(&numJoysticks);
for (int i = 0; i < numJoysticks; i++) {
if (!SDL_IsGamepad(joysticks[i])) {
SDL_Joystick* joy = SDL_OpenJoystick(joysticks[i]);
if (joy != nullptr) {
int numButtons = SDL_JoystickNumButtons(joy);
int numHats = SDL_JoystickNumHats(joy);
int numAxes = SDL_JoystickNumAxes(joy);
char guidStr[33];
SDL_GUIDToString(SDL_JoystickGetGUID(joy), guidStr, sizeof(guidStr));
const char* name = SDL_JoystickName(joy);
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Unmapped joystick: %s %s",
name ? name : "<UNKNOWN>",
guidStr);
int numButtons = SDL_GetNumJoystickButtons(joy);
int numHats = SDL_GetNumJoystickHats(joy);
int numAxes = SDL_GetNumJoystickAxes(joy);
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Number of axes: %d | Number of buttons: %d | Number of hats: %d",
@@ -943,7 +948,7 @@ QString SdlInputHandler::getUnmappedGamepads()
ret += name;
}
SDL_JoystickClose(joy);
SDL_CloseJoystick(joy);
}
else {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
@@ -952,12 +957,13 @@ QString SdlInputHandler::getUnmappedGamepads()
}
}
}
SDL_free(joysticks);
SDL_QuitSubSystem(SDL_INIT_GAMECONTROLLER);
SDL_QuitSubSystem(SDL_INIT_GAMEPAD);
// Flush stale events so they aren't processed by the main session event loop
SDL_FlushEvents(SDL_JOYDEVICEADDED, SDL_JOYDEVICEREMOVED);
SDL_FlushEvents(SDL_CONTROLLERDEVICEADDED, SDL_CONTROLLERDEVICEREMAPPED);
SDL_FlushEvents(SDL_EVENT_JOYSTICK_ADDED, SDL_EVENT_JOYSTICK_REMOVED);
SDL_FlushEvents(SDL_EVENT_GAMEPAD_ADDED, SDL_EVENT_GAMEPAD_REMAPPED);
return ret;
}
@@ -973,18 +979,17 @@ int SdlInputHandler::getAttachedGamepadMask()
}
count = mask = 0;
for (int i = 0; i < SDL_NumJoysticks(); i++) {
if (SDL_IsGameController(i)) {
char guidStr[33];
SDL_JoystickGetGUIDString(SDL_JoystickGetDeviceGUID(i),
guidStr, sizeof(guidStr));
int numGamepads = 0;
SDL_JoystickID *gamepads = SDL_GetGamepads(&numGamepads);
for (int i = 0; i < numGamepads; i++) {
char guidStr[33];
SDL_GUIDToString(SDL_GetJoystickGUIDForID(i), guidStr, sizeof(guidStr));
if (!m_IgnoreDeviceGuids.contains(guidStr, Qt::CaseInsensitive))
{
mask |= (1 << count++);
}
if (!m_IgnoreDeviceGuids.contains(guidStr, Qt::CaseInsensitive)) {
mask |= (1 << count++);
}
}
SDL_free(gamepads);
return mask;
}
+17 -18
View File
@@ -1,5 +1,5 @@
#include <Limelight.h>
#include <SDL.h>
#include "SDL_compat.h"
#include "streaming/session.h"
#include "settings/mappingmanager.h"
#include "path.h"
@@ -50,7 +50,7 @@ SdlInputHandler::SdlInputHandler(StreamingPreferences& prefs, int streamWidth, i
#endif
// Opt-out of SDL's built-in Alt+Tab handling while keyboard grab is enabled
SDL_SetHint("SDL_ALLOW_ALT_TAB_WHILE_GRABBED", "0");
SDL_SetHint(SDL_HINT_ALLOW_ALT_TAB_WHILE_GRABBED, "0");
// Allow clicks to pass through to us when focusing the window. If we're in
// absolute mouse mode, this will avoid the user having to click twice to
@@ -62,8 +62,8 @@ SdlInputHandler::SdlInputHandler(StreamingPreferences& prefs, int streamWidth, i
// controllers, but breaks DirectInput applications. We will enable it because
// it's likely that working rumble is what the user is expecting. If they don't
// want this behavior, they can override it with the environment variable.
SDL_SetHint("SDL_JOYSTICK_HIDAPI_PS4_RUMBLE", "1");
SDL_SetHint("SDL_JOYSTICK_HIDAPI_PS5_RUMBLE", "1");
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, "1");
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1");
// Populate special key combo configuration
m_SpecialKeyCombos[KeyComboQuit].keyCombo = KeyComboQuit;
@@ -148,7 +148,7 @@ SdlInputHandler::SdlInputHandler(StreamingPreferences& prefs, int streamWidth, i
// can allow mapping manager to update the mappings before GC attach
// events are generated.
SDL_assert(!SDL_WasInit(SDL_INIT_JOYSTICK));
if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) != 0) {
if (SDLC_FAILURE(SDL_InitSubSystem(SDL_INIT_JOYSTICK))) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_InitSubSystem(SDL_INIT_JOYSTICK) failed: %s",
SDL_GetError());
@@ -165,16 +165,16 @@ SdlInputHandler::SdlInputHandler(StreamingPreferences& prefs, int streamWidth, i
// We need to reinit this each time, since you only get
// an initial set of gamepad arrival events once per init.
SDL_assert(!SDL_WasInit(SDL_INIT_GAMECONTROLLER));
if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) != 0) {
SDL_assert(!SDL_WasInit(SDL_INIT_GAMEPAD));
if (SDLC_FAILURE(SDL_InitSubSystem(SDL_INIT_GAMEPAD))) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) failed: %s",
"SDL_InitSubSystem(SDL_INIT_GAMEPAD) failed: %s",
SDL_GetError());
}
#if !SDL_VERSION_ATLEAST(2, 0, 9)
SDL_assert(!SDL_WasInit(SDL_INIT_HAPTIC));
if (SDL_InitSubSystem(SDL_INIT_HAPTIC) != 0) {
if (SDLC_FAILURE(SDL_InitSubSystem(SDL_INIT_HAPTIC))) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_InitSubSystem(SDL_INIT_HAPTIC) failed: %s",
SDL_GetError());
@@ -219,8 +219,8 @@ SdlInputHandler::~SdlInputHandler()
SDL_assert(!SDL_WasInit(SDL_INIT_HAPTIC));
#endif
SDL_QuitSubSystem(SDL_INIT_GAMECONTROLLER);
SDL_assert(!SDL_WasInit(SDL_INIT_GAMECONTROLLER));
SDL_QuitSubSystem(SDL_INIT_GAMEPAD);
SDL_assert(!SDL_WasInit(SDL_INIT_GAMEPAD));
SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
SDL_assert(!SDL_WasInit(SDL_INIT_JOYSTICK));
@@ -275,7 +275,7 @@ void SdlInputHandler::notifyMouseLeave()
// NB: Not using SDL_GetGlobalMouseState() because we want our state not the system's
Uint32 mouseState = SDL_GetMouseState(nullptr, nullptr);
for (Uint32 button = SDL_BUTTON_LEFT; button <= SDL_BUTTON_X2; button++) {
if (mouseState & SDL_BUTTON(button)) {
if (mouseState & SDL_BUTTON_MASK(button)) {
SDL_CaptureMouse(SDL_TRUE);
break;
}
@@ -289,7 +289,7 @@ void SdlInputHandler::notifyFocusLost()
// This lets user to interact with our window's title bar and with the buttons in it.
// Doing this while the window is full-screen breaks the transition out of FS
// (desktop and exclusive), so we must check for that before releasing mouse capture.
if (!(SDL_GetWindowFlags(m_Window) & SDL_WINDOW_FULLSCREEN) && !m_AbsoluteMouseMode) {
if (!SDLC_IsFullscreen(m_Window) && !m_AbsoluteMouseMode) {
setCaptureActive(false);
}
@@ -315,9 +315,8 @@ void SdlInputHandler::updateKeyboardGrabState()
}
bool shouldGrab = isCaptureActive();
Uint32 windowFlags = SDL_GetWindowFlags(m_Window);
if (m_CaptureSystemKeysMode == StreamingPreferences::CSK_FULLSCREEN &&
!(windowFlags & SDL_WINDOW_FULLSCREEN)) {
!SDLC_IsFullscreen(m_Window)) {
// Ungrab if it's fullscreen only and we left fullscreen
shouldGrab = false;
}
@@ -355,7 +354,7 @@ bool SdlInputHandler::isSystemKeyCaptureActive()
}
if (m_CaptureSystemKeysMode == StreamingPreferences::CSK_FULLSCREEN &&
!(windowFlags & SDL_WINDOW_FULLSCREEN)) {
!SDLC_IsFullscreen(m_Window)) {
return false;
}
@@ -389,7 +388,7 @@ void SdlInputHandler::setCaptureActive(bool active)
if (isMouseInVideoRegion(mouseX, mouseY)) {
// Synthesize a mouse event to synchronize the cursor
SDL_MouseMotionEvent motionEvent = {};
motionEvent.type = SDL_MOUSEMOTION;
motionEvent.type = SDL_EVENT_MOUSE_MOTION;
motionEvent.timestamp = SDL_GetTicks();
motionEvent.windowID = SDL_GetWindowID(m_Window);
motionEvent.x = mouseX;
@@ -419,7 +418,7 @@ void SdlInputHandler::setCaptureActive(bool active)
void SdlInputHandler::handleTouchFingerEvent(SDL_TouchFingerEvent* event)
{
#if SDL_VERSION_ATLEAST(2, 0, 10)
if (SDL_GetTouchDeviceType(event->touchId) != SDL_TOUCH_DEVICE_DIRECT) {
if (SDL_GetTouchDeviceType(event->touchID) != SDL_TOUCH_DEVICE_DIRECT) {
// Ignore anything that isn't a touchscreen. We may get callbacks
// for trackpads, but we want to handle those in the mouse path.
return;
+9 -9
View File
@@ -3,10 +3,10 @@
#include "settings/streamingpreferences.h"
#include "backend/computermanager.h"
#include <SDL.h>
#include "SDL_compat.h"
struct GamepadState {
SDL_GameController* controller;
SDL_Gamepad * controller;
SDL_JoystickID jsId;
short index;
@@ -24,11 +24,11 @@ struct GamepadState {
#if SDL_VERSION_ATLEAST(2, 0, 14)
uint8_t gyroReportPeriodMs;
float lastGyroEventData[SDL_arraysize(SDL_ControllerSensorEvent::data)];
float lastGyroEventData[SDL_arraysize(SDL_GamepadSensorEvent::data)];
uint32_t lastGyroEventTime;
uint8_t accelReportPeriodMs;
float lastAccelEventData[SDL_arraysize(SDL_ControllerSensorEvent::data)];
float lastAccelEventData[SDL_arraysize(SDL_GamepadSensorEvent::data)];
uint32_t lastAccelEventTime;
#endif
@@ -67,16 +67,16 @@ public:
void handleMouseWheelEvent(SDL_MouseWheelEvent* event);
void handleControllerAxisEvent(SDL_ControllerAxisEvent* event);
void handleControllerAxisEvent(SDL_GamepadAxisEvent* event);
void handleControllerButtonEvent(SDL_ControllerButtonEvent* event);
void handleControllerButtonEvent(SDL_GamepadButtonEvent* event);
void handleControllerDeviceEvent(SDL_ControllerDeviceEvent* event);
void handleControllerDeviceEvent(SDL_GamepadDeviceEvent* event);
#if SDL_VERSION_ATLEAST(2, 0, 14)
void handleControllerSensorEvent(SDL_ControllerSensorEvent* event);
void handleControllerSensorEvent(SDL_GamepadSensorEvent* event);
void handleControllerTouchpadEvent(SDL_ControllerTouchpadEvent* event);
void handleControllerTouchpadEvent(SDL_GamepadTouchpadEvent* event);
#endif
#if SDL_VERSION_ATLEAST(2, 24, 0)
+29 -28
View File
@@ -1,7 +1,8 @@
#include "streaming/session.h"
#include <Limelight.h>
#include <SDL.h>
#include "SDL_compat.h"
#define VK_0 0x30
#define VK_A 0x41
@@ -22,7 +23,7 @@ void SdlInputHandler::performSpecialKeyCombo(KeyCombo combo)
// Push a quit event to the main loop
SDL_Event event;
event.type = SDL_QUIT;
event.type = SDL_EVENT_QUIT;
event.quit.timestamp = SDL_GetTicks();
SDL_PushEvent(&event);
break;
@@ -151,15 +152,15 @@ void SdlInputHandler::handleKeyEvent(SDL_KeyboardEvent* event)
if (event->repeat) {
// Ignore repeat key down events
SDL_assert(event->state == SDL_PRESSED);
SDL_assert(event->state == true);
return;
}
// Check for our special key combos
if ((event->state == SDL_PRESSED) &&
(event->keysym.mod & KMOD_CTRL) &&
(event->keysym.mod & KMOD_ALT) &&
(event->keysym.mod & KMOD_SHIFT)) {
if ((event->state == true) &&
(KEY_MOD(event) & SDL_KMOD_CTRL) &&
(KEY_MOD(event) & SDL_KMOD_ALT) &&
(KEY_MOD(event) & SDL_KMOD_SHIFT)) {
// First we test the SDLK combos for matches,
// that way we ensure that latin keyboard users
// can match to the key they see on their keyboards.
@@ -172,14 +173,14 @@ void SdlInputHandler::handleKeyEvent(SDL_KeyboardEvent* event)
// the scancode of another.
for (int i = 0; i < KeyComboMax; i++) {
if (m_SpecialKeyCombos[i].enabled && event->keysym.sym == m_SpecialKeyCombos[i].keyCode) {
if (m_SpecialKeyCombos[i].enabled && KEY_KEY(event) == m_SpecialKeyCombos[i].keyCode) {
performSpecialKeyCombo(m_SpecialKeyCombos[i].keyCombo);
return;
}
}
for (int i = 0; i < KeyComboMax; i++) {
if (m_SpecialKeyCombos[i].enabled && event->keysym.scancode == m_SpecialKeyCombos[i].scanCode) {
if (m_SpecialKeyCombos[i].enabled && KEY_SCANCODE(event) == m_SpecialKeyCombos[i].scanCode) {
performSpecialKeyCombo(m_SpecialKeyCombos[i].keyCombo);
return;
}
@@ -188,16 +189,16 @@ void SdlInputHandler::handleKeyEvent(SDL_KeyboardEvent* event)
// Set modifier flags
modifiers = 0;
if (event->keysym.mod & KMOD_CTRL) {
if (KEY_MOD(event) & SDL_KMOD_CTRL) {
modifiers |= MODIFIER_CTRL;
}
if (event->keysym.mod & KMOD_ALT) {
if (KEY_MOD(event) & SDL_KMOD_ALT) {
modifiers |= MODIFIER_ALT;
}
if (event->keysym.mod & KMOD_SHIFT) {
if (KEY_MOD(event) & SDL_KMOD_SHIFT) {
modifiers |= MODIFIER_SHIFT;
}
if (event->keysym.mod & KMOD_GUI) {
if (KEY_MOD(event) & SDL_KMOD_GUI) {
if (isSystemKeyCaptureActive()) {
modifiers |= MODIFIER_META;
}
@@ -206,25 +207,25 @@ void SdlInputHandler::handleKeyEvent(SDL_KeyboardEvent* event)
// Set keycode. We explicitly use scancode here because GFE will try to correct
// for AZERTY layouts on the host but it depends on receiving VK_ values matching
// a QWERTY layout to work.
if (event->keysym.scancode >= SDL_SCANCODE_1 && event->keysym.scancode <= SDL_SCANCODE_9) {
if (KEY_SCANCODE(event) >= SDL_SCANCODE_1 && KEY_SCANCODE(event) <= SDL_SCANCODE_9) {
// SDL defines SDL_SCANCODE_0 > SDL_SCANCODE_9, so we need to handle that manually
keyCode = (event->keysym.scancode - SDL_SCANCODE_1) + VK_0 + 1;
keyCode = (KEY_SCANCODE(event) - SDL_SCANCODE_1) + VK_0 + 1;
}
else if (event->keysym.scancode >= SDL_SCANCODE_A && event->keysym.scancode <= SDL_SCANCODE_Z) {
keyCode = (event->keysym.scancode - SDL_SCANCODE_A) + VK_A;
else if (KEY_SCANCODE(event) >= SDL_SCANCODE_A && KEY_SCANCODE(event) <= SDL_SCANCODE_Z) {
keyCode = (KEY_SCANCODE(event) - SDL_SCANCODE_A) + VK_A;
}
else if (event->keysym.scancode >= SDL_SCANCODE_F1 && event->keysym.scancode <= SDL_SCANCODE_F12) {
keyCode = (event->keysym.scancode - SDL_SCANCODE_F1) + VK_F1;
else if (KEY_SCANCODE(event) >= SDL_SCANCODE_F1 && KEY_SCANCODE(event) <= SDL_SCANCODE_F12) {
keyCode = (KEY_SCANCODE(event) - SDL_SCANCODE_F1) + VK_F1;
}
else if (event->keysym.scancode >= SDL_SCANCODE_F13 && event->keysym.scancode <= SDL_SCANCODE_F24) {
keyCode = (event->keysym.scancode - SDL_SCANCODE_F13) + VK_F13;
else if (KEY_SCANCODE(event) >= SDL_SCANCODE_F13 && KEY_SCANCODE(event) <= SDL_SCANCODE_F24) {
keyCode = (KEY_SCANCODE(event) - SDL_SCANCODE_F13) + VK_F13;
}
else if (event->keysym.scancode >= SDL_SCANCODE_KP_1 && event->keysym.scancode <= SDL_SCANCODE_KP_9) {
else if (KEY_SCANCODE(event) >= SDL_SCANCODE_KP_1 && KEY_SCANCODE(event) <= SDL_SCANCODE_KP_9) {
// SDL defines SDL_SCANCODE_KP_0 > SDL_SCANCODE_KP_9, so we need to handle that manually
keyCode = (event->keysym.scancode - SDL_SCANCODE_KP_1) + VK_NUMPAD0 + 1;
keyCode = (KEY_SCANCODE(event) - SDL_SCANCODE_KP_1) + VK_NUMPAD0 + 1;
}
else {
switch (event->keysym.scancode) {
switch (KEY_SCANCODE(event)) {
case SDL_SCANCODE_BACKSPACE:
keyCode = 0x08;
break;
@@ -417,13 +418,13 @@ void SdlInputHandler::handleKeyEvent(SDL_KeyboardEvent* event)
default:
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Unhandled button event: %d",
event->keysym.scancode);
KEY_SCANCODE(event));
return;
}
}
// Track the key state so we always know which keys are down
if (event->state == SDL_PRESSED) {
if (event->state == true) {
m_KeysDown.insert(keyCode);
}
else {
@@ -431,7 +432,7 @@ void SdlInputHandler::handleKeyEvent(SDL_KeyboardEvent* event)
}
LiSendKeyboardEvent(0x8000 | keyCode,
event->state == SDL_PRESSED ?
KEY_ACTION_DOWN : KEY_ACTION_UP,
event->state == true ?
KEY_ACTION_DOWN : KEY_ACTION_UP,
modifiers);
}
+14 -14
View File
@@ -1,7 +1,7 @@
#include "input.h"
#include <Limelight.h>
#include <SDL.h>
#include "SDL_compat.h"
#include "streaming/streamutils.h"
void SdlInputHandler::handleMouseButtonEvent(SDL_MouseButtonEvent* event)
@@ -13,7 +13,7 @@ void SdlInputHandler::handleMouseButtonEvent(SDL_MouseButtonEvent* event)
return;
}
else if (!isCaptureActive()) {
if (event->button == SDL_BUTTON_LEFT && event->state == SDL_RELEASED &&
if (event->button == SDL_BUTTON_LEFT && event->state == false &&
isMouseInVideoRegion(event->x, event->y)) {
// Capture the mouse again if clicked when unbound.
// We start capture on left button released instead of
@@ -26,7 +26,7 @@ void SdlInputHandler::handleMouseButtonEvent(SDL_MouseButtonEvent* event)
// Not capturing
return;
}
else if (m_AbsoluteMouseMode && !isMouseInVideoRegion(event->x, event->y) && event->state == SDL_PRESSED) {
else if (m_AbsoluteMouseMode && !isMouseInVideoRegion(event->x, event->y) && event->state == true) {
// Ignore button presses outside the video region, but allow button releases
return;
}
@@ -62,7 +62,7 @@ void SdlInputHandler::handleMouseButtonEvent(SDL_MouseButtonEvent* event)
button = BUTTON_RIGHT;
}
LiSendMouseButtonEvent(event->state == SDL_PRESSED ?
LiSendMouseButtonEvent(event->state == true ?
BUTTON_ACTION_PRESS :
BUTTON_ACTION_RELEASE,
button);
@@ -82,7 +82,7 @@ void SdlInputHandler::handleMouseMotionEvent(SDL_MouseMotionEvent* event)
// Batch all pending mouse motion events to save CPU time
Sint32 x = event->x, y = event->y, xrel = event->xrel, yrel = event->yrel;
SDL_Event nextEvent;
while (SDL_PeepEvents(&nextEvent, 1, SDL_GETEVENT, SDL_MOUSEMOTION, SDL_MOUSEMOTION) > 0) {
while (SDL_PeepEvents(&nextEvent, 1, SDL_GETEVENT, SDL_EVENT_MOUSE_MOTION, SDL_EVENT_MOUSE_MOTION) > 0) {
event = &nextEvent.motion;
// Ignore synthetic mouse events
@@ -175,34 +175,34 @@ void SdlInputHandler::handleMouseWheelEvent(SDL_MouseWheelEvent* event)
}
#if SDL_VERSION_ATLEAST(2, 0, 18)
if (event->preciseY != 0.0f) {
if (event->y != 0.0f) {
// Invert the scroll direction if needed
if (m_ReverseScrollDirection) {
event->preciseY = -event->preciseY;
event->y = -event->y;
}
#ifdef Q_OS_DARWIN
// HACK: Clamp the scroll values on macOS to prevent OS scroll acceleration
// from generating wild scroll deltas when scrolling quickly.
event->preciseY = SDL_clamp(event->preciseY, -1.0f, 1.0f);
event->y = SDL_clamp(event->y, -1.0f, 1.0f);
#endif
LiSendHighResScrollEvent((short)(event->preciseY * 120)); // WHEEL_DELTA
LiSendHighResScrollEvent((short)(event->y * 120)); // WHEEL_DELTA
}
if (event->preciseX != 0.0f) {
if (event->x != 0.0f) {
// Invert the scroll direction if needed
if (m_ReverseScrollDirection) {
event->preciseX = -event->preciseY;
event->x = -event->y;
}
#ifdef Q_OS_DARWIN
// HACK: Clamp the scroll values on macOS to prevent OS scroll acceleration
// from generating wild scroll deltas when scrolling quickly.
event->preciseX = SDL_clamp(event->preciseX, -1.0f, 1.0f);
event->x = SDL_clamp(event->x, -1.0f, 1.0f);
#endif
LiSendHighResHScrollEvent((short)(event->preciseX * 120)); // WHEEL_DELTA
LiSendHighResHScrollEvent((short)(event->x * 120)); // WHEEL_DELTA
}
#else
if (event->y != 0) {
@@ -270,7 +270,7 @@ void SdlInputHandler::updatePointerRegionLock()
// have full control over it and we don't touch it anymore.
if (!m_PointerRegionLockToggledByUser) {
// Lock the pointer in true full-screen mode and leave it unlocked in other modes
m_PointerRegionLockActive = (SDL_GetWindowFlags(m_Window) & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN;
m_PointerRegionLockActive = SDLC_IsFullscreenExclusive(m_Window);
}
// If region lock is enabled, grab the cursor so it can't accidentally leave our window.
+12 -12
View File
@@ -1,7 +1,7 @@
#include "input.h"
#include <Limelight.h>
#include <SDL.h>
#include "SDL_compat.h"
#include <QtMath>
@@ -59,9 +59,9 @@ void SdlInputHandler::handleRelativeFingerEvent(SDL_TouchFingerEvent* event)
// This is also required to handle finger up which
// where the finger will not be in SDL_GetTouchFinger()
// anymore.
if (event->type != SDL_FINGERDOWN) {
if (event->type != SDL_EVENT_FINGER_DOWN) {
for (int i = 0; i < MAX_FINGERS; i++) {
if (event->fingerId == m_TouchDownEvent[i].fingerId) {
if (event->fingerID == m_TouchDownEvent[i].fingerID) {
fingerIndex = i;
break;
}
@@ -70,12 +70,12 @@ void SdlInputHandler::handleRelativeFingerEvent(SDL_TouchFingerEvent* event)
else {
// Resolve the new finger by determining the ID of each
// finger on the display.
int numTouchFingers = SDL_GetNumTouchFingers(event->touchId);
int numTouchFingers = SDL_GetNumTouchFingers(event->touchID);
for (int i = 0; i < numTouchFingers; i++) {
SDL_Finger* finger = SDL_GetTouchFinger(event->touchId, i);
SDL_Finger* finger = SDL_GetTouchFinger(event->touchID, i);
SDL_assert(finger != nullptr);
if (finger != nullptr) {
if (finger->id == event->fingerId) {
if (finger->id == event->fingerID) {
fingerIndex = i;
break;
}
@@ -106,7 +106,7 @@ void SdlInputHandler::handleRelativeFingerEvent(SDL_TouchFingerEvent* event)
// Start a drag timer when primary or secondary
// fingers go down
if (event->type == SDL_FINGERDOWN &&
if (event->type == SDL_EVENT_FINGER_DOWN &&
(fingerIndex == 0 || fingerIndex == 1)) {
SDL_RemoveTimer(m_DragTimer);
m_DragTimer = SDL_AddTimer(DRAG_ACTIVATION_DELAY,
@@ -114,7 +114,7 @@ void SdlInputHandler::handleRelativeFingerEvent(SDL_TouchFingerEvent* event)
this);
}
if (event->type == SDL_FINGERMOTION) {
if (event->type == SDL_EVENT_FINGER_MOTION) {
// If it's outside the deadzone delta, cancel drags and taps
if (qSqrt(qPow(event->x - m_TouchDownEvent[fingerIndex].x, 2) +
qPow(event->y - m_TouchDownEvent[fingerIndex].y, 2)) > DEAD_ZONE_DELTA) {
@@ -126,7 +126,7 @@ void SdlInputHandler::handleRelativeFingerEvent(SDL_TouchFingerEvent* event)
}
}
if (event->type == SDL_FINGERUP) {
if (event->type == SDL_EVENT_FINGER_UP) {
// Cancel the drag timer on finger up
SDL_RemoveTimer(m_DragTimer);
m_DragTimer = 0;
@@ -164,12 +164,12 @@ void SdlInputHandler::handleRelativeFingerEvent(SDL_TouchFingerEvent* event)
}
}
m_NumFingersDown = SDL_GetNumTouchFingers(event->touchId);
m_NumFingersDown = SDL_GetNumTouchFingers(event->touchID);
if (event->type == SDL_FINGERDOWN) {
if (event->type == SDL_EVENT_FINGER_DOWN) {
m_TouchDownEvent[fingerIndex] = *event;
}
else if (event->type == SDL_FINGERUP) {
else if (event->type == SDL_EVENT_FINGER_UP) {
m_TouchDownEvent[fingerIndex] = {};
}
}
+382 -364
View File
@@ -4,7 +4,7 @@
#include "backend/richpresencemanager.h"
#include <Limelight.h>
#include <SDL.h>
#include "SDL_compat.h"
#include "utils.h"
#ifdef HAVE_FFMPEG
@@ -24,7 +24,6 @@
// HACK: Remove once proper Dark Mode support lands in SDL
#ifdef Q_OS_WIN32
#include <SDL_syswm.h>
#include <dwmapi.h>
#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE_OLD
#define DWMWA_USE_IMMERSIVE_DARK_MODE_OLD 19
@@ -170,7 +169,7 @@ void Session::clRumble(unsigned short controllerNumber, unsigned short lowFreqMo
// with the removal of game controllers that could result in our game controller
// going away during this callback.
SDL_Event rumbleEvent = {};
rumbleEvent.type = SDL_USEREVENT;
rumbleEvent.type = SDL_EVENT_USER;
rumbleEvent.user.code = SDL_CODE_GAMECONTROLLER_RUMBLE;
rumbleEvent.user.data1 = (void*)(uintptr_t)controllerNumber;
rumbleEvent.user.data2 = (void*)(uintptr_t)((lowFreqMotor << 16) | highFreqMotor);
@@ -211,12 +210,12 @@ void Session::clSetHdrMode(bool enabled)
// If we're in the process of recreating our decoder when we get
// this callback, we'll drop it. The main thread will make the
// callback when it finishes creating the new decoder.
if (SDL_AtomicTryLock(&s_ActiveSession->m_DecoderLock)) {
if (SDL_TryLockSpinlock(&s_ActiveSession->m_DecoderLock)) {
IVideoDecoder* decoder = s_ActiveSession->m_VideoDecoder;
if (decoder != nullptr) {
decoder->setHdrMode(enabled);
}
SDL_AtomicUnlock(&s_ActiveSession->m_DecoderLock);
SDL_UnlockSpinlock(&s_ActiveSession->m_DecoderLock);
}
}
@@ -226,7 +225,7 @@ void Session::clRumbleTriggers(uint16_t controllerNumber, uint16_t leftTrigger,
// with the removal of game controllers that could result in our game controller
// going away during this callback.
SDL_Event rumbleEvent = {};
rumbleEvent.type = SDL_USEREVENT;
rumbleEvent.type = SDL_EVENT_USER;
rumbleEvent.user.code = SDL_CODE_GAMECONTROLLER_RUMBLE_TRIGGERS;
rumbleEvent.user.data1 = (void*)(uintptr_t)controllerNumber;
rumbleEvent.user.data2 = (void*)(uintptr_t)((leftTrigger << 16) | rightTrigger);
@@ -239,7 +238,7 @@ void Session::clSetMotionEventState(uint16_t controllerNumber, uint8_t motionTyp
// with the removal of game controllers that could result in our game controller
// going away during this callback.
SDL_Event setMotionEventStateEvent = {};
setMotionEventStateEvent.type = SDL_USEREVENT;
setMotionEventStateEvent.type = SDL_EVENT_USER;
setMotionEventStateEvent.user.code = SDL_CODE_GAMECONTROLLER_SET_MOTION_EVENT_STATE;
setMotionEventStateEvent.user.data1 = (void*)(uintptr_t)controllerNumber;
setMotionEventStateEvent.user.data2 = (void*)(uintptr_t)((motionType << 16) | reportRateHz);
@@ -252,7 +251,7 @@ void Session::clSetControllerLED(uint16_t controllerNumber, uint8_t r, uint8_t g
// with the removal of game controllers that could result in our game controller
// going away during this callback.
SDL_Event setControllerLEDEvent = {};
setControllerLEDEvent.type = SDL_USEREVENT;
setControllerLEDEvent.type = SDL_EVENT_USER;
setControllerLEDEvent.user.code = SDL_CODE_GAMECONTROLLER_SET_CONTROLLER_LED;
setControllerLEDEvent.user.data1 = (void*)(uintptr_t)controllerNumber;
setControllerLEDEvent.user.data2 = (void*)(uintptr_t)(r << 16 | g << 8 | b);
@@ -349,15 +348,15 @@ int Session::drSubmitDecodeUnit(PDECODE_UNIT du)
// safely return DR_OK and wait for the IDR frame request by
// the decoder reinitialization code.
if (SDL_AtomicTryLock(&s_ActiveSession->m_DecoderLock)) {
if (SDL_TryLockSpinlock(&s_ActiveSession->m_DecoderLock)) {
IVideoDecoder* decoder = s_ActiveSession->m_VideoDecoder;
if (decoder != nullptr) {
int ret = decoder->submitDecodeUnit(du);
SDL_AtomicUnlock(&s_ActiveSession->m_DecoderLock);
SDL_UnlockSpinlock(&s_ActiveSession->m_DecoderLock);
return ret;
}
else {
SDL_AtomicUnlock(&s_ActiveSession->m_DecoderLock);
SDL_UnlockSpinlock(&s_ActiveSession->m_DecoderLock);
return DR_OK;
}
}
@@ -551,6 +550,9 @@ Session::Session(NvComputer* computer, NvApp& app, StreamingPreferences *prefere
m_InputHandler(nullptr),
m_MouseEmulationRefCount(0),
m_FlushingWindowEventsRef(0),
m_CurrentDisplay(-1),
m_NeedsFirstEnterCapture(false),
m_NeedsPostDecoderCreationCapture(false),
m_AsyncConnectionSuccess(false),
m_PortTestResults(0),
m_OpusDecoder(nullptr),
@@ -604,7 +606,7 @@ bool Session::initialize()
}
#endif
if (SDL_InitSubSystem(SDL_INIT_VIDEO) != 0) {
if (SDLC_FAILURE(SDL_InitSubSystem(SDL_INIT_VIDEO))) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_InitSubSystem(SDL_INIT_VIDEO) failed: %s",
SDL_GetError());
@@ -619,21 +621,15 @@ bool Session::initialize()
getWindowDimensions(x, y, width, height);
// Create a hidden window to use for decoder initialization tests
SDL_Window* testWindow = SDL_CreateWindow("", x, y, width, height,
SDL_WINDOW_HIDDEN | StreamUtils::getPlatformWindowFlags());
SDL_Window* testWindow = SDLC_CreateWindowWithFallback("", x, y, width, height,
SDL_WINDOW_HIDDEN,
StreamUtils::getPlatformWindowFlags());
if (!testWindow) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Failed to create test window with platform flags: %s",
SDL_GetError());
testWindow = SDL_CreateWindow("", x, y, width, height, SDL_WINDOW_HIDDEN);
if (!testWindow) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Failed to create window for hardware decode test: %s",
SDL_GetError());
SDL_QuitSubSystem(SDL_INIT_VIDEO);
return false;
}
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Failed to create window for hardware decode test: %s",
SDL_GetError());
SDL_QuitSubSystem(SDL_INIT_VIDEO);
return false;
}
qInfo() << "Server GPU:" << m_Computer->gpuModel;
@@ -852,7 +848,7 @@ bool Session::initialize()
case StreamingPreferences::WM_FULLSCREEN_DESKTOP:
// Only use full-screen desktop mode if we're running a desktop environment
if (WMUtils::isRunningDesktopEnvironment()) {
m_FullScreenFlag = SDL_WINDOW_FULLSCREEN_DESKTOP;
m_FullScreenExclusiveMode = false;
break;
}
// Fall-through
@@ -860,13 +856,13 @@ bool Session::initialize()
#ifdef Q_OS_DARWIN
if (qEnvironmentVariableIntValue("I_WANT_BUGGY_FULLSCREEN") == 0) {
// Don't use "real" fullscreen on macOS by default. See comments above.
m_FullScreenFlag = SDL_WINDOW_FULLSCREEN_DESKTOP;
m_FullScreenExclusiveMode = false;
}
else {
m_FullScreenFlag = SDL_WINDOW_FULLSCREEN;
m_FullScreenExclusiveMode = true;
}
#else
m_FullScreenFlag = SDL_WINDOW_FULLSCREEN;
m_FullScreenExclusiveMode = true;
#endif
break;
}
@@ -878,7 +874,7 @@ bool Session::initialize()
if (qgetenv("DESKTOP_SESSION") == "LXDE-pi") {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Forcing windowed mode on LXDE-Pi");
m_FullScreenFlag = 0;
m_FullScreenExclusiveMode = false;
}
#endif
@@ -902,27 +898,35 @@ bool Session::initialize()
return false;
}
// Display launch warnings in Qt only after destroying SDL's window.
// This avoids conflicts between the windows on display subsystems
// such as KMSDRM that only support a single window.
for (const auto &text : m_LaunchWarnings) {
// Emit the warning to the UI
emit displayLaunchWarning(text);
// Wait a little bit so the user can actually read what we just said.
// This wait is a little longer than the actual toast timeout (3 seconds)
// to allow it to transition off the screen before continuing.
uint32_t start = SDL_GetTicks();
while (!SDL_TICKS_PASSED(SDL_GetTicks(), start + 3500)) {
SDL_Delay(5);
if (!m_ThreadedExec) {
// Pump the UI loop while we wait if we're on the main thread
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
QCoreApplication::sendPostedEvents();
}
}
}
return true;
}
void Session::emitLaunchWarning(QString text)
{
// Emit the warning to the UI
emit displayLaunchWarning(text);
// Wait a little bit so the user can actually read what we just said.
// This wait is a little longer than the actual toast timeout (3 seconds)
// to allow it to transition off the screen before continuing.
uint32_t start = SDL_GetTicks();
while (!SDL_TICKS_PASSED(SDL_GetTicks(), start + 3500)) {
SDL_Delay(5);
if (!m_ThreadedExec) {
// Pump the UI loop while we wait if we're on the main thread
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
QCoreApplication::sendPostedEvents();
}
}
// Queue this launch warning to be displayed after validation
m_LaunchWarnings.append(text);
}
bool Session::validateLaunch(SDL_Window* testWindow)
@@ -1257,11 +1261,10 @@ private:
void Session::getWindowDimensions(int& x, int& y,
int& width, int& height)
{
int displayIndex = 0;
SDL_DisplayID display = SDL_GetPrimaryDisplay();
if (m_Window != nullptr) {
displayIndex = SDL_GetWindowDisplayIndex(m_Window);
SDL_assert(displayIndex >= 0);
display = SDL_GetDisplayForWindow(m_Window);
}
// Create our window on the same display that Qt's UI
// was being displayed on.
@@ -1275,25 +1278,28 @@ void Session::getWindowDimensions(int& x, int& y,
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Qt UI screen is at (%d,%d)",
displayRect.x(), displayRect.y());
for (int i = 0; i < SDL_GetNumVideoDisplays(); i++) {
int numDisplays = 0;
SDL_DisplayID* displays = SDL_GetDisplays(&numDisplays);
for (int i = 0; i < numDisplays; i++) {
SDL_Rect displayBounds;
if (SDL_GetDisplayBounds(i, &displayBounds) == 0) {
if (SDLC_SUCCESS(SDL_GetDisplayBounds(displays[i], &displayBounds))) {
if (displayBounds.x == displayRect.x() &&
displayBounds.y == displayRect.y()) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"SDL found matching display %d",
i);
displayIndex = i;
displays[i]);
display = displays[i];
break;
}
}
else {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"SDL_GetDisplayBounds(%d) failed: %s",
i, SDL_GetError());
displays[i], SDL_GetError());
}
}
SDL_free(displays);
}
else {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
@@ -1303,7 +1309,7 @@ void Session::getWindowDimensions(int& x, int& y,
}
SDL_Rect usableBounds;
if (SDL_GetDisplayUsableBounds(displayIndex, &usableBounds) == 0) {
if (SDLC_SUCCESS(SDL_GetDisplayUsableBounds(display, &usableBounds))) {
// Don't use more than 80% of the display to leave room for system UI
// and ensure the target size is not odd (otherwise one of the sides
// of the image will have a one-pixel black bar next to it).
@@ -1337,22 +1343,27 @@ void Session::getWindowDimensions(int& x, int& y,
height = m_StreamConfig.height;
}
x = y = SDL_WINDOWPOS_CENTERED_DISPLAY(displayIndex);
x = y = SDL_WINDOWPOS_CENTERED_DISPLAY(display);
}
void Session::updateOptimalWindowDisplayMode()
{
SDL_DisplayMode desktopMode, bestMode, mode;
int displayIndex = SDL_GetWindowDisplayIndex(m_Window);
// Nothing to do if we're not using full-screen exclusive mode
if (!m_FullScreenExclusiveMode) {
return;
}
// Try the current display mode first. On macOS, this will be the normal
// scaled desktop resolution setting.
if (SDL_GetDesktopDisplayMode(displayIndex, &desktopMode) == 0) {
SDL_DisplayID display = SDL_GetDisplayForWindow(m_Window);
if (SDL_GetDesktopDisplayMode(display, &desktopMode) == 0) {
// If this doesn't fit the selected resolution, use the native
// resolution of the panel (unscaled).
if (desktopMode.w < m_ActiveVideoWidth || desktopMode.h < m_ActiveVideoHeight) {
SDL_Rect safeArea;
if (!StreamUtils::getNativeDesktopMode(displayIndex, &desktopMode, &safeArea)) {
if (!StreamUtils::getNativeDesktopMode(display, &desktopMode, &safeArea)) {
return;
}
}
@@ -1368,15 +1379,18 @@ void Session::updateOptimalWindowDisplayMode()
// the highest refresh rate that our stream FPS evenly divides.
bestMode = desktopMode;
bestMode.refresh_rate = 0;
for (int i = 0; i < SDL_GetNumDisplayModes(displayIndex); i++) {
if (SDL_GetDisplayMode(displayIndex, i, &mode) == 0) {
if (mode.w == desktopMode.w && mode.h == desktopMode.h &&
{
int numDisplayModes = SDL_GetNumDisplayModes(display);
for (int i = 0; i < numDisplayModes; i++) {
if (SDL_GetDisplayMode(display, i, &mode) == 0) {
if (mode.w == desktopMode.w && mode.h == desktopMode.h &&
mode.refresh_rate % m_StreamConfig.fps == 0) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Found display mode with desktop resolution: %dx%dx%d",
mode.w, mode.h, mode.refresh_rate);
if (mode.refresh_rate > bestMode.refresh_rate) {
bestMode = mode;
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Found display mode with desktop resolution: %dx%dx%d",
mode.w, mode.h, mode.refresh_rate);
if (mode.refresh_rate > bestMode.refresh_rate) {
bestMode = mode;
}
}
}
}
@@ -1390,8 +1404,9 @@ void Session::updateOptimalWindowDisplayMode()
if (bestMode.refresh_rate == 0) {
float bestModeAspectRatio = 0;
float videoAspectRatio = (float)m_ActiveVideoWidth / (float)m_ActiveVideoHeight;
for (int i = 0; i < SDL_GetNumDisplayModes(displayIndex); i++) {
if (SDL_GetDisplayMode(displayIndex, i, &mode) == 0) {
int numDisplayModes = SDL_GetNumDisplayModes(display);
for (int i = 0; i < numDisplayModes; i++) {
if (SDL_GetDisplayMode(display, i, &mode) == 0) {
float modeAspectRatio = (float)mode.w / (float)mode.h;
if (mode.w >= m_ActiveVideoWidth && mode.h >= m_ActiveVideoHeight &&
mode.refresh_rate % m_StreamConfig.fps == 0) {
@@ -1418,7 +1433,7 @@ void Session::updateOptimalWindowDisplayMode()
bestMode = desktopMode;
}
if ((SDL_GetWindowFlags(m_Window) & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN) {
if (SDLC_IsFullscreenExclusive(m_Window)) {
// Only print when the window is actually in full-screen exclusive mode,
// otherwise we're not actually using the mode we've set here
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
@@ -1426,12 +1441,12 @@ void Session::updateOptimalWindowDisplayMode()
bestMode.w, bestMode.h, bestMode.refresh_rate);
}
SDL_SetWindowDisplayMode(m_Window, &bestMode);
SDL_SetWindowFullscreenMode(m_Window, &bestMode);
}
void Session::toggleFullscreen()
{
bool fullScreen = !(SDL_GetWindowFlags(m_Window) & m_FullScreenFlag);
bool enterFullScreen = !SDLC_IsFullscreen(m_Window);
#if defined(Q_OS_WIN32) || defined(Q_OS_DARWIN)
// Destroy the video decoder before toggling full-screen because D3D9 can try
@@ -1442,20 +1457,25 @@ void Session::toggleFullscreen()
// On Apple Silicon Macs, the AVSampleBufferDisplayLayer may cause WindowServer
// to deadlock when transitioning out of fullscreen. Destroy the decoder before
// exiting fullscreen as a workaround. See issue #973.
SDL_AtomicLock(&m_DecoderLock);
SDL_LockSpinlock(&m_DecoderLock);
delete m_VideoDecoder;
m_VideoDecoder = nullptr;
SDL_AtomicUnlock(&m_DecoderLock);
SDL_UnlockSpinlock(&m_DecoderLock);
#endif
// Actually enter/leave fullscreen
SDL_SetWindowFullscreen(m_Window, fullScreen ? m_FullScreenFlag : 0);
if (enterFullScreen) {
SDLC_EnterFullscreen(m_Window, m_FullScreenExclusiveMode);
}
else {
SDLC_LeaveFullscreen(m_Window);
}
#ifdef Q_OS_DARWIN
// SDL on macOS has a bug that causes the window size to be reset to crazy
// large dimensions when exiting out of true fullscreen mode. We can work
// around the issue by manually resetting the position and size here.
if (!fullScreen && m_FullScreenFlag == SDL_WINDOW_FULLSCREEN) {
if (!enterFullScreen && m_FullScreenExclusiveMode) {
int x, y, width, height;
getWindowDimensions(x, y, width, height);
SDL_SetWindowSize(m_Window, width, height);
@@ -1659,11 +1679,213 @@ void Session::flushWindowEvents()
// This event will cause us to set m_FlushingWindowEvents back to false.
SDL_Event flushEvent = {};
flushEvent.type = SDL_USEREVENT;
flushEvent.type = SDL_EVENT_USER;
flushEvent.user.code = SDL_CODE_FLUSH_WINDOW_EVENT_BARRIER;
SDL_PushEvent(&flushEvent);
}
bool Session::handleWindowEvent(SDL_WindowEvent* event)
{
// Early handling of some events
switch (event->event) {
case SDL_EVENT_WINDOW_FOCUS_LOST :
if (m_Preferences->muteOnFocusLoss) {
m_AudioMuted = true;
}
m_InputHandler->notifyFocusLost();
break;
case SDL_EVENT_WINDOW_FOCUS_GAINED :
if (m_Preferences->muteOnFocusLoss) {
m_AudioMuted = false;
}
break;
case SDL_EVENT_WINDOW_MOUSE_LEAVE :
m_InputHandler->notifyMouseLeave();
break;
}
// Capture the mouse on SDL_WINDOWEVENT_ENTER if needed
if (m_NeedsFirstEnterCapture && event->event == SDL_EVENT_WINDOW_MOUSE_ENTER) {
m_InputHandler->setCaptureActive(true);
m_NeedsFirstEnterCapture = false;
}
// We want to recreate the decoder for resizes (full-screen toggles) and the initial shown event.
// We use SDL_WINDOWEVENT_SIZE_CHANGED rather than SDL_WINDOWEVENT_RESIZED because the latter doesn't
// seem to fire when switching from windowed to full-screen on X11.
if (event->event != SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED &&
(event->event != SDL_EVENT_WINDOW_SHOWN || m_VideoDecoder != nullptr)) {
// Check that the window display hasn't changed. If it has, we want
// to recreate the decoder to allow it to adapt to the new display.
// This will allow Pacer to pull the new display refresh rate.
#if SDL_VERSION_ATLEAST(2, 0, 18)
// On SDL 2.0.18+, there's an event for this specific situation
if (event->event != SDL_EVENT_WINDOW_DISPLAY_CHANGED) {
return true;
}
#else
// Prior to SDL 2.0.18, we must check the display index for each window event
if (SDL_GetDisplayForWindow(m_Window) == m_CurrentDisplay) {
return true;
}
#endif
}
#ifdef Q_OS_WIN32
// We can get a resize event after being minimized. Recreating the renderer at that time can cause
// us to start drawing on the screen even while our window is minimized. Minimizing on Windows also
// moves the window to -32000, -32000 which can cause a false window display index change. Avoid
// that whole mess by never recreating the decoder if we're minimized.
else if (SDL_GetWindowFlags(m_Window) & SDL_WINDOW_MINIMIZED) {
return true;
}
#endif
if (m_FlushingWindowEventsRef > 0) {
// Ignore window events for renderer reset if flushing
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Dropping window event during flush: %d (%d %d)",
event->event,
event->data1,
event->data2);
return true;
}
// Allow the renderer to handle the state change without being recreated
if (m_VideoDecoder) {
bool forceRecreation = false;
WINDOW_STATE_CHANGE_INFO windowChangeInfo = {};
windowChangeInfo.window = m_Window;
if (event->event == SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED) {
windowChangeInfo.stateChangeFlags |= WINDOW_STATE_CHANGE_SIZE;
windowChangeInfo.width = event->data1;
windowChangeInfo.height = event->data2;
}
SDL_DisplayID newDisplay = SDL_GetDisplayForWindow(m_Window);
if (newDisplay != m_CurrentDisplay) {
windowChangeInfo.stateChangeFlags |= WINDOW_STATE_CHANGE_DISPLAY;
windowChangeInfo.displayIndex = newDisplay;
// If the refresh rates have changed, we will need to go through the full
// decoder recreation path to ensure Pacer is switched to the new display
// and that we apply any V-Sync disablement rules that may be needed for
// this display.
SDL_DisplayMode oldMode, newMode;
if (SDL_GetCurrentDisplayMode(m_CurrentDisplay, &oldMode) < 0 ||
SDL_GetCurrentDisplayMode(newDisplay, &newMode) < 0 ||
oldMode.refresh_rate != newMode.refresh_rate) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Forcing renderer recreation due to refresh rate change between displays");
forceRecreation = true;
}
}
if (!forceRecreation && m_VideoDecoder->notifyWindowChanged(&windowChangeInfo)) {
// Update the window display mode based on our current monitor
// NB: Avoid a useless modeset by only doing this if it changed.
if (newDisplay != m_CurrentDisplay) {
m_CurrentDisplay = newDisplay;
updateOptimalWindowDisplayMode();
}
return true;
}
}
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Recreating renderer for window event: %d (%d %d)",
event->event,
event->data1,
event->data2);
if (!recreateRenderer()) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Failed to recreate decoder after reset");
emit displayLaunchError(tr("Unable to initialize video decoder. Please check your streaming settings and try again."));
return false;
}
return true;
}
bool Session::recreateRenderer()
{
SDL_LockSpinlock(&m_DecoderLock);
// Destroy the old decoder
delete m_VideoDecoder;
// Insert a barrier to discard any additional window events
// that could cause the renderer to be and recreated again.
// We don't use SDL_FlushEvent() here because it could cause
// important events to be lost.
flushWindowEvents();
// Update the window display mode based on our current monitor
// NB: Avoid a useless modeset by only doing this if it changed.
if (m_CurrentDisplay != SDL_GetDisplayForWindow(m_Window)) {
m_CurrentDisplay = SDL_GetDisplayForWindow(m_Window);
updateOptimalWindowDisplayMode();
}
// Now that the old decoder is dead, flush any events it may
// have queued to reset itself (if this reset was the result
// of state loss).
SDL_PumpEvents();
SDL_FlushEvent(SDL_EVENT_RENDER_DEVICE_RESET);
SDL_FlushEvent(SDL_EVENT_RENDER_TARGETS_RESET);
{
// If the stream exceeds the display refresh rate (plus some slack),
// forcefully disable V-sync to allow the stream to render faster
// than the display.
int displayHz = StreamUtils::getDisplayRefreshRate(m_Window);
bool enableVsync = m_Preferences->enableVsync;
if (displayHz + 5 < m_StreamConfig.fps) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Disabling V-sync because refresh rate limit exceeded");
enableVsync = false;
}
// Choose a new decoder (hopefully the same one, but possibly
// not if a GPU was removed or something).
if (!chooseDecoder(m_Preferences->videoDecoderSelection,
m_Window, m_ActiveVideoFormat, m_ActiveVideoWidth,
m_ActiveVideoHeight, m_ActiveVideoFrameRate,
enableVsync,
enableVsync && m_Preferences->framePacing,
false,
s_ActiveSession->m_VideoDecoder)) {
SDL_UnlockSpinlock(&m_DecoderLock);
return false;
}
// As of SDL 2.0.12, SDL_RecreateWindow() doesn't carry over mouse capture
// or mouse hiding state to the new window. By capturing after the decoder
// is set up, this ensures the window re-creation is already done.
if (m_NeedsPostDecoderCreationCapture) {
m_InputHandler->setCaptureActive(true);
m_NeedsPostDecoderCreationCapture = false;
}
}
// Request an IDR frame to complete the reset
LiRequestIdrFrame();
// Set HDR mode. We may miss the callback if we're in the middle
// of recreating our decoder at the time the HDR transition happens.
m_VideoDecoder->setHdrMode(LiGetCurrentHostDisplayHdrMode());
// After a window resize, we need to reset the pointer lock region
m_InputHandler->updatePointerRegionLock();
SDL_UnlockSpinlock(&m_DecoderLock);
return true;
}
class ExecThread : public QThread
{
public:
@@ -1786,7 +2008,7 @@ void Session::execInternal()
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
// We always want a resizable window with High DPI enabled
Uint32 defaultWindowFlags = SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_RESIZABLE;
Uint32 defaultWindowFlags = SDL_WINDOW_HIGH_PIXEL_DENSITY | SDL_WINDOW_RESIZABLE;
// If we're starting in windowed mode and the Moonlight GUI is maximized or
// minimized, match that with the streaming window.
@@ -1818,34 +2040,23 @@ void Session::execInternal()
std::string windowName = QString(m_Computer->name + " - Moonlight").toStdString();
#endif
m_Window = SDL_CreateWindow(windowName.c_str(),
x,
y,
width,
height,
defaultWindowFlags | StreamUtils::getPlatformWindowFlags());
m_Window = SDLC_CreateWindowWithFallback(windowName.c_str(),
x,
y,
width,
height,
defaultWindowFlags,
StreamUtils::getPlatformWindowFlags());
if (!m_Window) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"SDL_CreateWindow() failed with platform flags: %s",
SDL_GetError());
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_CreateWindow() failed: %s",
SDL_GetError());
m_Window = SDL_CreateWindow(windowName.c_str(),
x,
y,
width,
height,
defaultWindowFlags);
if (!m_Window) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_CreateWindow() failed: %s",
SDL_GetError());
delete m_InputHandler;
m_InputHandler = nullptr;
SDL_QuitSubSystem(SDL_INIT_VIDEO);
QThreadPool::globalInstance()->start(new DeferredSessionCleanupTask(this));
return;
}
delete m_InputHandler;
m_InputHandler = nullptr;
SDL_QuitSubSystem(SDL_INIT_VIDEO);
QThreadPool::globalInstance()->start(new DeferredSessionCleanupTask(this));
return;
}
// HACK: Remove once proper Dark Mode support lands in SDL
@@ -1859,23 +2070,20 @@ void Session::execInternal()
darkModeEnabled = FALSE;
}
SDL_SysWMinfo info;
SDL_VERSION(&info.version);
// If dark mode is enabled, propagate that to our SDL window
if (darkModeEnabled) {
HWND hwnd = (HWND)SDLC_Win32_GetHwnd(m_Window);
if (SDL_GetWindowWMInfo(m_Window, &info) && info.subsystem == SDL_SYSWM_WINDOWS) {
// If dark mode is enabled, propagate that to our SDL window
if (darkModeEnabled) {
if (FAILED(DwmSetWindowAttribute(info.info.win.window, DWMWA_USE_IMMERSIVE_DARK_MODE, &darkModeEnabled, sizeof(darkModeEnabled)))) {
DwmSetWindowAttribute(info.info.win.window, DWMWA_USE_IMMERSIVE_DARK_MODE_OLD, &darkModeEnabled, sizeof(darkModeEnabled));
}
// Toggle non-client rendering off and back on to ensure dark mode takes effect on Windows 10.
// DWM doesn't seem to correctly invalidate the non-client area after enabling dark mode.
DWMNCRENDERINGPOLICY ncPolicy = DWMNCRP_DISABLED;
DwmSetWindowAttribute(info.info.win.window, DWMWA_NCRENDERING_POLICY, &ncPolicy, sizeof(ncPolicy));
ncPolicy = DWMNCRP_ENABLED;
DwmSetWindowAttribute(info.info.win.window, DWMWA_NCRENDERING_POLICY, &ncPolicy, sizeof(ncPolicy));
if (FAILED(DwmSetWindowAttribute(hwnd, DWMWA_USE_IMMERSIVE_DARK_MODE, &darkModeEnabled, sizeof(darkModeEnabled)))) {
DwmSetWindowAttribute(hwnd, DWMWA_USE_IMMERSIVE_DARK_MODE_OLD, &darkModeEnabled, sizeof(darkModeEnabled));
}
// Toggle non-client rendering off and back on to ensure dark mode takes effect on Windows 10.
// DWM doesn't seem to correctly invalidate the non-client area after enabling dark mode.
DWMNCRENDERINGPOLICY ncPolicy = DWMNCRP_DISABLED;
DwmSetWindowAttribute(hwnd, DWMWA_NCRENDERING_POLICY, &ncPolicy, sizeof(ncPolicy));
ncPolicy = DWMNCRP_ENABLED;
DwmSetWindowAttribute(hwnd, DWMWA_NCRENDERING_POLICY, &ncPolicy, sizeof(ncPolicy));
}
}
#endif
@@ -1888,12 +2096,7 @@ void Session::execInternal()
QPainter svgPainter(&svgImage);
svgIconRenderer.render(&svgPainter);
SDL_Surface* iconSurface = SDL_CreateRGBSurfaceWithFormatFrom((void*)svgImage.constBits(),
svgImage.width(),
svgImage.height(),
32,
4 * svgImage.width(),
SDL_PIXELFORMAT_RGBA32);
SDL_Surface* iconSurface = SDL_CreateSurfaceFrom(svgImage.width(), svgImage.height(), SDL_PIXELFORMAT_RGBA32, (void *)svgImage.constBits(), 4 * svgImage.width());
#ifndef Q_OS_DARWIN
// Other platforms seem to preserve our Qt icon when creating a new window.
if (iconSurface != nullptr) {
@@ -1909,23 +2112,20 @@ void Session::execInternal()
// Enter full screen if requested
if (m_IsFullScreen) {
SDL_SetWindowFullscreen(m_Window, m_FullScreenFlag);
SDLC_EnterFullscreen(m_Window, m_FullScreenExclusiveMode);
}
bool needsFirstEnterCapture = false;
bool needsPostDecoderCreationCapture = false;
// HACK: For Wayland, we wait until we get the first SDL_WINDOWEVENT_ENTER
// event where it seems to work consistently on GNOME. For other platforms,
// especially where SDL may call SDL_RecreateWindow(), we must only capture
// after the decoder is created.
if (strcmp(SDL_GetCurrentVideoDriver(), "wayland") == 0) {
// Native Wayland: Capture on SDL_WINDOWEVENT_ENTER
needsFirstEnterCapture = true;
m_NeedsFirstEnterCapture = true;
}
else {
// X11/XWayland: Capture after decoder creation
needsPostDecoderCreationCapture = true;
m_NeedsPostDecoderCreationCapture = true;
}
// Stop text input. SDL enables it by default
@@ -1948,7 +2148,7 @@ void Session::execInternal()
// sleep precision and more accurate callback timing.
SDL_SetHint(SDL_HINT_TIMER_RESOLUTION, "1");
int currentDisplayIndex = SDL_GetWindowDisplayIndex(m_Window);
m_CurrentDisplay = SDL_GetDisplayForWindow(m_Window);
// Now that we're about to stream, any SDL_QUIT event is expected
// unless it comes from the connection termination callback where
@@ -1996,13 +2196,22 @@ void Session::execInternal()
continue;
}
#endif
if (event.type == SDL_WINDOWEVENT) {
if (!handleWindowEvent(&event.window)) {
goto DispatchDeferredCleanup;
}
presence.runCallbacks();
}
switch (event.type) {
case SDL_QUIT:
case SDL_EVENT_QUIT :
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Quit event received");
goto DispatchDeferredCleanup;
case SDL_USEREVENT:
case SDL_EVENT_USER :
switch (event.user.code) {
case SDL_CODE_FRAME_READY:
if (m_VideoDecoder != nullptr) {
@@ -2037,260 +2246,69 @@ void Session::execInternal()
SDL_assert(false);
}
break;
case SDL_EVENT_RENDER_DEVICE_RESET :
case SDL_EVENT_RENDER_TARGETS_RESET :
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Recreating renderer by internal request: %d",
event.type);
case SDL_WINDOWEVENT:
// Early handling of some events
switch (event.window.event) {
case SDL_WINDOWEVENT_FOCUS_LOST:
if (m_Preferences->muteOnFocusLoss) {
m_AudioMuted = true;
}
m_InputHandler->notifyFocusLost();
break;
case SDL_WINDOWEVENT_FOCUS_GAINED:
if (m_Preferences->muteOnFocusLoss) {
m_AudioMuted = false;
}
break;
case SDL_WINDOWEVENT_LEAVE:
m_InputHandler->notifyMouseLeave();
break;
if (!recreateRenderer()) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Failed to recreate decoder after reset");
emit displayLaunchError(tr("Unable to initialize video decoder. Please check your streaming settings and try again."));
goto DispatchDeferredCleanup;
}
presence.runCallbacks();
// Capture the mouse on SDL_WINDOWEVENT_ENTER if needed
if (needsFirstEnterCapture && event.window.event == SDL_WINDOWEVENT_ENTER) {
m_InputHandler->setCaptureActive(true);
needsFirstEnterCapture = false;
}
// We want to recreate the decoder for resizes (full-screen toggles) and the initial shown event.
// We use SDL_WINDOWEVENT_SIZE_CHANGED rather than SDL_WINDOWEVENT_RESIZED because the latter doesn't
// seem to fire when switching from windowed to full-screen on X11.
if (event.window.event != SDL_WINDOWEVENT_SIZE_CHANGED &&
(event.window.event != SDL_WINDOWEVENT_SHOWN || m_VideoDecoder != nullptr)) {
// Check that the window display hasn't changed. If it has, we want
// to recreate the decoder to allow it to adapt to the new display.
// This will allow Pacer to pull the new display refresh rate.
#if SDL_VERSION_ATLEAST(2, 0, 18)
// On SDL 2.0.18+, there's an event for this specific situation
if (event.window.event != SDL_WINDOWEVENT_DISPLAY_CHANGED) {
break;
}
#else
// Prior to SDL 2.0.18, we must check the display index for each window event
if (SDL_GetWindowDisplayIndex(m_Window) == currentDisplayIndex) {
break;
}
#endif
}
#ifdef Q_OS_WIN32
// We can get a resize event after being minimized. Recreating the renderer at that time can cause
// us to start drawing on the screen even while our window is minimized. Minimizing on Windows also
// moves the window to -32000, -32000 which can cause a false window display index change. Avoid
// that whole mess by never recreating the decoder if we're minimized.
else if (SDL_GetWindowFlags(m_Window) & SDL_WINDOW_MINIMIZED) {
break;
}
#endif
if (m_FlushingWindowEventsRef > 0) {
// Ignore window events for renderer reset if flushing
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Dropping window event during flush: %d (%d %d)",
event.window.event,
event.window.data1,
event.window.data2);
break;
}
// Allow the renderer to handle the state change without being recreated
if (m_VideoDecoder) {
bool forceRecreation = false;
WINDOW_STATE_CHANGE_INFO windowChangeInfo = {};
windowChangeInfo.window = m_Window;
if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
windowChangeInfo.stateChangeFlags |= WINDOW_STATE_CHANGE_SIZE;
windowChangeInfo.width = event.window.data1;
windowChangeInfo.height = event.window.data2;
}
int newDisplayIndex = SDL_GetWindowDisplayIndex(m_Window);
if (newDisplayIndex != currentDisplayIndex) {
windowChangeInfo.stateChangeFlags |= WINDOW_STATE_CHANGE_DISPLAY;
windowChangeInfo.displayIndex = newDisplayIndex;
// If the refresh rates have changed, we will need to go through the full
// decoder recreation path to ensure Pacer is switched to the new display
// and that we apply any V-Sync disablement rules that may be needed for
// this display.
SDL_DisplayMode oldMode, newMode;
if (SDL_GetCurrentDisplayMode(currentDisplayIndex, &oldMode) < 0 ||
SDL_GetCurrentDisplayMode(newDisplayIndex, &newMode) < 0 ||
oldMode.refresh_rate != newMode.refresh_rate) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Forcing renderer recreation due to refresh rate change between displays");
forceRecreation = true;
}
}
if (!forceRecreation && m_VideoDecoder->notifyWindowChanged(&windowChangeInfo)) {
// Update the window display mode based on our current monitor
// NB: Avoid a useless modeset by only doing this if it changed.
if (newDisplayIndex != currentDisplayIndex) {
currentDisplayIndex = newDisplayIndex;
updateOptimalWindowDisplayMode();
}
break;
}
}
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Recreating renderer for window event: %d (%d %d)",
event.window.event,
event.window.data1,
event.window.data2);
// Fall through
case SDL_RENDER_DEVICE_RESET:
case SDL_RENDER_TARGETS_RESET:
if (event.type != SDL_WINDOWEVENT) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Recreating renderer by internal request: %d",
event.type);
}
SDL_AtomicLock(&m_DecoderLock);
// Destroy the old decoder
delete m_VideoDecoder;
// Insert a barrier to discard any additional window events
// that could cause the renderer to be and recreated again.
// We don't use SDL_FlushEvent() here because it could cause
// important events to be lost.
flushWindowEvents();
// Update the window display mode based on our current monitor
// NB: Avoid a useless modeset by only doing this if it changed.
if (currentDisplayIndex != SDL_GetWindowDisplayIndex(m_Window)) {
currentDisplayIndex = SDL_GetWindowDisplayIndex(m_Window);
updateOptimalWindowDisplayMode();
}
// Now that the old decoder is dead, flush any events it may
// have queued to reset itself (if this reset was the result
// of state loss).
SDL_PumpEvents();
SDL_FlushEvent(SDL_RENDER_DEVICE_RESET);
SDL_FlushEvent(SDL_RENDER_TARGETS_RESET);
{
// If the stream exceeds the display refresh rate (plus some slack),
// forcefully disable V-sync to allow the stream to render faster
// than the display.
int displayHz = StreamUtils::getDisplayRefreshRate(m_Window);
bool enableVsync = m_Preferences->enableVsync;
if (displayHz + 5 < m_StreamConfig.fps) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Disabling V-sync because refresh rate limit exceeded");
enableVsync = false;
}
// Choose a new decoder (hopefully the same one, but possibly
// not if a GPU was removed or something).
if (!chooseDecoder(m_Preferences->videoDecoderSelection,
m_Window, m_ActiveVideoFormat, m_ActiveVideoWidth,
m_ActiveVideoHeight, m_ActiveVideoFrameRate,
enableVsync,
enableVsync && m_Preferences->framePacing,
false,
s_ActiveSession->m_VideoDecoder)) {
SDL_AtomicUnlock(&m_DecoderLock);
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Failed to recreate decoder after reset");
emit displayLaunchError(tr("Unable to initialize video decoder. Please check your streaming settings and try again."));
goto DispatchDeferredCleanup;
}
// As of SDL 2.0.12, SDL_RecreateWindow() doesn't carry over mouse capture
// or mouse hiding state to the new window. By capturing after the decoder
// is set up, this ensures the window re-creation is already done.
if (needsPostDecoderCreationCapture) {
m_InputHandler->setCaptureActive(true);
needsPostDecoderCreationCapture = false;
}
}
// Request an IDR frame to complete the reset
LiRequestIdrFrame();
// Set HDR mode. We may miss the callback if we're in the middle
// of recreating our decoder at the time the HDR transition happens.
m_VideoDecoder->setHdrMode(LiGetCurrentHostDisplayHdrMode());
// After a window resize, we need to reset the pointer lock region
m_InputHandler->updatePointerRegionLock();
SDL_AtomicUnlock(&m_DecoderLock);
break;
case SDL_KEYUP:
case SDL_KEYDOWN:
case SDL_EVENT_KEY_UP :
case SDL_EVENT_KEY_DOWN :
presence.runCallbacks();
m_InputHandler->handleKeyEvent(&event.key);
break;
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
case SDL_EVENT_MOUSE_BUTTON_DOWN :
case SDL_EVENT_MOUSE_BUTTON_UP :
presence.runCallbacks();
m_InputHandler->handleMouseButtonEvent(&event.button);
break;
case SDL_MOUSEMOTION:
case SDL_EVENT_MOUSE_MOTION :
m_InputHandler->handleMouseMotionEvent(&event.motion);
break;
case SDL_MOUSEWHEEL:
case SDL_EVENT_MOUSE_WHEEL :
m_InputHandler->handleMouseWheelEvent(&event.wheel);
break;
case SDL_CONTROLLERAXISMOTION:
m_InputHandler->handleControllerAxisEvent(&event.caxis);
case SDL_EVENT_GAMEPAD_AXIS_MOTION :
m_InputHandler->handleControllerAxisEvent(&event.gaxis);
break;
case SDL_CONTROLLERBUTTONDOWN:
case SDL_CONTROLLERBUTTONUP:
case SDL_EVENT_GAMEPAD_BUTTON_DOWN :
case SDL_EVENT_GAMEPAD_BUTTON_UP :
presence.runCallbacks();
m_InputHandler->handleControllerButtonEvent(&event.cbutton);
m_InputHandler->handleControllerButtonEvent(&event.gbutton);
break;
#if SDL_VERSION_ATLEAST(2, 0, 14)
case SDL_CONTROLLERSENSORUPDATE:
m_InputHandler->handleControllerSensorEvent(&event.csensor);
case SDL_EVENT_GAMEPAD_SENSOR_UPDATE :
m_InputHandler->handleControllerSensorEvent(&event.gsensor);
break;
case SDL_CONTROLLERTOUCHPADDOWN:
case SDL_CONTROLLERTOUCHPADUP:
case SDL_CONTROLLERTOUCHPADMOTION:
m_InputHandler->handleControllerTouchpadEvent(&event.ctouchpad);
case SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN :
case SDL_EVENT_GAMEPAD_TOUCHPAD_UP :
case SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION :
m_InputHandler->handleControllerTouchpadEvent(&event.gtouchpad);
break;
#endif
#if SDL_VERSION_ATLEAST(2, 24, 0)
case SDL_JOYBATTERYUPDATED:
case SDL_EVENT_JOYSTICK_BATTERY_UPDATED :
m_InputHandler->handleJoystickBatteryEvent(&event.jbattery);
break;
#endif
case SDL_CONTROLLERDEVICEADDED:
case SDL_CONTROLLERDEVICEREMOVED:
m_InputHandler->handleControllerDeviceEvent(&event.cdevice);
case SDL_EVENT_GAMEPAD_ADDED :
case SDL_EVENT_GAMEPAD_REMOVED :
m_InputHandler->handleControllerDeviceEvent(&event.gdevice);
break;
case SDL_JOYDEVICEADDED:
case SDL_EVENT_JOYSTICK_ADDED :
m_InputHandler->handleJoystickArrivalEvent(&event.jdevice);
break;
case SDL_FINGERDOWN:
case SDL_FINGERMOTION:
case SDL_FINGERUP:
case SDL_EVENT_FINGER_DOWN :
case SDL_EVENT_FINGER_MOTION :
case SDL_EVENT_FINGER_UP :
m_InputHandler->handleTouchFingerEvent(&event.tfinger);
break;
}
@@ -2318,10 +2336,10 @@ DispatchDeferredCleanup:
// Destroy the decoder, since this must be done on the main thread
// NB: This must happen before LiStopConnection() for pull-based
// decoders.
SDL_AtomicLock(&m_DecoderLock);
SDL_LockSpinlock(&m_DecoderLock);
delete m_VideoDecoder;
m_VideoDecoder = nullptr;
SDL_AtomicUnlock(&m_DecoderLock);
SDL_UnlockSpinlock(&m_DecoderLock);
// Propagate state changes from the SDL window back to the Qt window
//
@@ -2353,7 +2371,7 @@ DispatchDeferredCleanup:
SDL_DestroyWindow(m_Window);
if (iconSurface != nullptr) {
SDL_FreeSurface(iconSurface);
SDL_DestroySurface(iconSurface);
}
SDL_QuitSubSystem(SDL_INIT_VIDEO);
+10 -1
View File
@@ -172,6 +172,10 @@ private:
void updateOptimalWindowDisplayMode();
bool recreateRenderer();
bool handleWindowEvent(SDL_WindowEvent* event);
enum class DecoderAvailability {
None,
Software,
@@ -253,13 +257,18 @@ private:
SDL_SpinLock m_DecoderLock;
bool m_AudioDisabled;
bool m_AudioMuted;
Uint32 m_FullScreenFlag;
bool m_FullScreenExclusiveMode;
QWindow* m_QtWindow;
bool m_ThreadedExec;
bool m_UnexpectedTermination;
SdlInputHandler* m_InputHandler;
int m_MouseEmulationRefCount;
int m_FlushingWindowEventsRef;
QList<QString> m_LaunchWarnings;
SDL_DisplayID m_CurrentDisplay;
bool m_NeedsFirstEnterCapture;
bool m_NeedsPostDecoderCreationCapture;
bool m_AsyncConnectionSuccess;
int m_PortTestResults;
+98 -24
View File
@@ -1,6 +1,7 @@
#include "streamutils.h"
#include <Qt>
#include <QDir>
#ifdef Q_OS_DARWIN
#include <ApplicationServices/ApplicationServices.h>
@@ -10,6 +11,11 @@
#include <Windows.h>
#endif
#ifdef Q_OS_UNIX
#include <unistd.h>
#include <fcntl.h>
#endif
#ifdef Q_OS_LINUX
#include <sys/auxv.h>
@@ -105,31 +111,22 @@ void StreamUtils::screenSpaceToNormalizedDeviceCoords(SDL_Rect* src, SDL_FRect*
int StreamUtils::getDisplayRefreshRate(SDL_Window* window)
{
int displayIndex = SDL_GetWindowDisplayIndex(window);
if (displayIndex < 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Failed to get current display: %s",
SDL_GetError());
// Assume display 0 if it fails
displayIndex = 0;
}
SDL_DisplayMode mode;
if ((SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN) {
if (SDLC_IsFullscreenExclusive(window)) {
// Use the window display mode for full-screen exclusive mode
if (SDL_GetWindowDisplayMode(window, &mode) != 0) {
const SDL_DisplayMode *fsMode = SDL_GetWindowFullscreenMode(window);
if (fsMode) {
mode = *fsMode;
}
else {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_GetWindowDisplayMode() failed: %s",
"SDL_GetWindowFullscreenMode() failed: %s",
SDL_GetError());
// Assume 60 Hz
return 60;
}
}
else {
// Use the current display mode for windowed and borderless
if (SDL_GetCurrentDisplayMode(displayIndex, &mode) != 0) {
if (SDL_GetCurrentDisplayMode(SDL_GetDisplayForWindow(window), &mode) != 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_GetCurrentDisplayMode() failed: %s",
SDL_GetError());
@@ -193,7 +190,7 @@ bool StreamUtils::hasFastAes()
#endif
}
bool StreamUtils::getNativeDesktopMode(int displayIndex, SDL_DisplayMode* mode, SDL_Rect* safeArea)
bool StreamUtils::getNativeDesktopMode(SDL_DisplayID display, SDL_DisplayMode* mode, SDL_Rect* safeArea)
{
#ifdef Q_OS_DARWIN
#define MAX_DISPLAYS 16
@@ -271,17 +268,13 @@ bool StreamUtils::getNativeDesktopMode(int displayIndex, SDL_DisplayMode* mode,
#else
SDL_assert(SDL_WasInit(SDL_INIT_VIDEO));
if (displayIndex >= SDL_GetNumVideoDisplays()) {
return false;
}
// We need to get the true display resolution without DPI scaling (since we use High DPI).
// Windows returns the real display resolution here, even if DPI scaling is enabled.
// macOS and Wayland report a resolution that includes the DPI scaling factor. Picking
// the first mode on Wayland will get the native resolution without the scaling factor
// (and macOS is handled in the #ifdef above).
if (!strcmp(SDL_GetCurrentVideoDriver(), "wayland")) {
if (SDL_GetDisplayMode(displayIndex, 0, mode) != 0) {
if (SDL_GetDisplayMode(display, 0, mode) != 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_GetDisplayMode() failed: %s",
SDL_GetError());
@@ -289,7 +282,7 @@ bool StreamUtils::getNativeDesktopMode(int displayIndex, SDL_DisplayMode* mode,
}
}
else {
if (SDL_GetDesktopDisplayMode(displayIndex, mode) != 0) {
if (SDL_GetDesktopDisplayMode(display, mode) != 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_GetDesktopDisplayMode() failed: %s",
SDL_GetError());
@@ -306,3 +299,84 @@ bool StreamUtils::getNativeDesktopMode(int displayIndex, SDL_DisplayMode* mode,
return true;
}
int StreamUtils::getDrmFdForWindow(SDL_Window* window, bool* mustClose)
{
*mustClose = false;
// If SDL has an FD, share that
int fd = SDLC_KMSDRM_GetFd(window);
if (fd >= 0) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Sharing DRM FD with SDL");
return fd;
}
#ifdef Q_OS_UNIX
int devIndex = SDLC_KMSDRM_GetDevIndex(window);
if (devIndex >= 0) {
char path[128];
snprintf(path, sizeof(path), "/dev/dri/card%u", devIndex);
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Opening DRM FD from SDL by path: %s",
path);
int fd = open(path, O_RDWR | O_CLOEXEC);
if (fd >= 0) {
*mustClose = true;
}
return fd;
}
#endif
return -1;
}
int StreamUtils::getDrmFd(bool preferRenderNode)
{
#ifdef Q_OS_UNIX
const char* userDevice = SDL_getenv("DRM_DEV");
if (userDevice != nullptr) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Opening user-specified DRM device: %s",
userDevice);
return open(userDevice, O_RDWR | O_CLOEXEC);
}
else {
QDir driDir("/dev/dri");
int fd;
// We have to explicitly ask for devices to be returned
driDir.setFilter(QDir::Files | QDir::System);
if (preferRenderNode) {
// Try a render node first since we aren't using DRM for output in this codepath
for (QFileInfo& node : driDir.entryInfoList(QStringList("renderD*"))) {
QByteArray absolutePath = node.absoluteFilePath().toUtf8();
fd = open(absolutePath.constData(), O_RDWR | O_CLOEXEC);
if (fd >= 0) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Opened DRM render node: %s",
absolutePath.constData());
return fd;
}
}
}
// If that fails, try to use a primary node and hope for the best
for (QFileInfo& node : driDir.entryInfoList(QStringList("card*"))) {
QByteArray absolutePath = node.absoluteFilePath().toUtf8();
fd = open(absolutePath.constData(), O_RDWR | O_CLOEXEC);
if (fd >= 0) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Opened DRM primary node: %s",
absolutePath.constData());
return fd;
}
}
}
#else
Q_UNUSED(preferRenderNode);
#endif
return -1;
}
+8 -13
View File
@@ -1,17 +1,6 @@
#pragma once
#include <SDL.h>
// SDL_FRect wasn't added until 2.0.10
#if !SDL_VERSION_ATLEAST(2, 0, 10)
typedef struct SDL_FRect
{
float x;
float y;
float w;
float h;
} SDL_FRect;
#endif
#include "SDL_compat.h"
class StreamUtils
{
@@ -29,11 +18,17 @@ public:
void screenSpaceToNormalizedDeviceCoords(SDL_Rect* src, SDL_FRect* dst, int viewportWidth, int viewportHeight);
static
bool getNativeDesktopMode(int displayIndex, SDL_DisplayMode* mode, SDL_Rect* safeArea);
bool getNativeDesktopMode(SDL_DisplayID display, SDL_DisplayMode* mode, SDL_Rect* safeArea);
static
int getDisplayRefreshRate(SDL_Window* window);
static
bool hasFastAes();
static
int getDrmFdForWindow(SDL_Window* window, bool* needsClose);
static
int getDrmFd(bool preferRenderNode);
};
+1 -1
View File
@@ -1,7 +1,7 @@
#pragma once
#include <Limelight.h>
#include <SDL.h>
#include "SDL_compat.h"
#include "settings/streamingpreferences.h"
#define SDL_CODE_FRAME_READY 0
@@ -8,7 +8,6 @@
#include "streaming/streamutils.h"
#include "streaming/session.h"
#include <SDL_syswm.h>
#include <VersionHelpers.h>
#include <dwmapi.h>
@@ -428,23 +427,20 @@ bool D3D11VARenderer::initialize(PDECODER_PARAMETERS params)
// DXVA2 may let us take over for FSE V-sync off cases. However, if we don't have DXGI_FEATURE_PRESENT_ALLOW_TEARING
// then we should not attempt to do this unless there's no other option (HDR, DXVA2 failed in pass 1, etc).
if (!m_AllowTearing && m_DecoderSelectionPass == 0 && !(params->videoFormat & VIDEO_FORMAT_MASK_10BIT) &&
(SDL_GetWindowFlags(params->window) & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN) {
SDLC_IsFullscreenExclusive(params->window)) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Defaulting to DXVA2 for FSE without DXGI_FEATURE_PRESENT_ALLOW_TEARING support");
return false;
}
}
SDL_SysWMinfo info;
SDL_VERSION(&info.version);
SDL_GetWindowWMInfo(params->window, &info);
SDL_assert(info.subsystem == SDL_SYSWM_WINDOWS);
HWND window = (HWND)SDLC_Win32_GetHwnd(params->window);
// Always use windowed or borderless windowed mode.. SDL does mode-setting for us in
// full-screen exclusive mode (SDL_WINDOW_FULLSCREEN), so this actually works out okay.
// Always use windowed or borderless windowed mode. SDL does mode-setting for us in
// full-screen exclusive mode, so this actually works out okay.
ComPtr<IDXGISwapChain1> swapChain;
hr = m_Factory->CreateSwapChainForHwnd(m_Device.Get(),
info.info.win.window,
window,
&swapChainDesc,
nullptr,
nullptr,
@@ -468,7 +464,7 @@ bool D3D11VARenderer::initialize(PDECODER_PARAMETERS params)
// Disable Alt+Enter, PrintScreen, and window message snooping. This makes
// it safe to run the renderer on a separate rendering thread rather than
// requiring the main (message loop) thread.
hr = m_Factory->MakeWindowAssociation(info.info.win.window, DXGI_MWA_NO_WINDOW_CHANGES);
hr = m_Factory->MakeWindowAssociation(window, DXGI_MWA_NO_WINDOW_CHANGES);
if (FAILED(hr)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"IDXGIFactory::MakeWindowAssociation() failed: %x",
@@ -670,7 +666,7 @@ void D3D11VARenderer::renderFrame(AVFrame* frame)
// The card may have been removed or crashed. Reset the decoder.
SDL_Event event;
event.type = SDL_RENDER_TARGETS_RESET;
event.type = SDL_EVENT_RENDER_TARGETS_RESET;
SDL_PushEvent(&event);
return;
}
@@ -1188,7 +1184,7 @@ int D3D11VARenderer::getRendererAttributes()
// In windowed mode, we will render as fast we can and DWM will grab whatever is latest at the
// time unless the user opts for pacing. We will use pacing in full-screen mode and normal DWM
// sequencing in full-screen desktop mode to behave similarly to the DXVA2 renderer.
if ((SDL_GetWindowFlags(m_DecoderParams.window) & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN) {
if (SDLC_IsFullscreenExclusive(m_DecoderParams.window)) {
attributes |= RENDERER_ATTRIBUTE_FORCE_PACING;
}
+55 -78
View File
@@ -72,15 +72,6 @@ extern "C" {
#include <Limelight.h>
// HACK: Avoid including X11 headers which conflict with QDir
#ifdef SDL_VIDEO_DRIVER_X11
#undef SDL_VIDEO_DRIVER_X11
#endif
#include <SDL_syswm.h>
#include <QDir>
#include <map>
// This map is used to lookup characteristics of a given DRM format
@@ -149,7 +140,8 @@ DrmRenderer::DrmRenderer(AVHWDeviceType hwDeviceType, IFFmpegRenderer *backendRe
m_HwDeviceType(hwDeviceType),
m_HwContext(nullptr),
m_DrmFd(-1),
m_SdlOwnsDrmFd(false),
m_DrmIsMaster(false),
m_MustCloseDrmFd(false),
m_SupportsDirectRendering(false),
m_VideoFormat(0),
m_ConnectorId(0),
@@ -166,6 +158,7 @@ DrmRenderer::DrmRenderer(AVHWDeviceType hwDeviceType, IFFmpegRenderer *backendRe
m_ColorspaceProp(nullptr),
m_Version(nullptr),
m_HdrOutputMetadataBlobId(0),
m_OutputRect{},
m_SwFrameMapper(this),
m_CurrentSwFrameIdx(0)
#ifdef HAVE_EGL
@@ -232,7 +225,7 @@ DrmRenderer::~DrmRenderer()
av_buffer_unref(&m_HwContext);
}
if (!m_SdlOwnsDrmFd && m_DrmFd != -1) {
if (m_MustCloseDrmFd && m_DrmFd != -1) {
close(m_DrmFd);
}
}
@@ -264,11 +257,14 @@ bool DrmRenderer::prepareDecoderContext(AVCodecContext* context, AVDictionary**
void DrmRenderer::prepareToRender()
{
// Retake DRM master if we dropped it earlier
drmSetMaster(m_DrmFd);
// Create a dummy renderer to force SDL to complete the modesetting
// operation that the KMSDRM backend keeps pending until the next
// time we swap buffers. We have to do this before we enumerate
// CRTC modes below.
SDL_Renderer* renderer = SDL_CreateRenderer(m_Window, -1, SDL_RENDERER_SOFTWARE);
SDL_Renderer* renderer = SDL_CreateRenderer(m_Window, SDLC_DEFAULT_RENDER_DRIVER, SDL_RENDERER_SOFTWARE);
if (renderer != nullptr) {
// SDL_CreateRenderer() can end up having to recreate our window (SDL_RecreateWindow())
// to ensure it's compatible with the renderer's OpenGL context. If that happens, we
@@ -284,7 +280,7 @@ void DrmRenderer::prepareToRender()
else {
// If we get here prior to the start of a session, just pump and flush ourselves.
SDL_PumpEvents();
SDL_FlushEvent(SDL_WINDOWEVENT);
SDLC_FlushWindowEvents();
}
SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
@@ -297,6 +293,30 @@ void DrmRenderer::prepareToRender()
"SDL_CreateRenderer() failed: %s",
SDL_GetError());
}
// Set the output rect to match the new CRTC size after modesetting
m_OutputRect.x = m_OutputRect.y = 0;
drmModeCrtc* crtc = drmModeGetCrtc(m_DrmFd, m_CrtcId);
if (crtc != nullptr) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"CRTC size after modesetting: %ux%u",
crtc->width,
crtc->height);
m_OutputRect.w = crtc->width;
m_OutputRect.h = crtc->height;
drmModeFreeCrtc(crtc);
}
else {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"drmModeGetCrtc() failed: %d",
errno);
SDL_GetWindowSize(m_Window, &m_OutputRect.w, &m_OutputRect.h);
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Guessing CRTC is window size: %dx%d",
m_OutputRect.w,
m_OutputRect.h);
}
}
bool DrmRenderer::getPropertyByName(drmModeObjectPropertiesPtr props, const char* name, uint64_t *value) {
@@ -325,68 +345,28 @@ bool DrmRenderer::initialize(PDECODER_PARAMETERS params)
m_VideoFormat = params->videoFormat;
m_SwFrameMapper.setVideoFormat(params->videoFormat);
#if SDL_VERSION_ATLEAST(2, 0, 15)
SDL_SysWMinfo info;
// Try to get the FD that we're sharing with SDL
m_DrmFd = StreamUtils::getDrmFdForWindow(m_Window, &m_MustCloseDrmFd);
if (m_DrmFd >= 0) {
// If we got a DRM FD for the window, we can render to it
m_DrmIsMaster = true;
SDL_VERSION(&info.version);
if (!SDL_GetWindowWMInfo(params->window, &info)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_GetWindowWMInfo() failed: %s",
SDL_GetError());
return false;
}
if (info.subsystem == SDL_SYSWM_KMSDRM) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Sharing DRM FD with SDL");
SDL_assert(info.info.kmsdrm.drm_fd >= 0);
m_DrmFd = info.info.kmsdrm.drm_fd;
m_SdlOwnsDrmFd = true;
}
else
#endif
{
const char* userDevice = SDL_getenv("DRM_DEV");
if (userDevice != nullptr) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Opening user-specified DRM device: %s",
userDevice);
m_DrmFd = open(userDevice, O_RDWR | O_CLOEXEC);
// If we just opened a new FD, let's drop master on it
// so SDL can take master for Vulkan rendering. We'll
// regrab master later if we end up direct rendering.
if (m_MustCloseDrmFd) {
drmDropMaster(m_DrmFd);
}
else {
QDir driDir("/dev/dri");
}
else {
// Try to open any DRM render node
m_DrmFd = StreamUtils::getDrmFd(true);
if (m_DrmFd >= 0) {
// Drop master in case we somehow got a primary node
drmDropMaster(m_DrmFd);
// We have to explicitly ask for devices to be returned
driDir.setFilter(QDir::Files | QDir::System);
// Try a render node first since we aren't using DRM for output in this codepath
for (QFileInfo& node : driDir.entryInfoList(QStringList("renderD*"))) {
QByteArray absolutePath = node.absoluteFilePath().toUtf8();
m_DrmFd = open(absolutePath.constData(), O_RDWR | O_CLOEXEC);
if (m_DrmFd >= 0) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Opened DRM render node: %s",
absolutePath.constData());
break;
}
}
// If that fails, try to use a primary node and hope for the best
if (m_DrmFd < 0) {
for (QFileInfo& node : driDir.entryInfoList(QStringList("card*"))) {
QByteArray absolutePath = node.absoluteFilePath().toUtf8();
m_DrmFd = open(absolutePath.constData(), O_RDWR | O_CLOEXEC);
if (m_DrmFd >= 0) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Opened DRM primary node: %s",
absolutePath.constData());
break;
}
}
}
// This is a new FD that we must close
m_MustCloseDrmFd = true;
}
}
@@ -464,7 +444,7 @@ bool DrmRenderer::initialize(PDECODER_PARAMETERS params)
// If we're not sharing the DRM FD with SDL, that means we don't
// have DRM master, so we can't call drmModeSetPlane(). We can
// use EGLRenderer or SDLRenderer to render in this situation.
if (!m_SdlOwnsDrmFd) {
if (!m_DrmIsMaster) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Direct rendering via DRM is disabled");
return DIRECT_RENDERING_INIT_FAILED;
@@ -523,12 +503,7 @@ bool DrmRenderer::initialize(PDECODER_PARAMETERS params)
int crtcIndex = -1;
for (int i = 0; i < resources->count_crtcs; i++) {
if (resources->crtcs[i] == m_CrtcId) {
drmModeCrtc* crtc = drmModeGetCrtc(m_DrmFd, resources->crtcs[i]);
crtcIndex = i;
m_OutputRect.x = m_OutputRect.y = 0;
m_OutputRect.w = crtc->width;
m_OutputRect.h = crtc->height;
drmModeFreeCrtc(crtc);
break;
}
}
@@ -1240,6 +1215,8 @@ void DrmRenderer::renderFrame(AVFrame* frame)
int err;
SDL_Rect src, dst;
SDL_assert(m_OutputRect.w > 0 && m_OutputRect.h > 0);
src.x = src.y = 0;
src.w = frame->width;
src.h = frame->height;
+2 -1
View File
@@ -86,7 +86,8 @@ private:
AVHWDeviceType m_HwDeviceType;
AVBufferRef* m_HwContext;
int m_DrmFd;
bool m_SdlOwnsDrmFd;
bool m_DrmIsMaster;
bool m_MustCloseDrmFd;
bool m_SupportsDirectRendering;
int m_VideoFormat;
uint32_t m_ConnectorId;
+7 -15
View File
@@ -9,8 +9,6 @@
#include <streaming/streamutils.h>
#include <streaming/session.h>
#include <SDL_syswm.h>
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <VersionHelpers.h>
@@ -541,11 +539,6 @@ bool DXVA2Renderer::isDecoderBlacklisted()
bool DXVA2Renderer::initializeDevice(SDL_Window* window, bool enableVsync)
{
SDL_SysWMinfo info;
SDL_VERSION(&info.version);
SDL_GetWindowWMInfo(window, &info);
ComPtr<IDirect3D9Ex> d3d9ex;
HRESULT hr = Direct3DCreate9Ex(D3D_SDK_VERSION, &d3d9ex);
if (FAILED(hr)) {
@@ -556,7 +549,6 @@ bool DXVA2Renderer::initializeDevice(SDL_Window* window, bool enableVsync)
}
int adapterIndex = SDL_Direct3D9GetAdapterIndex(SDL_GetWindowDisplayIndex(window));
Uint32 windowFlags = SDL_GetWindowFlags(window);
// Initialize quirks *before* calling CreateDeviceEx() to allow our below
// logic to avoid a hang with NahimicOSD.dll's broken full-screen handling.
@@ -572,7 +564,7 @@ bool DXVA2Renderer::initializeDevice(SDL_Window* window, bool enableVsync)
d3d9ex->GetAdapterDisplayModeEx(adapterIndex, &currentMode, nullptr);
D3DPRESENT_PARAMETERS d3dpp = {};
d3dpp.hDeviceWindow = info.info.win.window;
d3dpp.hDeviceWindow = (HWND)SDLC_Win32_GetHwnd(window);
d3dpp.Flags = D3DPRESENTFLAG_VIDEO;
if (m_VideoFormat & VIDEO_FORMAT_MASK_10BIT) {
@@ -590,7 +582,7 @@ bool DXVA2Renderer::initializeDevice(SDL_Window* window, bool enableVsync)
}
}
if ((windowFlags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN) {
if (SDLC_IsFullscreenExclusive(window)) {
d3dpp.Windowed = false;
d3dpp.BackBufferWidth = currentMode.Width;
d3dpp.BackBufferHeight = currentMode.Height;
@@ -1120,7 +1112,7 @@ void DXVA2Renderer::renderFrame(AVFrame *frame)
"Clear() failed: %x",
hr);
SDL_Event event;
event.type = SDL_RENDER_TARGETS_RESET;
event.type = SDL_EVENT_RENDER_TARGETS_RESET;
SDL_PushEvent(&event);
return;
}
@@ -1131,7 +1123,7 @@ void DXVA2Renderer::renderFrame(AVFrame *frame)
"BeginScene() failed: %x",
hr);
SDL_Event event;
event.type = SDL_RENDER_TARGETS_RESET;
event.type = SDL_EVENT_RENDER_TARGETS_RESET;
SDL_PushEvent(&event);
return;
}
@@ -1157,7 +1149,7 @@ void DXVA2Renderer::renderFrame(AVFrame *frame)
"StretchRect() failed: %x",
hr);
SDL_Event event;
event.type = SDL_RENDER_TARGETS_RESET;
event.type = SDL_EVENT_RENDER_TARGETS_RESET;
SDL_PushEvent(&event);
return;
}
@@ -1174,7 +1166,7 @@ void DXVA2Renderer::renderFrame(AVFrame *frame)
"EndScene() failed: %x",
hr);
SDL_Event event;
event.type = SDL_RENDER_TARGETS_RESET;
event.type = SDL_EVENT_RENDER_TARGETS_RESET;
SDL_PushEvent(&event);
return;
}
@@ -1192,7 +1184,7 @@ void DXVA2Renderer::renderFrame(AVFrame *frame)
"PresentEx() failed: %x",
hr);
SDL_Event event;
event.type = SDL_RENDER_TARGETS_RESET;
event.type = SDL_EVENT_RENDER_TARGETS_RESET;
SDL_PushEvent(&event);
return;
}
@@ -1,5 +1,7 @@
#include "eglimagefactory.h"
#include <vector>
// Don't take a dependency on libdrm just for these constants
#ifndef DRM_FORMAT_MOD_INVALID
#define DRM_FORMAT_MOD_INVALID ((1ULL << 56) - 1)
@@ -449,8 +451,8 @@ bool EglImageFactory::supportsImportingFormat(EGLDisplay dpy, EGLint format)
return false;
}
EGLint formats[numFormats];
if (!m_eglQueryDmaBufFormatsEXT(dpy, numFormats, formats, &numFormats)) {
std::vector<EGLint> formats(numFormats);
if (!m_eglQueryDmaBufFormatsEXT(dpy, numFormats, formats.data(), &numFormats)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"eglQueryDmaBufFormatsEXT() #2 failed: %d", eglGetError());
return false;
@@ -491,8 +493,8 @@ bool EglImageFactory::supportsImportingModifier(EGLDisplay dpy, EGLint format, E
return false;
}
EGLuint64KHR modifiers[numModifiers];
if (!m_eglQueryDmaBufModifiersEXT(dpy, format, numModifiers, modifiers, nullptr, &numModifiers)) {
std::vector<EGLuint64KHR> modifiers(numModifiers);
if (!m_eglQueryDmaBufModifiersEXT(dpy, format, numModifiers, modifiers.data(), nullptr, &numModifiers)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"eglQueryDmaBufModifiersEXT() #2 failed: %d", eglGetError());
return false;
+19 -26
View File
@@ -11,7 +11,6 @@
#include <unistd.h>
#include <SDL_render.h>
#include <SDL_syswm.h>
// These are extensions, so some platform headers may not provide them
#ifndef EGL_PLATFORM_WAYLAND_KHR
@@ -163,7 +162,7 @@ void EGLRenderer::notifyOverlayUpdated(Overlay::OverlayType type)
if (!Session::get()->getOverlayManager().isOverlayEnabled(type)) {
// If the overlay has been disabled, mark the data as invalid/stale.
SDL_AtomicSet(&m_OverlayHasValidData[type], 0);
SDL_SetAtomicInt(&m_OverlayHasValidData[type], 0);
return;
}
}
@@ -212,7 +211,7 @@ void EGLRenderer::renderOverlay(Overlay::OverlayType type, int viewportWidth, in
// we must allocate a tightly packed buffer and copy our pixels there.
packedPixelData = malloc(newSurface->w * newSurface->h * newSurface->format->BytesPerPixel);
if (!packedPixelData) {
SDL_FreeSurface(newSurface);
SDL_DestroySurface(newSurface);
return;
}
@@ -248,7 +247,7 @@ void EGLRenderer::renderOverlay(Overlay::OverlayType type, int viewportWidth, in
overlayRect.w = newSurface->w;
overlayRect.h = newSurface->h;
SDL_FreeSurface(newSurface);
SDL_DestroySurface(newSurface);
// Convert screen space to normalized device coordinates
StreamUtils::screenSpaceToNormalizedDeviceCoords(&overlayRect, viewportWidth, viewportHeight);
@@ -266,10 +265,10 @@ void EGLRenderer::renderOverlay(Overlay::OverlayType type, int viewportWidth, in
glBindBuffer(GL_ARRAY_BUFFER, m_OverlayVbos[type]);
glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW);
SDL_AtomicSet(&m_OverlayHasValidData[type], 1);
SDL_SetAtomicInt(&m_OverlayHasValidData[type], 1);
}
if (!SDL_AtomicGet(&m_OverlayHasValidData[type])) {
if (!SDL_GetAtomicInt(&m_OverlayHasValidData[type])) {
// If the overlay is not populated yet or is stale, don't render it.
return;
}
@@ -436,6 +435,9 @@ bool EGLRenderer::initialize(PDECODER_PARAMETERS params)
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
#if SDL_VERSION_ATLEAST(3, 0, 0)
m_DummyRenderer = SDL_CreateRenderer(m_Window, "opengles2", SDL_RENDERER_ACCELERATED);
#else
int renderIndex;
int maxRenderers = SDL_GetNumRenderDrivers();
SDL_assert(maxRenderers >= 0);
@@ -455,6 +457,7 @@ bool EGLRenderer::initialize(PDECODER_PARAMETERS params)
}
m_DummyRenderer = SDL_CreateRenderer(m_Window, renderIndex, SDL_RENDERER_ACCELERATED);
#endif
if (!m_DummyRenderer) {
// Print the error here (before it gets clobbered), but ensure that we flush window
// events just in case SDL re-created the window before eventually failing.
@@ -475,7 +478,7 @@ bool EGLRenderer::initialize(PDECODER_PARAMETERS params)
else {
// If we get here prior to the start of a session, just pump and flush ourselves.
SDL_PumpEvents();
SDL_FlushEvent(SDL_WINDOWEVENT);
SDLC_FlushWindowEvents();
}
// Now we finally bail if we failed during SDL_CreateRenderer() above.
@@ -485,12 +488,7 @@ bool EGLRenderer::initialize(PDECODER_PARAMETERS params)
return false;
}
SDL_SysWMinfo info;
SDL_VERSION(&info.version);
if (!SDL_GetWindowWMInfo(params->window, &info)) {
EGL_LOG(Error, "SDL_GetWindowWMInfo() failed: %s", SDL_GetError());
return false;
}
SDLC_VideoDriver videoDriver = SDLC_GetVideoDriver();
if (!(m_Context = SDL_GL_CreateContext(params->window))) {
EGL_LOG(Error, "Cannot create OpenGL context: %s", SDL_GetError());
@@ -597,21 +595,14 @@ bool EGLRenderer::initialize(PDECODER_PARAMETERS params)
// the Wayland viewport can be stale when using Super+Left/Right/Up
// to resize the window. This seems to happen significantly more often
// with vsync enabled, so this also mitigates that problem too.
if (params->enableVsync
#ifdef SDL_VIDEO_DRIVER_WAYLAND
&& info.subsystem != SDL_SYSWM_WAYLAND
#endif
) {
if (params->enableVsync && videoDriver != SDLC_VIDEO_WAYLAND) {
SDL_GL_SetSwapInterval(1);
#if SDL_VERSION_ATLEAST(2, 0, 15) && defined(SDL_VIDEO_DRIVER_KMSDRM)
// The SDL KMSDRM backend already enforces double buffering (due to
// SDL_HINT_VIDEO_DOUBLE_BUFFER=1), so calling glFinish() after
// SDL_GL_SwapWindow() will block an extra frame and lock rendering
// at 1/2 the display refresh rate.
if (info.subsystem != SDL_SYSWM_KMSDRM)
#endif
{
if (videoDriver != SDLC_VIDEO_KMSDRM) {
m_BlockingSwapBuffers = true;
}
} else {
@@ -647,14 +638,12 @@ bool EGLRenderer::initialize(PDECODER_PARAMETERS params)
// Detach the context from this thread, so the render thread can attach it
SDL_GL_MakeCurrent(m_Window, nullptr);
#ifdef SDL_HINT_VIDEO_X11_FORCE_EGL
if (err == GL_NO_ERROR) {
// If we got a working GL implementation via EGL, avoid using GLX from now on.
// GLX will cause problems if we later want to use EGL again on this window.
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "EGL passed preflight checks. Using EGL for GL context creation.");
SDL_SetHint(SDL_HINT_VIDEO_X11_FORCE_EGL, "1");
SDL_SetHint(SDL_HINT_VIDEO_FORCE_EGL, "1");
}
#endif
return err == GL_NO_ERROR;
}
@@ -837,7 +826,7 @@ void EGLRenderer::renderFrame(AVFrame* frame)
// XWayland. Other strategies like calling glGetError() don't seem
// to be able to detect this situation for some reason.
SDL_Event event;
event.type = SDL_RENDER_TARGETS_RESET;
event.type = SDL_EVENT_RENDER_TARGETS_RESET;
SDL_PushEvent(&event);
return;
@@ -856,7 +845,11 @@ void EGLRenderer::renderFrame(AVFrame* frame)
glClear(GL_COLOR_BUFFER_BIT);
int drawableWidth, drawableHeight;
#if SDL_VERSION_ATLEAST(3, 0, 0)
SDL_GetWindowSizeInPixels(m_Window, &drawableWidth, &drawableHeight);
#else
SDL_GL_GetDrawableSize(m_Window, &drawableWidth, &drawableHeight);
#endif
// Set the viewport to the size of the aspect-ratio-scaled video
SDL_Rect src, dst;
@@ -5,13 +5,6 @@
#include <Limelight.h>
// HACK: Avoid including X11 headers which conflict with QDir
#ifdef SDL_VIDEO_DRIVER_X11
#undef SDL_VIDEO_DRIVER_X11
#endif
#include <SDL_syswm.h>
#include <QDir>
#include <QTextStream>
@@ -63,7 +56,7 @@ void MmalRenderer::prepareToRender()
{
// Create a renderer and draw a black background for the area not covered by the MMAL overlay.
// On the KMSDRM backend, this triggers the modeset that puts the CRTC into the mode we selected.
m_BackgroundRenderer = SDL_CreateRenderer(m_Window, -1, SDL_RENDERER_SOFTWARE);
m_BackgroundRenderer = SDL_CreateRenderer(m_Window, SDLC_DEFAULT_RENDER_DRIVER, SDL_RENDERER_SOFTWARE);
if (m_BackgroundRenderer == nullptr) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_CreateRenderer() failed: %s",
@@ -85,7 +78,7 @@ void MmalRenderer::prepareToRender()
else {
// If we get here prior to the start of a session, just pump and flush ourselves.
SDL_PumpEvents();
SDL_FlushEvent(SDL_WINDOWEVENT);
SDLC_FlushWindowEvents();
}
SDL_SetRenderDrawColor(m_BackgroundRenderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
@@ -47,21 +47,7 @@ bool DxVsyncSource::initialize(SDL_Window* window, int)
return false;
}
SDL_SysWMinfo info;
SDL_VERSION(&info.version);
if (!SDL_GetWindowWMInfo(window, &info)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_GetWindowWMInfo() failed: %s",
SDL_GetError());
return false;
}
// Pacer should only create us on Win32
SDL_assert(info.subsystem == SDL_SYSWM_WINDOWS);
m_Window = info.info.win.window;
m_Window = (HWND)SDLC_Win32_GetHwnd(window);
return true;
}
@@ -2,7 +2,8 @@
#include "pacer.h"
#include <SDL_syswm.h>
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
// from <D3dkmthk.h>
typedef LONG NTSTATUS;
@@ -2,18 +2,14 @@
#include "streaming/streamutils.h"
#ifdef Q_OS_WIN32
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <VersionHelpers.h>
#include "dxvsyncsource.h"
#include <VersionHelpers.h>
#endif
#ifdef HAS_WAYLAND
#include "waylandvsyncsource.h"
#endif
#include <SDL_syswm.h>
// Limit the number of queued frames to prevent excessive memory consumption
// if the V-Sync source or renderer is blocked for a while. It's important
// that the sum of all queued frames between both pacing and rendering queues
@@ -102,11 +98,7 @@ int Pacer::vsyncThread(void *context)
{
Pacer* me = reinterpret_cast<Pacer*>(context);
#if SDL_VERSION_ATLEAST(2, 0, 9)
SDL_SetThreadPriority(SDL_THREAD_PRIORITY_TIME_CRITICAL);
#else
SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH);
#endif
SDL_SetCurrentThreadPriority(SDL_THREAD_PRIORITY_TIME_CRITICAL);
bool async = me->m_VsyncSource->isAsync();
while (!me->m_Stopping) {
@@ -135,7 +127,7 @@ int Pacer::renderThread(void* context)
{
Pacer* me = reinterpret_cast<Pacer*>(context);
if (SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH) < 0) {
if (!SDL_SetCurrentThreadPriority(SDL_THREAD_PRIORITY_HIGH)) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Unable to set render thread to high priority: %s",
SDL_GetError());
@@ -187,7 +179,7 @@ void Pacer::enqueueFrameForRenderingAndUnlock(AVFrame *frame)
SDL_Event event;
// For main thread rendering, we'll push an event to trigger a callback
event.type = SDL_USEREVENT;
event.type = SDL_EVENT_USER;
event.user.code = SDL_CODE_FRAME_READY;
SDL_PushEvent(&event);
}
@@ -267,31 +259,22 @@ bool Pacer::initialize(SDL_Window* window, int maxVideoFps, bool enablePacing)
"Frame pacing: target %d Hz with %d FPS stream",
m_DisplayFps, m_MaxVideoFps);
SDL_SysWMinfo info;
SDL_VERSION(&info.version);
if (!SDL_GetWindowWMInfo(window, &info)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_GetWindowWMInfo() failed: %s",
SDL_GetError());
return false;
}
switch (info.subsystem) {
#ifdef Q_OS_WIN32
case SDL_SYSWM_WINDOWS:
switch (SDLC_GetVideoDriver()) {
case SDLC_VIDEO_WIN32:
#ifdef Q_OS_WIN32
// Don't use D3DKMTWaitForVerticalBlankEvent() on Windows 7, because
// it blocks during other concurrent DX operations (like actually rendering).
if (IsWindows8OrGreater()) {
m_VsyncSource = new DxVsyncSource(this);
}
#endif
break;
#endif
#if defined(SDL_VIDEO_DRIVER_WAYLAND) && defined(HAS_WAYLAND)
case SDL_SYSWM_WAYLAND:
case SDLC_VIDEO_WAYLAND:
#if defined(SDL_VIDEO_DRIVER_WAYLAND) && defined(HAS_WAYLAND)
m_VsyncSource = new WaylandVsyncSource(this);
#endif
break;
#endif
default:
// Platforms without a VsyncSource will just render frames
@@ -1,7 +1,5 @@
#include "waylandvsyncsource.h"
#include <SDL_syswm.h>
#ifndef SDL_VIDEO_DRIVER_WAYLAND
#warning Unable to use WaylandVsyncSource without SDL support
#else
@@ -29,22 +27,8 @@ WaylandVsyncSource::~WaylandVsyncSource()
bool WaylandVsyncSource::initialize(SDL_Window* window, int)
{
SDL_SysWMinfo info;
SDL_VERSION(&info.version);
if (!SDL_GetWindowWMInfo(window, &info)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_GetWindowWMInfo() failed: %s",
SDL_GetError());
return false;
}
// Pacer should not create us for non-Wayland windows
SDL_assert(info.subsystem == SDL_SYSWM_WAYLAND);
m_Display = info.info.wl.display;
m_Surface = info.info.wl.surface;
m_Display = (wl_display*)SDLC_Wayland_GetDisplay(window);
m_Surface = (wl_surface*)SDLC_Wayland_GetSurface(window);
// Enqueue our first frame callback
m_Callback = wl_surface_frame(m_Surface);
+107 -44
View File
@@ -14,8 +14,9 @@
#include <vector>
#include <set>
#ifndef VK_KHR_VIDEO_DECODE_AV1_EXTENSION_NAME
#ifndef VK_KHR_video_decode_av1
#define VK_KHR_VIDEO_DECODE_AV1_EXTENSION_NAME "VK_KHR_video_decode_av1"
#define VK_VIDEO_CODEC_OPERATION_DECODE_AV1_BIT_KHR ((VkVideoCodecOperationFlagBitsKHR)0x00000004)
#endif
// Keep these in sync with hwcontext_vulkan.c
@@ -94,7 +95,7 @@ void PlVkRenderer::unlockQueue(struct AVHWDeviceContext *dev_ctx, uint32_t queue
void PlVkRenderer::overlayUploadComplete(void* opaque)
{
SDL_FreeSurface((SDL_Surface*)opaque);
SDL_DestroySurface((SDL_Surface*)opaque);
}
PlVkRenderer::PlVkRenderer(bool hwaccel, IFFmpegRenderer *backendRenderer) :
@@ -317,7 +318,7 @@ bool PlVkRenderer::tryInitializeDevice(VkPhysicalDevice device, VkPhysicalDevice
vkParams.device = device;
vkParams.opt_extensions = k_OptionalDeviceExtensions;
vkParams.num_opt_extensions = SDL_arraysize(k_OptionalDeviceExtensions);
vkParams.extra_queues = VK_QUEUE_VIDEO_DECODE_BIT_KHR;
vkParams.extra_queues = m_HwAccelBackend ? VK_QUEUE_FLAG_BITS_MAX_ENUM : 0;
m_Vulkan = pl_vulkan_create(m_Log, &vkParams);
if (m_Vulkan == nullptr) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
@@ -361,7 +362,11 @@ bool PlVkRenderer::initialize(PDECODER_PARAMETERS params)
m_Window = params->window;
unsigned int instanceExtensionCount = 0;
#if SDL_VERSION_ATLEAST(3, 0, 0)
if (!SDL_Vulkan_GetInstanceExtensions(&instanceExtensionCount, nullptr)) {
#else
if (!SDL_Vulkan_GetInstanceExtensions(params->window, &instanceExtensionCount, nullptr)) {
#endif
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_Vulkan_GetInstanceExtensions() #1 failed: %s",
SDL_GetError());
@@ -369,7 +374,11 @@ bool PlVkRenderer::initialize(PDECODER_PARAMETERS params)
}
std::vector<const char*> instanceExtensions(instanceExtensionCount);
#if SDL_VERSION_ATLEAST(3, 0, 0)
if (!SDL_Vulkan_GetInstanceExtensions(&instanceExtensionCount, instanceExtensions.data())) {
#else
if (!SDL_Vulkan_GetInstanceExtensions(params->window, &instanceExtensionCount, instanceExtensions.data())) {
#endif
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_Vulkan_GetInstanceExtensions() #2 failed: %s",
SDL_GetError());
@@ -378,14 +387,8 @@ bool PlVkRenderer::initialize(PDECODER_PARAMETERS params)
pl_vk_inst_params vkInstParams = pl_vk_inst_default_params;
{
bool ok;
vkInstParams.debug_extra = !!qEnvironmentVariableIntValue("PLVK_DEBUG_EXTRA", &ok);
vkInstParams.debug = vkInstParams.debug_extra || !!qEnvironmentVariableIntValue("PLVK_DEBUG", &ok);
#ifdef QT_DEBUG
if (!ok) {
vkInstParams.debug = true;
}
#endif
vkInstParams.debug_extra = !!qEnvironmentVariableIntValue("PLVK_DEBUG_EXTRA");
vkInstParams.debug = vkInstParams.debug_extra || !!qEnvironmentVariableIntValue("PLVK_DEBUG");
}
vkInstParams.get_proc_addr = (PFN_vkGetInstanceProcAddr)SDL_Vulkan_GetVkGetInstanceProcAddr();
vkInstParams.extensions = instanceExtensions.data();
@@ -399,7 +402,7 @@ bool PlVkRenderer::initialize(PDECODER_PARAMETERS params)
// Lookup all Vulkan functions we require
POPULATE_FUNCTION(vkDestroySurfaceKHR);
POPULATE_FUNCTION(vkGetPhysicalDeviceQueueFamilyProperties);
POPULATE_FUNCTION(vkGetPhysicalDeviceQueueFamilyProperties2);
POPULATE_FUNCTION(vkGetPhysicalDeviceSurfacePresentModesKHR);
POPULATE_FUNCTION(vkGetPhysicalDeviceSurfaceFormatsKHR);
POPULATE_FUNCTION(vkEnumeratePhysicalDevices);
@@ -407,7 +410,11 @@ bool PlVkRenderer::initialize(PDECODER_PARAMETERS params)
POPULATE_FUNCTION(vkGetPhysicalDeviceSurfaceSupportKHR);
POPULATE_FUNCTION(vkEnumerateDeviceExtensionProperties);
#if SDL_VERSION_ATLEAST(3, 0, 0)
if (!SDL_Vulkan_CreateSurface(params->window, m_PlVkInstance->instance, NULL, &m_VkSurface)) {
#else
if (!SDL_Vulkan_CreateSurface(params->window, m_PlVkInstance->instance, &m_VkSurface)) {
#endif
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_Vulkan_CreateSurface() failed: %s",
SDL_GetError());
@@ -504,24 +511,13 @@ bool PlVkRenderer::initialize(PDECODER_PARAMETERS params)
vkDeviceContext->nb_enabled_inst_extensions = m_PlVkInstance->num_extensions;
vkDeviceContext->enabled_dev_extensions = m_Vulkan->extensions;
vkDeviceContext->nb_enabled_dev_extensions = m_Vulkan->num_extensions;
vkDeviceContext->queue_family_index = m_Vulkan->queue_graphics.index;
vkDeviceContext->nb_graphics_queues = m_Vulkan->queue_graphics.count;
vkDeviceContext->queue_family_tx_index = m_Vulkan->queue_transfer.index;
vkDeviceContext->nb_tx_queues = m_Vulkan->queue_transfer.count;
vkDeviceContext->queue_family_comp_index = m_Vulkan->queue_compute.index;
vkDeviceContext->nb_comp_queues = m_Vulkan->queue_compute.count;
#if LIBAVUTIL_VERSION_INT > AV_VERSION_INT(58, 9, 100)
vkDeviceContext->lock_queue = lockQueue;
vkDeviceContext->unlock_queue = unlockQueue;
#endif
static_assert(sizeof(vkDeviceContext->queue_family_decode_index) == sizeof(uint32_t), "sizeof(int) != sizeof(uint32_t)");
static_assert(sizeof(vkDeviceContext->nb_decode_queues) == sizeof(uint32_t), "sizeof(int) != sizeof(uint32_t)");
if (!getQueue(VK_QUEUE_VIDEO_DECODE_BIT_KHR, (uint32_t*)&vkDeviceContext->queue_family_decode_index, (uint32_t*)&vkDeviceContext->nb_decode_queues)) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Vulkan video decoding is not supported by the Vulkan device");
return false;
}
// Populate the device queues for decoding this video format
populateQueues(params->videoFormat);
int err = av_hwdevice_ctx_init(m_HwDeviceCtx);
if (err < 0) {
@@ -581,23 +577,86 @@ bool PlVkRenderer::mapAvFrameToPlacebo(const AVFrame *frame, pl_frame* mappedFra
return true;
}
bool PlVkRenderer::getQueue(VkQueueFlags requiredFlags, uint32_t *queueIndex, uint32_t *queueCount)
bool PlVkRenderer::populateQueues(int videoFormat)
{
uint32_t queueFamilyCount = 0;
fn_vkGetPhysicalDeviceQueueFamilyProperties(m_Vulkan->phys_device, &queueFamilyCount, nullptr);
auto vkDeviceContext = (AVVulkanDeviceContext*)((AVHWDeviceContext *)m_HwDeviceCtx->data)->hwctx;
std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
fn_vkGetPhysicalDeviceQueueFamilyProperties(m_Vulkan->phys_device, &queueFamilyCount, queueFamilies.data());
uint32_t queueFamilyCount = 0;
fn_vkGetPhysicalDeviceQueueFamilyProperties2(m_Vulkan->phys_device, &queueFamilyCount, nullptr);
std::vector<VkQueueFamilyProperties2> queueFamilies(queueFamilyCount);
std::vector<VkQueueFamilyVideoPropertiesKHR> queueFamilyVideoProps(queueFamilyCount);
for (uint32_t i = 0; i < queueFamilyCount; i++) {
queueFamilyVideoProps[i].sType = VK_STRUCTURE_TYPE_QUEUE_FAMILY_VIDEO_PROPERTIES_KHR;
queueFamilies[i].sType = VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2;
queueFamilies[i].pNext = &queueFamilyVideoProps[i];
}
fn_vkGetPhysicalDeviceQueueFamilyProperties2(m_Vulkan->phys_device, &queueFamilyCount, queueFamilies.data());
#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(59, 34, 100)
Q_UNUSED(videoFormat);
for (uint32_t i = 0; i < queueFamilyCount; i++) {
if ((queueFamilies[i].queueFlags & requiredFlags) == requiredFlags) {
*queueIndex = i;
*queueCount = queueFamilies[i].queueCount;
return true;
vkDeviceContext->qf[i].idx = i;
vkDeviceContext->qf[i].num = queueFamilies[i].queueFamilyProperties.queueCount;
vkDeviceContext->qf[i].flags = (VkQueueFlagBits)queueFamilies[i].queueFamilyProperties.queueFlags;
vkDeviceContext->qf[i].video_caps = (VkVideoCodecOperationFlagBitsKHR)queueFamilyVideoProps[i].videoCodecOperations;
}
vkDeviceContext->nb_qf = queueFamilyCount;
#else
vkDeviceContext->queue_family_index = m_Vulkan->queue_graphics.index;
vkDeviceContext->nb_graphics_queues = m_Vulkan->queue_graphics.count;
vkDeviceContext->queue_family_tx_index = m_Vulkan->queue_transfer.index;
vkDeviceContext->nb_tx_queues = m_Vulkan->queue_transfer.count;
vkDeviceContext->queue_family_comp_index = m_Vulkan->queue_compute.index;
vkDeviceContext->nb_comp_queues = m_Vulkan->queue_compute.count;
// Select a video decode queue that is capable of decoding our chosen format
for (uint32_t i = 0; i < queueFamilyCount; i++) {
if (queueFamilies[i].queueFamilyProperties.queueFlags & VK_QUEUE_VIDEO_DECODE_BIT_KHR) {
if (videoFormat & VIDEO_FORMAT_MASK_H264) {
if (queueFamilyVideoProps[i].videoCodecOperations & VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR) {
vkDeviceContext->queue_family_decode_index = i;
vkDeviceContext->nb_decode_queues = queueFamilies[i].queueFamilyProperties.queueCount;
break;
}
}
else if (videoFormat & VIDEO_FORMAT_MASK_H265) {
if (queueFamilyVideoProps[i].videoCodecOperations & VK_VIDEO_CODEC_OPERATION_DECODE_H265_BIT_KHR) {
vkDeviceContext->queue_family_decode_index = i;
vkDeviceContext->nb_decode_queues = queueFamilies[i].queueFamilyProperties.queueCount;
break;
}
}
else if (videoFormat & VIDEO_FORMAT_MASK_AV1) {
#if LIBAVCODEC_VERSION_MAJOR >= 61
// VK_KHR_video_decode_av1 added VK_VIDEO_CODEC_OPERATION_DECODE_AV1_BIT_KHR to check for AV1
// decoding support on this queue. Since FFmpeg 6.1 used the older Mesa-specific AV1 extension,
// we'll just assume all video decode queues on this device support AV1 (since we checked that
// the physical device supports it earlier.
if (queueFamilyVideoProps[i].videoCodecOperations & VK_VIDEO_CODEC_OPERATION_DECODE_AV1_BIT_KHR)
#endif
{
vkDeviceContext->queue_family_decode_index = i;
vkDeviceContext->nb_decode_queues = queueFamilies[i].queueFamilyProperties.queueCount;
break;
}
}
else {
SDL_assert(false);
}
}
}
return false;
if (vkDeviceContext->queue_family_decode_index < 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Unable to find compatible video decode queue!");
return false;
}
#endif
return true;
}
bool PlVkRenderer::isPresentModeSupportedByPhysicalDevice(VkPhysicalDevice device, VkPresentModeKHR presentMode)
@@ -637,7 +696,7 @@ bool PlVkRenderer::isColorSpaceSupportedByPhysicalDevice(VkPhysicalDevice device
bool PlVkRenderer::isSurfacePresentationSupportedByPhysicalDevice(VkPhysicalDevice device)
{
uint32_t queueFamilyCount = 0;
fn_vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr);
fn_vkGetPhysicalDeviceQueueFamilyProperties2(device, &queueFamilyCount, nullptr);
for (uint32_t i = 0; i < queueFamilyCount; i++) {
VkBool32 supported = VK_FALSE;
@@ -656,7 +715,7 @@ void PlVkRenderer::waitToRender()
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"GPU is in failed state. Recreating renderer.");
SDL_Event event;
event.type = SDL_RENDER_DEVICE_RESET;
event.type = SDL_EVENT_RENDER_DEVICE_RESET;
SDL_PushEvent(&event);
return;
}
@@ -674,7 +733,11 @@ void PlVkRenderer::waitToRender()
// Handle the swapchain being resized
int vkDrawableW, vkDrawableH;
#if SDL_VERSION_ATLEAST(3, 0, 0)
SDL_GetWindowSizeInPixels(m_Window, &vkDrawableW, &vkDrawableH);
#else
SDL_Vulkan_GetDrawableSize(m_Window, &vkDrawableW, &vkDrawableH);
#endif
if (!pl_swapchain_resize(m_Swapchain, &vkDrawableW, &vkDrawableH)) {
// Swapchain (re)creation can fail if the window is occluded
return;
@@ -858,12 +921,12 @@ void PlVkRenderer::notifyOverlayUpdated(Overlay::OverlayType type)
return;
}
SDL_AtomicLock(&m_OverlayLock);
SDL_LockSpinlock(&m_OverlayLock);
// We want to clear the staging overlay flag even if a staging overlay is still present,
// since this ensures the render thread will not read from a partially initialized pl_tex
// as we modify or recreate the staging overlay texture outside the overlay lock.
m_Overlays[type].hasStagingOverlay = false;
SDL_AtomicUnlock(&m_OverlayLock);
SDL_UnlockSpinlock(&m_OverlayLock);
// If there's no new staging overlay, free the old staging overlay texture.
// NB: This is safe to do outside the overlay lock because we're guaranteed
@@ -878,7 +941,7 @@ void PlVkRenderer::notifyOverlayUpdated(Overlay::OverlayType type)
SDL_assert(newSurface->format->format == SDL_PIXELFORMAT_ARGB8888);
pl_fmt texFormat = pl_find_named_fmt(m_Vulkan->gpu, "bgra8");
if (!texFormat) {
SDL_FreeSurface(newSurface);
SDL_DestroySurface(newSurface);
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"pl_find_named_fmt(bgra8) failed");
return;
@@ -898,7 +961,7 @@ void PlVkRenderer::notifyOverlayUpdated(Overlay::OverlayType type)
if (!pl_tex_recreate(m_Vulkan->gpu, &m_Overlays[type].stagingOverlay.tex, &texParams)) {
pl_tex_destroy(m_Vulkan->gpu, &m_Overlays[type].stagingOverlay.tex);
SDL_zero(m_Overlays[type].stagingOverlay);
SDL_FreeSurface(newSurface);
SDL_DestroySurface(newSurface);
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"pl_tex_recreate() failed");
return;
@@ -915,7 +978,7 @@ void PlVkRenderer::notifyOverlayUpdated(Overlay::OverlayType type)
if (!pl_tex_upload(m_Vulkan->gpu, &xferParams)) {
pl_tex_destroy(m_Vulkan->gpu, &m_Overlays[type].stagingOverlay.tex);
SDL_zero(m_Overlays[type].stagingOverlay);
SDL_FreeSurface(newSurface);
SDL_DestroySurface(newSurface);
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"pl_tex_upload() failed");
return;
@@ -931,10 +994,10 @@ void PlVkRenderer::notifyOverlayUpdated(Overlay::OverlayType type)
m_Overlays[type].stagingOverlay.color = pl_color_space_srgb;
// Make this staging overlay visible to the render thread
SDL_AtomicLock(&m_OverlayLock);
SDL_LockSpinlock(&m_OverlayLock);
SDL_assert(!m_Overlays[type].hasStagingOverlay);
m_Overlays[type].hasStagingOverlay = true;
SDL_AtomicUnlock(&m_OverlayLock);
SDL_UnlockSpinlock(&m_OverlayLock);
}
bool PlVkRenderer::notifyWindowChanged(PWINDOW_STATE_CHANGE_INFO info)
+2 -2
View File
@@ -37,7 +37,7 @@ private:
static void overlayUploadComplete(void* opaque);
bool mapAvFrameToPlacebo(const AVFrame *frame, pl_frame* mappedFrame);
bool getQueue(VkQueueFlags requiredFlags, uint32_t* queueIndex, uint32_t* queueCount);
bool populateQueues(int videoFormat);
bool chooseVulkanDevice(PDECODER_PARAMETERS params, bool hdrOutputRequired);
bool tryInitializeDevice(VkPhysicalDevice device, VkPhysicalDeviceProperties* deviceProps,
PDECODER_PARAMETERS decoderParams, bool hdrOutputRequired);
@@ -95,7 +95,7 @@ private:
// Vulkan functions we call directly
PFN_vkDestroySurfaceKHR fn_vkDestroySurfaceKHR = nullptr;
PFN_vkGetPhysicalDeviceQueueFamilyProperties fn_vkGetPhysicalDeviceQueueFamilyProperties = nullptr;
PFN_vkGetPhysicalDeviceQueueFamilyProperties2 fn_vkGetPhysicalDeviceQueueFamilyProperties2 = nullptr;
PFN_vkGetPhysicalDeviceSurfacePresentModesKHR fn_vkGetPhysicalDeviceSurfacePresentModesKHR = nullptr;
PFN_vkGetPhysicalDeviceSurfaceFormatsKHR fn_vkGetPhysicalDeviceSurfaceFormatsKHR = nullptr;
PFN_vkEnumeratePhysicalDevices fn_vkEnumeratePhysicalDevices = nullptr;
@@ -1,6 +1,6 @@
#pragma once
#include <SDL.h>
#include "SDL_compat.h"
#include "streaming/video/decoder.h"
#include "streaming/video/overlaymanager.h"
@@ -5,8 +5,6 @@
#include <Limelight.h>
#include <SDL_syswm.h>
extern "C" {
#include <libavutil/pixdesc.h>
#include <libavutil/opt.h>
@@ -140,28 +138,19 @@ bool SdlRenderer::initialize(PDECODER_PARAMETERS params)
return false;
}
SDL_SysWMinfo info;
SDL_VERSION(&info.version);
if (!SDL_GetWindowWMInfo(params->window, &info)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_GetWindowWMInfo() failed: %s",
SDL_GetError());
return false;
}
// Only set SDL_RENDERER_PRESENTVSYNC if we know we'll get tearing otherwise.
// Since we don't use V-Sync to pace our frame rate, we want non-blocking
// presents to reduce video latency.
switch (info.subsystem) {
case SDL_SYSWM_WINDOWS:
switch (SDLC_GetVideoDriver()) {
case SDLC_VIDEO_WIN32:
// DWM is always tear-free except in full-screen exclusive mode
if ((SDL_GetWindowFlags(params->window) & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN) {
if (SDLC_IsFullscreenExclusive(params->window)) {
if (params->enableVsync) {
rendererFlags |= SDL_RENDERER_PRESENTVSYNC;
}
}
break;
case SDL_SYSWM_WAYLAND:
case SDLC_VIDEO_WAYLAND:
// Wayland is always tear-free in all modes
break;
default:
@@ -180,7 +169,7 @@ bool SdlRenderer::initialize(PDECODER_PARAMETERS params)
SDL_SetHintWithPriority(SDL_HINT_RENDER_DIRECT3D_THREADSAFE, "1", SDL_HINT_OVERRIDE);
#endif
m_Renderer = SDL_CreateRenderer(params->window, -1, rendererFlags);
m_Renderer = SDL_CreateRenderer(params->window, SDLC_DEFAULT_RENDER_DRIVER, rendererFlags);
if (!m_Renderer) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_CreateRenderer() failed: %s",
@@ -202,7 +191,7 @@ bool SdlRenderer::initialize(PDECODER_PARAMETERS params)
else {
// If we get here prior to the start of a session, just pump and flush ourselves.
SDL_PumpEvents();
SDL_FlushEvent(SDL_WINDOWEVENT);
SDLC_FlushWindowEvents();
}
#ifdef Q_OS_WIN32
@@ -232,7 +221,7 @@ void SdlRenderer::renderOverlay(Overlay::OverlayType type)
if (type == Overlay::OverlayStatusUpdate) {
// Bottom Left
SDL_Rect viewportRect;
SDL_RenderGetViewport(m_Renderer, &viewportRect);
SDL_GetRenderViewport(m_Renderer, &viewportRect);
m_OverlayRects[type].x = 0;
m_OverlayRects[type].y = viewportRect.h - newSurface->h;
}
@@ -246,12 +235,12 @@ void SdlRenderer::renderOverlay(Overlay::OverlayType type)
m_OverlayRects[type].h = newSurface->h;
m_OverlayTextures[type] = SDL_CreateTextureFromSurface(m_Renderer, newSurface);
SDL_FreeSurface(newSurface);
SDL_DestroySurface(newSurface);
}
// If we have an overlay texture, render it too
if (m_OverlayTextures[type] != nullptr) {
SDL_RenderCopy(m_Renderer, m_OverlayTextures[type], nullptr, &m_OverlayRects[type]);
SDL_RenderTexture(m_Renderer, m_OverlayTextures[type], nullptr, &m_OverlayRects[type]);
}
}
}
+51 -39
View File
@@ -1,5 +1,7 @@
#include <QString>
#include <vector>
// HACK: Include before vaapi.h to prevent conflicts with Xlib.h
#include <streaming/session.h>
@@ -11,8 +13,6 @@
#include <xf86drm.h>
#endif
#include <SDL_syswm.h>
#include <unistd.h>
#include <fcntl.h>
@@ -79,23 +79,14 @@ VAAPIRenderer::~VAAPIRenderer()
VADisplay
VAAPIRenderer::openDisplay(SDL_Window* window)
{
SDL_SysWMinfo info;
VADisplay display;
SDL_VERSION(&info.version);
m_WindowSystem = SDLC_GetVideoDriver();
if (!SDL_GetWindowWMInfo(window, &info)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_GetWindowWMInfo() failed: %s",
SDL_GetError());
return nullptr;
}
m_WindowSystem = info.subsystem;
if (info.subsystem == SDL_SYSWM_X11) {
if (m_WindowSystem == SDLC_VIDEO_X11) {
#ifdef HAVE_LIBVA_X11
m_XWindow = info.info.x11.window;
display = vaGetDisplay(info.info.x11.display);
m_XWindow = (Window)SDLC_X11_GetWindow(window);
display = vaGetDisplay((Display*)SDLC_X11_GetDisplay(window));
if (display == nullptr) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Unable to open X11 display for VAAPI");
@@ -107,9 +98,9 @@ VAAPIRenderer::openDisplay(SDL_Window* window)
return nullptr;
#endif
}
else if (info.subsystem == SDL_SYSWM_WAYLAND) {
else if (m_WindowSystem == SDLC_VIDEO_WAYLAND) {
#ifdef HAVE_LIBVA_WAYLAND
display = vaGetDisplayWl(info.info.wl.display);
display = vaGetDisplayWl((wl_display*)SDLC_Wayland_GetDisplay(window));
if (display == nullptr) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Unable to open Wayland display for VAAPI");
@@ -121,18 +112,35 @@ VAAPIRenderer::openDisplay(SDL_Window* window)
return nullptr;
#endif
}
#if defined(SDL_VIDEO_DRIVER_KMSDRM) && defined(HAVE_LIBVA_DRM) && SDL_VERSION_ATLEAST(2, 0, 15)
else if (info.subsystem == SDL_SYSWM_KMSDRM) {
SDL_assert(info.info.kmsdrm.drm_fd >= 0);
else if (m_WindowSystem == SDLC_VIDEO_KMSDRM) {
#ifdef HAVE_LIBVA_DRM
// It's possible to enter this function several times as we're probing VA drivers.
// Make sure to only duplicate the DRM FD the first time through.
if (m_DrmFd < 0) {
// Try to get the FD that we're sharing with SDL
bool mustCloseFd = false;
int fd = StreamUtils::getDrmFdForWindow(window, &mustCloseFd);
if (fd < 0) {
// Try to open any DRM render node
fd = StreamUtils::getDrmFd(true);
if (fd < 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Failed to open DRM render node: %d",
errno);
return nullptr;
}
}
// If the KMSDRM FD is not a render node FD, open the render node for libva to use.
// Since libva 2.20, using a primary node will fail in vaGetDriverNames().
if (drmGetNodeTypeFromFd(info.info.kmsdrm.drm_fd) != DRM_NODE_RENDER) {
char* renderNodePath = drmGetRenderDeviceNameFromFd(info.info.kmsdrm.drm_fd);
if (drmGetNodeTypeFromFd(fd) != DRM_NODE_RENDER) {
char* renderNodePath = drmGetRenderDeviceNameFromFd(fd);
if (renderNodePath) {
// Don't need the primary node FD anymore
if (mustCloseFd) {
close(fd);
}
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Opening render node for VAAPI: %s",
renderNodePath);
@@ -148,13 +156,13 @@ VAAPIRenderer::openDisplay(SDL_Window* window)
else {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Failed to get render node path. Using the SDL FD directly.");
m_DrmFd = dup(info.info.kmsdrm.drm_fd);
m_DrmFd = mustCloseFd ? fd : dup(fd);
}
}
else {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"KMSDRM FD is already a render node. Using the SDL FD directly.");
m_DrmFd = dup(info.info.kmsdrm.drm_fd);
m_DrmFd = mustCloseFd ? fd : dup(fd);
}
}
@@ -164,12 +172,16 @@ VAAPIRenderer::openDisplay(SDL_Window* window)
"Unable to open DRM display for VAAPI");
return nullptr;
}
}
#else
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Moonlight not compiled with VAAPI DRM support!");
return nullptr;
#endif
}
else {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Unsupported VAAPI rendering subsystem: %d",
info.subsystem);
m_WindowSystem);
return nullptr;
}
@@ -281,7 +293,7 @@ VAAPIRenderer::initialize(PDECODER_PARAMETERS params)
status = tryVaInitialize(vaDeviceContext, params, &major, &minor);
}
if (status != VA_STATUS_SUCCESS && (m_WindowSystem != SDL_SYSWM_X11 || m_DecoderSelectionPass > 0)) {
if (status != VA_STATUS_SUCCESS && (m_WindowSystem != SDLC_VIDEO_X11 || m_DecoderSelectionPass > 0)) {
// The unofficial nvidia VAAPI driver over NVDEC/CUDA works well on Wayland,
// but we'd rather use CUDA for XWayland and VDPAU for regular X11.
// NB: Remember to update the VA-API NVDEC condition below when modifying this!
@@ -394,7 +406,7 @@ VAAPIRenderer::initialize(PDECODER_PARAMETERS params)
}
// Prefer CUDA for XWayland and VDPAU for regular X11.
if (m_WindowSystem == SDL_SYSWM_X11 && vendorStr.contains("VA-API NVDEC", Qt::CaseInsensitive)) {
if (m_WindowSystem == SDLC_VIDEO_X11 && vendorStr.contains("VA-API NVDEC", Qt::CaseInsensitive)) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Deprioritizing VAAPI for NVIDIA driver on X11/XWayland. Set FORCE_VAAPI=1 to override.");
return false;
@@ -538,7 +550,7 @@ VAAPIRenderer::isDirectRenderingSupported()
}
// We only support direct rendering on X11 with VAEntrypointVideoProc support
if (m_WindowSystem != SDL_SYSWM_X11 || m_BlacklistedForDirectRendering) {
if (m_WindowSystem != SDLC_VIDEO_X11 || m_BlacklistedForDirectRendering) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Using indirect rendering due to WM or blacklist");
return false;
@@ -556,9 +568,9 @@ VAAPIRenderer::isDirectRenderingSupported()
AVHWDeviceContext* deviceContext = (AVHWDeviceContext*)m_HwContext->data;
AVVAAPIDeviceContext* vaDeviceContext = (AVVAAPIDeviceContext*)deviceContext->hwctx;
VAEntrypoint entrypoints[vaMaxNumEntrypoints(vaDeviceContext->display)];
std::vector<VAEntrypoint> entrypoints(vaMaxNumEntrypoints(vaDeviceContext->display));
int entrypointCount;
VAStatus status = vaQueryConfigEntrypoints(vaDeviceContext->display, VAProfileNone, entrypoints, &entrypointCount);
VAStatus status = vaQueryConfigEntrypoints(vaDeviceContext->display, VAProfileNone, entrypoints.data(), &entrypointCount);
if (status == VA_STATUS_SUCCESS) {
for (int i = 0; i < entrypointCount; i++) {
// Without VAEntrypointVideoProc support, the driver will crash inside vaPutSurface()
@@ -647,7 +659,7 @@ void VAAPIRenderer::notifyOverlayUpdated(Overlay::OverlayType type)
}
if (!overlayEnabled) {
SDL_FreeSurface(newSurface);
SDL_DestroySurface(newSurface);
return;
}
@@ -661,7 +673,7 @@ void VAAPIRenderer::notifyOverlayUpdated(Overlay::OverlayType type)
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"vaCreateImage() failed: %d",
status);
SDL_FreeSurface(newSurface);
SDL_DestroySurface(newSurface);
return;
}
@@ -671,7 +683,7 @@ void VAAPIRenderer::notifyOverlayUpdated(Overlay::OverlayType type)
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"vaMapBuffer() failed: %d",
status);
SDL_FreeSurface(newSurface);
SDL_DestroySurface(newSurface);
vaDestroyImage(vaDeviceContext->display, newImage.image_id);
return;
}
@@ -686,7 +698,7 @@ void VAAPIRenderer::notifyOverlayUpdated(Overlay::OverlayType type)
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"vaUnmapBuffer() failed: %d",
status);
SDL_FreeSurface(newSurface);
SDL_DestroySurface(newSurface);
vaDestroyImage(vaDeviceContext->display, newImage.image_id);
return;
}
@@ -708,7 +720,7 @@ void VAAPIRenderer::notifyOverlayUpdated(Overlay::OverlayType type)
overlayRect.h = newSurface->h;
// Surface data is no longer needed
SDL_FreeSurface(newSurface);
SDL_DestroySurface(newSurface);
VASubpictureID newSubpicture;
status = vaCreateSubpicture(vaDeviceContext->display, newImage.image_id, &newSubpicture);
@@ -754,7 +766,7 @@ VAAPIRenderer::renderFrame(AVFrame* frame)
StreamUtils::scaleSourceToDestinationSurface(&src, &dst);
if (m_WindowSystem == SDL_SYSWM_X11) {
if (m_WindowSystem == SDLC_VIDEO_X11) {
#ifdef HAVE_LIBVA_X11
unsigned int flags = 0;
@@ -890,7 +902,7 @@ VAAPIRenderer::renderFrame(AVFrame* frame)
SDL_UnlockMutex(m_OverlayMutex);
#endif
}
else if (m_WindowSystem == SDL_SYSWM_WAYLAND) {
else if (m_WindowSystem == SDLC_VIDEO_WAYLAND) {
// We don't support direct rendering on Wayland, so we should
// never get called there. Many common Wayland compositors don't
// support YUV surfaces, so direct rendering would fail.
+1 -1
View File
@@ -92,7 +92,7 @@ private:
#endif
int m_DecoderSelectionPass;
int m_WindowSystem;
SDLC_VideoDriver m_WindowSystem;
AVBufferRef* m_HwContext;
bool m_BlacklistedForDirectRendering;
bool m_HasRfiLatencyBug;
+10 -23
View File
@@ -3,8 +3,6 @@
#include <streaming/streamutils.h>
#include <utils.h>
#include <SDL_syswm.h>
#define BAIL_ON_FAIL(status, something) if ((status) != VDP_STATUS_OK) { \
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, \
#something " failed: %d", (status)); \
@@ -78,7 +76,6 @@ bool VDPAURenderer::initialize(PDECODER_PARAMETERS params)
{
int err;
VdpStatus status;
SDL_SysWMinfo info;
// Avoid initializing VDPAU on this window on the first selection pass if:
// a) We know we want HDR compatibility
@@ -97,24 +94,16 @@ bool VDPAURenderer::initialize(PDECODER_PARAMETERS params)
}
}
SDL_VERSION(&info.version);
if (!SDL_GetWindowWMInfo(params->window, &info)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_GetWindowWMInfo() failed: %s",
SDL_GetError());
return false;
}
if (info.subsystem == SDL_SYSWM_WAYLAND) {
SDLC_VideoDriver videoDriver = SDLC_GetVideoDriver();
if (videoDriver == SDLC_VIDEO_WAYLAND) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"VDPAU is not supported on Wayland");
return false;
}
else if (info.subsystem != SDL_SYSWM_X11) {
else if (videoDriver != SDLC_VIDEO_X11) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"VDPAU is not supported on the current subsystem: %d",
info.subsystem);
videoDriver);
return false;
}
else if (qgetenv("VDPAU_XWAYLAND") != "1" && WMUtils::isRunningWayland()) {
@@ -216,12 +205,10 @@ bool VDPAURenderer::initialize(PDECODER_PARAMETERS params)
SDL_GetWindowSize(params->window, (int*)&m_DisplayWidth, (int*)&m_DisplayHeight);
SDL_assert(info.subsystem == SDL_SYSWM_X11);
GET_PROC_ADDRESS(VDP_FUNC_ID_PRESENTATION_QUEUE_TARGET_CREATE_X11,
&m_VdpPresentationQueueTargetCreateX11);
status = m_VdpPresentationQueueTargetCreateX11(m_Device,
info.info.x11.window,
SDLC_X11_GetWindow(params->window),
&m_PresentationQueueTarget);
if (status != VDP_STATUS_OK) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
@@ -387,7 +374,7 @@ void VDPAURenderer::notifyOverlayUpdated(Overlay::OverlayType type)
}
if (!overlayEnabled) {
SDL_FreeSurface(newSurface);
SDL_DestroySurface(newSurface);
return;
}
@@ -406,7 +393,7 @@ void VDPAURenderer::notifyOverlayUpdated(Overlay::OverlayType type)
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"VdpBitmapSurfaceCreate() failed: %s",
m_VdpGetErrorString(status));
SDL_FreeSurface(newSurface);
SDL_DestroySurface(newSurface);
return;
}
@@ -419,7 +406,7 @@ void VDPAURenderer::notifyOverlayUpdated(Overlay::OverlayType type)
"VdpBitmapSurfacePutBitsNative() failed: %s",
m_VdpGetErrorString(status));
m_VdpBitmapSurfaceDestroy(newBitmapSurface);
SDL_FreeSurface(newSurface);
SDL_DestroySurface(newSurface);
return;
}
@@ -440,7 +427,7 @@ void VDPAURenderer::notifyOverlayUpdated(Overlay::OverlayType type)
overlayRect.y1 = overlayRect.y0 + newSurface->h;
// Surface data is no longer needed
SDL_FreeSurface(newSurface);
SDL_DestroySurface(newSurface);
SDL_LockMutex(m_OverlayMutex);
m_OverlaySurface[type] = newBitmapSurface;
@@ -480,7 +467,7 @@ void VDPAURenderer::renderOverlay(VdpOutputSurface destination, Overlay::Overlay
return;
}
if (SDL_TryLockMutex(m_OverlayMutex) != 0) {
if (!SDL_TryLockMutex(m_OverlayMutex)) {
// If the overlay is currently being updated, skip rendering it this frame.
return;
}
@@ -5,7 +5,6 @@
#include "pacer/pacer.h"
#undef AVMediaType
#include <SDL_syswm.h>
#include <Limelight.h>
#include <streaming/session.h>
@@ -139,9 +138,8 @@ public:
return kCVReturnSuccess;
}
bool initializeVsyncCallback(SDL_SysWMinfo* info)
bool initializeVsyncCallback(NSScreen* screen)
{
NSScreen* screen = [info->info.cocoa.window screen];
CVReturn status;
if (screen == nullptr) {
// Window not visible on any display, so use a
@@ -418,23 +416,11 @@ public:
// If we're using direct rendering, set up the AVSampleBufferDisplayLayer
if (m_DirectRendering) {
SDL_SysWMinfo info;
SDL_VERSION(&info.version);
if (!SDL_GetWindowWMInfo(params->window, &info)) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"SDL_GetWindowWMInfo() failed: %s",
SDL_GetError());
return false;
}
SDL_assert(info.subsystem == SDL_SYSWM_COCOA);
NSWindow* window = (NSWindow*)SDLC_MacOS_GetWindow(params->window);
// SDL adds its own content view to listen for events.
// We need to add a subview for our display layer.
NSView* contentView = info.info.cocoa.window.contentView;
m_StreamView = [[VTView alloc] initWithFrame:contentView.bounds];
m_StreamView = [[VTView alloc] initWithFrame:window.contentView.bounds];
m_DisplayLayer = [[AVSampleBufferDisplayLayer alloc] init];
m_DisplayLayer.bounds = m_StreamView.bounds;
@@ -453,9 +439,9 @@ public:
if (isAppleSilicon && !(params->videoFormat & VIDEO_FORMAT_MASK_10BIT)) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Using layer rasterization workaround");
if (info.info.cocoa.window.screen != nullptr) {
if (window.screen != nullptr) {
m_DisplayLayer.shouldRasterize = YES;
m_DisplayLayer.rasterizationScale = info.info.cocoa.window.screen.backingScaleFactor;
m_DisplayLayer.rasterizationScale = window.screen.backingScaleFactor;
}
else {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
@@ -471,10 +457,10 @@ public:
m_StreamView.layer = m_DisplayLayer;
m_StreamView.wantsLayer = YES;
[contentView addSubview: m_StreamView];
[window.contentView addSubview: m_StreamView];
if (params->enableFramePacing) {
if (!initializeVsyncCallback(&info)) {
if (!initializeVsyncCallback(window.screen)) {
return false;
}
}
@@ -58,6 +58,7 @@ bool VTBaseRenderer::checkDecoderCapabilities(id<MTLDevice> device, PDECODER_PAR
}
}
else if (params->videoFormat & VIDEO_FORMAT_MASK_AV1) {
#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 130000
if (!VTIsHardwareDecodeSupported(kCMVideoCodecType_AV1)) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"No HW accelerated AV1 decode via VT");
@@ -66,6 +67,11 @@ bool VTBaseRenderer::checkDecoderCapabilities(id<MTLDevice> device, PDECODER_PAR
// 10-bit is part of the Main profile for AV1, so it will always
// be present on hardware that supports 8-bit.
#else
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"AV1 requires building with Xcode 14 or later");
return false;
#endif
}
return true;
@@ -5,7 +5,6 @@
#include "pacer/pacer.h"
#undef AVMediaType
#include <SDL_syswm.h>
#include <Limelight.h>
#include "streaming/session.h"
#include "streaming/streamutils.h"
+64 -35
View File
@@ -123,7 +123,7 @@ int FFmpegVideoDecoder::getDecoderCapabilities()
if (!isHardwareAccelerated()) {
// Slice up to 4 times for parallel CPU decoding, once slice per core
int slices = qMin(MAX_SLICES, SDL_GetCPUCount());
int slices = qMin(MAX_SLICES, SDL_GetNumLogicalCPUCores());
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Encoder configured for %d slices per frame",
slices);
@@ -180,15 +180,25 @@ enum AVPixelFormat FFmpegVideoDecoder::ffGetFormat(AVCodecContext* context,
const enum AVPixelFormat* pixFmts)
{
FFmpegVideoDecoder* decoder = (FFmpegVideoDecoder*)context->opaque;
const enum AVPixelFormat *p;
const AVPixelFormat *p;
AVPixelFormat desiredFmt;
for (p = pixFmts; *p != -1; p++) {
if (decoder->m_HwDecodeCfg) {
desiredFmt = decoder->m_HwDecodeCfg->pix_fmt;
}
else if (decoder->m_RequiredPixelFormat != AV_PIX_FMT_NONE) {
desiredFmt = decoder->m_RequiredPixelFormat;
}
else {
desiredFmt = decoder->m_FrontendRenderer->getPreferredPixelFormat(decoder->m_VideoFormat);
}
for (p = pixFmts; *p != AV_PIX_FMT_NONE; p++) {
// Only match our hardware decoding codec or preferred SW pixel
// format (if not using hardware decoding). It's crucial
// to override the default get_format() which will try
// to gracefully fall back to software decode and break us.
if (*p == (decoder->m_HwDecodeCfg ? decoder->m_HwDecodeCfg->pix_fmt : context->pix_fmt) &&
decoder->m_BackendRenderer->prepareDecoderContextInGetFormat(context, *p)) {
if (*p == desiredFmt && decoder->m_BackendRenderer->prepareDecoderContextInGetFormat(context, *p)) {
return *p;
}
}
@@ -196,7 +206,7 @@ enum AVPixelFormat FFmpegVideoDecoder::ffGetFormat(AVCodecContext* context,
// Failed to match the preferred pixel formats. Try non-preferred pixel format options
// for non-hwaccel decoders if we didn't have a required pixel format to use.
if (decoder->m_HwDecodeCfg == nullptr && decoder->m_RequiredPixelFormat == AV_PIX_FMT_NONE) {
for (p = pixFmts; *p != -1; p++) {
for (p = pixFmts; *p != AV_PIX_FMT_NONE; p++) {
if (decoder->m_FrontendRenderer->isPixelFormatSupported(decoder->m_VideoFormat, *p) &&
decoder->m_BackendRenderer->prepareDecoderContextInGetFormat(context, *p)) {
return *p;
@@ -259,10 +269,10 @@ void FFmpegVideoDecoder::reset()
// Terminate the decoder thread before doing anything else.
// It might be touching things we're about to free.
if (m_DecoderThread != nullptr) {
SDL_AtomicSet(&m_DecoderThreadShouldQuit, 1);
SDL_SetAtomicInt(&m_DecoderThreadShouldQuit, 1);
LiWakeWaitForVideoFrame();
SDL_WaitThread(m_DecoderThread, NULL);
SDL_AtomicSet(&m_DecoderThreadShouldQuit, 0);
SDL_SetAtomicInt(&m_DecoderThreadShouldQuit, 0);
m_DecoderThread = nullptr;
}
@@ -474,7 +484,7 @@ bool FFmpegVideoDecoder::completeInitialization(const AVCodec* decoder, enum AVP
// Enable slice multi-threading for software decoding
if (!isHardwareAccelerated()) {
m_VideoDecoderCtx->thread_type = FF_THREAD_SLICE;
m_VideoDecoderCtx->thread_count = qMin(MAX_SLICES, SDL_GetCPUCount());
m_VideoDecoderCtx->thread_count = qMin(MAX_SLICES, SDL_GetNumLogicalCPUCores());
}
else {
// No threading for HW decode
@@ -1076,15 +1086,15 @@ bool FFmpegVideoDecoder::tryInitializeRenderer(const AVCodec* decoder,
#define TRY_PREFERRED_PIXEL_FORMAT(RENDERER_TYPE) \
{ \
RENDERER_TYPE renderer; \
if (renderer.getPreferredPixelFormat(params->videoFormat) == decoder->pix_fmts[i]) { \
if (renderer.getPreferredPixelFormat(params->videoFormat) == decoder_pix_fmts[i]) { \
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, \
"Trying " #RENDERER_TYPE " for codec %s due to preferred pixel format: 0x%x", \
decoder->name, decoder->pix_fmts[i]); \
if (tryInitializeRenderer(decoder, decoder->pix_fmts[i], params, nullptr, nullptr, \
decoder->name, decoder_pix_fmts[i]); \
if (tryInitializeRenderer(decoder, decoder_pix_fmts[i], params, nullptr, nullptr, \
[]() -> IFFmpegRenderer* { return new RENDERER_TYPE(); })) { \
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, \
"Chose " #RENDERER_TYPE " for codec %s due to preferred pixel format: 0x%x", \
decoder->name, decoder->pix_fmts[i]); \
decoder->name, decoder_pix_fmts[i]); \
return true; \
} \
} \
@@ -1093,16 +1103,16 @@ bool FFmpegVideoDecoder::tryInitializeRenderer(const AVCodec* decoder,
#define TRY_SUPPORTED_NON_PREFERRED_PIXEL_FORMAT(RENDERER_TYPE) \
{ \
RENDERER_TYPE renderer; \
if (decoder->pix_fmts[i] != renderer.getPreferredPixelFormat(params->videoFormat) && \
renderer.isPixelFormatSupported(params->videoFormat, decoder->pix_fmts[i])) { \
if (decoder_pix_fmts[i] != renderer.getPreferredPixelFormat(params->videoFormat) && \
renderer.isPixelFormatSupported(params->videoFormat, decoder_pix_fmts[i])) { \
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, \
"Trying " #RENDERER_TYPE " for codec %s due to compatible pixel format: 0x%x", \
decoder->name, decoder->pix_fmts[i]); \
if (tryInitializeRenderer(decoder, decoder->pix_fmts[i], params, nullptr, nullptr, \
decoder->name, decoder_pix_fmts[i]); \
if (tryInitializeRenderer(decoder, decoder_pix_fmts[i], params, nullptr, nullptr, \
[]() -> IFFmpegRenderer* { return new RENDERER_TYPE(); })) { \
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, \
"Chose " #RENDERER_TYPE " for codec %s due to compatible pixel format: 0x%x", \
decoder->name, decoder->pix_fmts[i]); \
decoder->name, decoder_pix_fmts[i]); \
return true; \
} \
} \
@@ -1116,6 +1126,16 @@ bool FFmpegVideoDecoder::tryInitializeRendererForUnknownDecoder(const AVCodec* d
return false;
}
const AVPixelFormat* decoder_pix_fmts;
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(61, 13, 100)
if (avcodec_get_supported_config(nullptr, decoder, AV_CODEC_CONFIG_PIX_FORMAT, 0,
(const void**)&decoder_pix_fmts, nullptr) < 0) {
decoder_pix_fmts = nullptr;
}
#else
decoder_pix_fmts = decoder->pix_fmts;
#endif
// This might be a hwaccel decoder, so try any hw configs first
if (tryHwAccel) {
for (int pass = 0; pass <= MAX_DECODER_PASS; pass++) {
@@ -1141,7 +1161,7 @@ bool FFmpegVideoDecoder::tryInitializeRendererForUnknownDecoder(const AVCodec* d
}
}
if (decoder->pix_fmts == NULL) {
if (decoder_pix_fmts == NULL) {
// Supported output pixel formats are unknown. We'll just try DRM/SDL and hope it can cope.
#if defined(HAVE_DRM) && defined(GL_IS_SLOW)
@@ -1177,11 +1197,11 @@ bool FFmpegVideoDecoder::tryInitializeRendererForUnknownDecoder(const AVCodec* d
// Even if it didn't completely deadlock us, the performance would likely be atrocious.
if (strcmp(decoder->name, "h264_mmal") == 0) {
#ifdef HAVE_MMAL
for (int i = 0; decoder->pix_fmts[i] != AV_PIX_FMT_NONE; i++) {
for (int i = 0; decoder_pix_fmts[i] != AV_PIX_FMT_NONE; i++) {
TRY_PREFERRED_PIXEL_FORMAT(MmalRenderer);
}
for (int i = 0; decoder->pix_fmts[i] != AV_PIX_FMT_NONE; i++) {
for (int i = 0; decoder_pix_fmts[i] != AV_PIX_FMT_NONE; i++) {
TRY_SUPPORTED_NON_PREFERRED_PIXEL_FORMAT(MmalRenderer);
}
#endif
@@ -1191,7 +1211,7 @@ bool FFmpegVideoDecoder::tryInitializeRendererForUnknownDecoder(const AVCodec* d
}
// Check if any of our decoders prefer any of the pixel formats first
for (int i = 0; decoder->pix_fmts[i] != AV_PIX_FMT_NONE; i++) {
for (int i = 0; decoder_pix_fmts[i] != AV_PIX_FMT_NONE; i++) {
#ifdef HAVE_DRM
TRY_PREFERRED_PIXEL_FORMAT(DrmRenderer);
#endif
@@ -1204,7 +1224,7 @@ bool FFmpegVideoDecoder::tryInitializeRendererForUnknownDecoder(const AVCodec* d
}
// Nothing prefers any of them. Let's see if anyone will tolerate one.
for (int i = 0; decoder->pix_fmts[i] != AV_PIX_FMT_NONE; i++) {
for (int i = 0; decoder_pix_fmts[i] != AV_PIX_FMT_NONE; i++) {
#ifdef HAVE_DRM
TRY_SUPPORTED_NON_PREFERRED_PIXEL_FORMAT(DrmRenderer);
#endif
@@ -1219,10 +1239,10 @@ bool FFmpegVideoDecoder::tryInitializeRendererForUnknownDecoder(const AVCodec* d
#if defined(HAVE_LIBPLACEBO_VULKAN) && defined(VULKAN_IS_SLOW)
// If we got here with VULKAN_IS_SLOW, DrmRenderer didn't work,
// so we have to resort to PlVkRenderer.
for (int i = 0; decoder->pix_fmts[i] != AV_PIX_FMT_NONE; i++) {
for (int i = 0; decoder_pix_fmts[i] != AV_PIX_FMT_NONE; i++) {
TRY_PREFERRED_PIXEL_FORMAT(PlVkRenderer);
}
for (int i = 0; decoder->pix_fmts[i] != AV_PIX_FMT_NONE; i++) {
for (int i = 0; decoder_pix_fmts[i] != AV_PIX_FMT_NONE; i++) {
TRY_SUPPORTED_NON_PREFERRED_PIXEL_FORMAT(PlVkRenderer);
}
#endif
@@ -1230,10 +1250,10 @@ bool FFmpegVideoDecoder::tryInitializeRendererForUnknownDecoder(const AVCodec* d
#ifdef GL_IS_SLOW
// If we got here with GL_IS_SLOW, DrmRenderer didn't work, so we have
// to resort to SdlRenderer.
for (int i = 0; decoder->pix_fmts[i] != AV_PIX_FMT_NONE; i++) {
for (int i = 0; decoder_pix_fmts[i] != AV_PIX_FMT_NONE; i++) {
TRY_PREFERRED_PIXEL_FORMAT(SdlRenderer);
}
for (int i = 0; decoder->pix_fmts[i] != AV_PIX_FMT_NONE; i++) {
for (int i = 0; decoder_pix_fmts[i] != AV_PIX_FMT_NONE; i++) {
TRY_SUPPORTED_NON_PREFERRED_PIXEL_FORMAT(SdlRenderer);
}
#endif
@@ -1369,9 +1389,18 @@ bool FFmpegVideoDecoder::tryInitializeNonHwAccelDecoder(PDECODER_PARAMETERS para
// Skip decoders without zero-copy output formats if requested
if (requireZeroCopyFormat) {
const AVPixelFormat* decoder_pix_fmts;
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(61, 13, 100)
if (avcodec_get_supported_config(nullptr, decoder, AV_CODEC_CONFIG_PIX_FORMAT, 0,
(const void**)&decoder_pix_fmts, nullptr) < 0) {
decoder_pix_fmts = nullptr;
}
#else
decoder_pix_fmts = decoder->pix_fmts;
#endif
bool foundZeroCopyFormat = false;
for (int i = 0; decoder->pix_fmts && decoder->pix_fmts[i] != AV_PIX_FMT_NONE; i++) {
if (isZeroCopyFormat(decoder->pix_fmts[i])) {
for (int i = 0; decoder_pix_fmts && decoder_pix_fmts[i] != AV_PIX_FMT_NONE; i++) {
if (isZeroCopyFormat(decoder_pix_fmts[i])) {
foundZeroCopyFormat = true;
break;
}
@@ -1579,7 +1608,7 @@ int FFmpegVideoDecoder::decoderThreadProcThunk(void *context)
void FFmpegVideoDecoder::decoderThreadProc()
{
while (!SDL_AtomicGet(&m_DecoderThreadShouldQuit)) {
while (!SDL_GetAtomicInt(&m_DecoderThreadShouldQuit)) {
if (m_FramesIn == m_FramesOut) {
VIDEO_FRAME_HANDLE handle;
PDECODE_UNIT du;
@@ -1707,18 +1736,18 @@ void FFmpegVideoDecoder::decoderThreadProc()
"Resetting decoder due to consistent failure");
SDL_Event event;
event.type = SDL_RENDER_DEVICE_RESET;
event.type = SDL_EVENT_RENDER_DEVICE_RESET;
SDL_PushEvent(&event);
// Don't consume any additional data
SDL_AtomicSet(&m_DecoderThreadShouldQuit, 1);
SDL_SetAtomicInt(&m_DecoderThreadShouldQuit, 1);
}
// Just in case the error resulted in the loss of the frame,
// request an IDR frame to reset our decoder state.
LiRequestIdrFrame();
}
} while (err == AVERROR(EAGAIN) && !SDL_AtomicGet(&m_DecoderThreadShouldQuit));
} while (err == AVERROR(EAGAIN) && !SDL_GetAtomicInt(&m_DecoderThreadShouldQuit));
if (err != 0) {
// Free the frame if we failed to submit it
@@ -1833,11 +1862,11 @@ int FFmpegVideoDecoder::submitDecodeUnit(PDECODE_UNIT du)
"Resetting decoder due to consistent failure");
SDL_Event event;
event.type = SDL_RENDER_DEVICE_RESET;
event.type = SDL_EVENT_RENDER_DEVICE_RESET;
SDL_PushEvent(&event);
// Don't consume any additional data
SDL_AtomicSet(&m_DecoderThreadShouldQuit, 1);
SDL_SetAtomicInt(&m_DecoderThreadShouldQuit, 1);
}
return DR_NEED_IDR;
+4 -4
View File
@@ -133,7 +133,7 @@ void OverlayManager::notifyOverlayUpdated(OverlayType type)
}
// m_FontData must stay around until the font is closed
m_Overlays[type].font = TTF_OpenFontRW(SDL_RWFromConstMem(m_FontData.constData(), m_FontData.size()),
m_Overlays[type].font = TTF_OpenFontRW(SDL_IOFromConstMem(m_FontData.constData(), m_FontData.size()),
1,
m_Overlays[type].fontSize);
if (m_Overlays[type].font == nullptr) {
@@ -146,11 +146,11 @@ void OverlayManager::notifyOverlayUpdated(OverlayType type)
}
}
SDL_Surface* oldSurface = (SDL_Surface*)SDL_AtomicSetPtr((void**)&m_Overlays[type].surface, nullptr);
SDL_Surface* oldSurface = (SDL_Surface*) SDL_SetAtomicPointer((void**)&m_Overlays[type].surface, nullptr);
// Free the old surface
if (oldSurface != nullptr) {
SDL_FreeSurface(oldSurface);
SDL_DestroySurface(oldSurface);
}
if (m_Overlays[type].enabled) {
@@ -159,7 +159,7 @@ void OverlayManager::notifyOverlayUpdated(OverlayType type)
m_Overlays[type].text,
m_Overlays[type].color,
1024);
SDL_AtomicSetPtr((void**)&m_Overlays[type].surface, surface);
SDL_SetAtomicPointer((void**)&m_Overlays[type].surface, surface);
}
// Notify the renderer
+1 -1
View File
@@ -2,7 +2,7 @@
#include <QString>
#include <SDL.h>
#include "SDL_compat.h"
#include <SDL_ttf.h>
namespace Overlay {
+3 -3
View File
@@ -195,7 +195,7 @@ void SLVideoDecoder::notifyOverlayUpdated(Overlay::OverlayType type)
}
if (!overlayEnabled) {
SDL_FreeSurface(newSurface);
SDL_DestroySurface(newSurface);
return;
}
@@ -203,7 +203,7 @@ void SLVideoDecoder::notifyOverlayUpdated(Overlay::OverlayType type)
if (m_Overlay == nullptr) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SLVideo_CreateOverlay() failed");
SDL_FreeSurface(newSurface);
SDL_DestroySurface(newSurface);
return;
}
@@ -221,7 +221,7 @@ void SLVideoDecoder::notifyOverlayUpdated(Overlay::OverlayType type)
SLVideo_SetOverlayDisplayArea(m_Overlay, 0.0f, 1.0f - flHeight, flWidth, flHeight);
// We're done with the surface now
SDL_FreeSurface(newSurface);
SDL_DestroySurface(newSurface);
// Show the overlay
SLVideo_ShowOverlay(m_Overlay);
+7 -7
View File
@@ -3,7 +3,7 @@
#include "utils.h"
#include <SDL.h>
#include "SDL_compat.h"
#ifdef HAS_X11
#include <X11/Xlib.h>
@@ -24,10 +24,10 @@
bool WMUtils::isRunningX11()
{
#ifdef HAS_X11
static SDL_atomic_t isRunningOnX11;
static SDL_AtomicInt isRunningOnX11;
// If the value is not set yet, populate it now.
int val = SDL_AtomicGet(&isRunningOnX11);
int val = SDL_GetAtomicInt(&isRunningOnX11);
if (!(val & VALUE_SET)) {
Display* display = XOpenDisplay(nullptr);
if (display != nullptr) {
@@ -38,7 +38,7 @@ bool WMUtils::isRunningX11()
// This can race with another thread populating the same data,
// but that's no big deal.
val = VALUE_SET | ((display != nullptr) ? VALUE_TRUE : 0);
SDL_AtomicSet(&isRunningOnX11, val);
SDL_SetAtomicInt(&isRunningOnX11, val);
}
return !!(val & VALUE_TRUE);
@@ -50,10 +50,10 @@ bool WMUtils::isRunningX11()
bool WMUtils::isRunningWayland()
{
#ifdef HAS_WAYLAND
static SDL_atomic_t isRunningOnWayland;
static SDL_AtomicInt isRunningOnWayland;
// If the value is not set yet, populate it now.
int val = SDL_AtomicGet(&isRunningOnWayland);
int val = SDL_GetAtomicInt(&isRunningOnWayland);
if (!(val & VALUE_SET)) {
struct wl_display* display = wl_display_connect(nullptr);
if (display != nullptr) {
@@ -64,7 +64,7 @@ bool WMUtils::isRunningWayland()
// This can race with another thread populating the same data,
// but that's no big deal.
val = VALUE_SET | ((display != nullptr) ? VALUE_TRUE : 0);
SDL_AtomicSet(&isRunningOnWayland, val);
SDL_SetAtomicInt(&isRunningOnWayland, val);
}
return !!(val & VALUE_TRUE);
+10 -10
View File
@@ -5,7 +5,7 @@ clone_depth: 1
environment:
matrix:
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022
QTDIR: C:\Qt\6.7
QTDIR: C:\Qt\6.8
- APPVEYOR_BUILD_WORKER_IMAGE: macOS-Sonoma
BUILD_TARGET: macos
QTDIR: Qt/6.6
@@ -16,9 +16,9 @@ environment:
FFMPEG_CONFIGURE_ARGS: --enable-pic --disable-static --enable-shared --disable-all --enable-avcodec --enable-avformat --enable-swscale --enable-decoder=h264 --enable-decoder=hevc --enable-decoder=av1 --enable-hwaccel=h264_vaapi --enable-hwaccel=hevc_vaapi --enable-hwaccel=av1_vaapi --enable-hwaccel=h264_vdpau --enable-hwaccel=hevc_vdpau --enable-hwaccel=av1_vdpau --enable-libdrm --enable-hwaccel=h264_vulkan --enable-hwaccel=hevc_vulkan --enable-hwaccel=av1_vulkan --enable-libdav1d --enable-decoder=libdav1d
install:
- cmd: 'copy /y scripts\appveyor\qmake.bat %QTDIR%\msvc2019_arm64\bin\'
- cmd: 'copy /y scripts\appveyor\qtpaths.bat %QTDIR%\msvc2019_arm64\bin\'
- cmd: 'copy /y scripts\appveyor\target_qt.conf %QTDIR%\msvc2019_arm64\bin\'
- cmd: 'copy /y scripts\appveyor\qmake.bat %QTDIR%\msvc2022_arm64\bin\'
- cmd: 'copy /y scripts\appveyor\qtpaths.bat %QTDIR%\msvc2022_arm64\bin\'
- cmd: 'copy /y scripts\appveyor\target_qt.conf %QTDIR%\msvc2022_arm64\bin\'
- sh: '[ "$BUILD_TARGET" != macos ] || nvm use node'
- sh: '[ "$BUILD_TARGET" != macos ] || npm install --global create-dmg'
- sh: '[ "$BUILD_TARGET" != steamlink ] || sudo apt install -y libc6:i386 libstdc++6:i386'
@@ -29,26 +29,26 @@ install:
- sh: '[ "$BUILD_TARGET" != linux ] || sudo apt update || true'
- sh: '[ "$BUILD_TARGET" != linux ] || sudo apt install -y qt515base qt515quickcontrols2 qt515svg qt515wayland python3-pip nasm libgbm-dev libdrm-dev libfreetype6-dev libasound2-dev libdbus-1-dev libegl1-mesa-dev libgl1-mesa-dev libgles2-mesa-dev libglu1-mesa-dev libibus-1.0-dev libpulse-dev libudev-dev libx11-dev libxcursor-dev libxext-dev libxi-dev libxinerama-dev libxkbcommon-dev libxrandr-dev libxss-dev libxt-dev libxv-dev libxxf86vm-dev libxcb-dri3-dev libx11-xcb-dev wayland-protocols libopus-dev libvdpau-dev vulkan-sdk'
- sh: '[ "$BUILD_TARGET" != linux ] || sudo pip3 install meson'
- sh: 'if [[ "$BUILD_TARGET" = linux ]]; then export SDL2_REV=1b26b54402eaa0a4fab4fcc799dcfa80d539fe8b && git clone https://github.com/libsdl-org/SDL.git SDL2 && cd SDL2 && git checkout $SDL2_REV && ./configure && make -j$(nproc) && sudo make install && cd ..; fi'
- sh: 'if [[ "$BUILD_TARGET" = linux ]]; then export SDL2_REV=86fd4ed83cdcf71fef6a57766b126e88f923acd3 && git clone https://github.com/libsdl-org/SDL.git SDL2 && cd SDL2 && git checkout $SDL2_REV && ./configure && make -j$(nproc) && sudo make install && cd ..; fi'
- sh: 'if [[ "$BUILD_TARGET" = linux ]]; then export SDL2_TTF_VER=2.22.0 && wget https://github.com/libsdl-org/SDL_ttf/releases/download/release-$SDL2_TTF_VER/SDL2_ttf-$SDL2_TTF_VER.tar.gz && tar -xf SDL2_ttf-$SDL2_TTF_VER.tar.gz && cd SDL2_ttf-$SDL2_TTF_VER && ./configure && make -j$(nproc) && sudo make install && cd ..; fi'
- sh: 'if [[ "$BUILD_TARGET" = linux ]]; then export LIBVA_VER=2.22.0 && git clone --branch $LIBVA_VER --depth 1 https://github.com/intel/libva.git && cd libva && ./autogen.sh && ./configure --enable-x11 --enable-wayland && make -j$(nproc) && sudo make install && cd ..; fi'
- sh: 'if [[ "$BUILD_TARGET" = linux ]]; then export DAV1D_VER=1.4.3 && git clone --branch $DAV1D_VER --depth 1 https://code.videolan.org/videolan/dav1d.git && cd dav1d && meson setup build -Ddefault_library=static -Dbuildtype=release -Denable_tools=false -Denable_tests=false && ninja -C build && sudo ninja install -C build && sudo ldconfig && cd ..; fi'
- sh: 'if [[ "$BUILD_TARGET" = linux ]]; then export DAV1D_VER=1.5.0 && git clone --branch $DAV1D_VER --depth 1 https://code.videolan.org/videolan/dav1d.git && cd dav1d && meson setup build -Ddefault_library=static -Dbuildtype=release -Denable_tools=false -Denable_tests=false && ninja -C build && sudo ninja install -C build && sudo ldconfig && cd ..; fi'
- sh: 'if [[ "$BUILD_TARGET" = linux ]]; then export LIBPLACEBO_REV=v7.349.0 && git clone https://code.videolan.org/videolan/libplacebo.git && cd libplacebo && git checkout $LIBPLACEBO_REV && git apply ../app/deploy/linux/appimage/*.patch && git submodule update --init --recursive && meson setup build -Dvulkan=enabled -Dopengl=disabled -Ddemos=false && ninja -C build && sudo ninja install -C build && sudo ldconfig && cd ..; fi'
- sh: 'if [[ "$BUILD_TARGET" = linux ]]; then export FFMPEG_REV=d62fa33d87b99afa091dc5309e9217b75cc3c7b3 && git clone https://github.com/cgutman/FFmpeg.git FFmpeg && cd FFmpeg && git checkout $FFMPEG_REV && ./configure $FFMPEG_CONFIGURE_ARGS && make -j$(nproc) && sudo make install && sudo ldconfig && cd ..; fi'
- sh: 'if [[ "$BUILD_TARGET" = linux ]]; then export FFMPEG_REV=87ccf995cb855f0baced9916928b7b48d8b6ed9d && git clone https://github.com/FFmpeg/FFmpeg.git FFmpeg && cd FFmpeg && git checkout $FFMPEG_REV && ./configure $FFMPEG_CONFIGURE_ARGS && make -j$(nproc) && sudo make install && sudo ldconfig && cd ..; fi'
- sh: 'if [[ "$BUILD_TARGET" = linux ]]; then mkdir $HOME/bin && wget -O $HOME/bin/linuxdeployqt https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage && chmod a+x $HOME/bin/linuxdeployqt; fi'
before_build:
- 'git -c submodule.libs.update=none submodule update --init --recursive'
- cmd: 'git submodule update --init --recursive'
- sh: '[ "$BUILD_TARGET" != macos ] || git submodule update --init --recursive'
- sh: '[ "$BUILD_TARGET" = linux ] || git submodule update --init --recursive'
- cmd: 'set OLDPATH=%PATH%'
- cmd: 'set /p VERSION=<app\version.txt'
- sh: 'export VERSION=`cat app/version.txt`'
build_script:
- cmd: 'set PATH=%OLDPATH%;%QTDIR%\msvc2019_64\bin'
- cmd: 'set PATH=%OLDPATH%;%QTDIR%\msvc2022_64\bin'
- cmd: 'scripts\build-arch.bat Release'
- cmd: 'set PATH=%OLDPATH%;%QTDIR%\msvc2019_arm64\bin'
- cmd: 'set PATH=%OLDPATH%;%QTDIR%\msvc2022_arm64\bin'
- cmd: 'scripts\build-arch.bat Release'
- cmd: 'scripts\generate-bundle.bat Release'
- sh: '[ "$BUILD_TARGET" != linux ] || source /opt/qt515/bin/qt515-env.sh'
+1 -1
Submodule libs updated: a27d6a7995...aba5ddffd0
+1 -1
View File
@@ -1,2 +1,2 @@
@echo off
%QTDIR%\msvc2019_64\bin\qmake.exe -qtconf "%~dp0\target_qt.conf" %*
%QTDIR%\msvc2022_64\bin\qmake.exe -qtconf "%~dp0\target_qt.conf" %*
+1 -1
View File
@@ -1,2 +1,2 @@
@echo off
%QTDIR%\msvc2019_64\bin\qtpaths.exe -qtconf "%~dp0\target_qt.conf" %*
%QTDIR%\msvc2022_64\bin\qtpaths.exe -qtconf "%~dp0\target_qt.conf" %*
+5 -5
View File
@@ -1,5 +1,5 @@
[DevicePaths]
Prefix=C:/Qt/Qt-6.7
Prefix=C:/Qt/Qt-6.8
[Paths]
Prefix=../
Documentation=./doc
@@ -15,14 +15,14 @@ Translations=./translations
Examples=examples
Tests=tests
Settings=etc/xdg
HostPrefix=../../msvc2019_64
HostPrefix=../../msvc2022_64
HostBinaries=bin
HostLibraries=lib
HostLibraryExecutables=./bin
HostData=../msvc2019_arm64
HostData=../msvc2022_arm64
Sysroot=
SysrootifyPrefix=false
TargetSpec=win32-arm64-msvc
HostSpec=
Documentation=../../Docs/Qt-6.7
Examples=../../Examples/Qt-6.7
Documentation=../../Docs/Qt-6.8
Examples=../../Examples/Qt-6.8