Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
231a67946d | ||
|
|
5290305944 | ||
|
|
3a22a01aef | ||
|
|
d1f3b98a1f | ||
|
|
a37020b974 | ||
|
|
13b2b28c2f | ||
|
|
dd2a99a96b | ||
|
|
edd7a134d8 | ||
|
|
ffa87c5f01 | ||
|
|
15e337fff8 | ||
|
|
707dd3cb83 | ||
|
|
98f6a09991 | ||
|
|
103f988dbf | ||
|
|
2257cb0cef | ||
|
|
b6a3369243 | ||
|
|
4af9623727 | ||
|
|
515db03fe5 | ||
|
|
e44d097683 | ||
|
|
9936085aee | ||
|
|
3279d9c3f6 | ||
|
|
3531fe0a4f | ||
|
|
6ce02616f0 | ||
|
|
13880353d8 | ||
|
|
ec69dad8d7 | ||
|
|
72ae324d71 | ||
|
|
901cbd255c | ||
|
|
2a63ad53d7 | ||
|
|
9b3d4c1ad7 | ||
|
|
054e334066 | ||
|
|
6d023c2dfa | ||
|
|
0e2d5bf441 | ||
|
|
023b6b2772 | ||
|
|
6f39d120cb | ||
|
|
9cf305865b | ||
|
|
6b11f43302 | ||
|
|
5a1ef55767 | ||
|
|
76deafbd7b |
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
#include <QDir>
|
||||
|
||||
#include <SDL.h>
|
||||
#include "SDL_compat.h"
|
||||
|
||||
#define SER_GAMEPADMAPPING "gcmapping"
|
||||
|
||||
|
||||
@@ -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,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "renderer.h"
|
||||
#include <SDL.h>
|
||||
#include "SDL_compat.h"
|
||||
|
||||
class SdlAudioRenderer : public IAudioRenderer
|
||||
{
|
||||
|
||||
@@ -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,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>
|
||||
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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, ¤tMode, 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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include <QString>
|
||||
|
||||
#include <SDL.h>
|
||||
#include "SDL_compat.h"
|
||||
#include <SDL_ttf.h>
|
||||
|
||||
namespace Overlay {
|
||||
|
||||
@@ -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
@@ -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
@@ -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,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,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" %*
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user