Skip to content

Commit

Permalink
SDL3 : Added SDL3 support, Linux-only for now until other platforms a…
Browse files Browse the repository at this point in the history
…re tested. SDL2 still supported.
  • Loading branch information
boberfly committed Feb 12, 2024
1 parent 896456a commit f263d73
Show file tree
Hide file tree
Showing 52 changed files with 2,358 additions and 34 deletions.
11 changes: 10 additions & 1 deletion app/app.pro
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
QT += core quick network quickcontrols2 svg
CONFIG += c++11

INCLUDEPATH += /usr/local/include/

unix:!macx {
TARGET = moonlight
} else {
Expand Down Expand Up @@ -66,7 +68,14 @@ macx {

unix:!macx {
CONFIG += link_pkgconfig
PKGCONFIG += openssl sdl2 SDL2_ttf opus
PKGCONFIG += openssl opus
packagesExist(sdl3) {
DEFINES += HAVE_SDL3
PKGCONFIG += sdl3 sdl3-ttf
}
else {
PKGCONFIG += sdl2 SDL2_ttf
}

!disable-ffmpeg {
packagesExist(libavcodec) {
Expand Down
34 changes: 34 additions & 0 deletions app/backend/systemproperties.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,14 +132,22 @@ void SystemProperties::querySdlVideoInfoInternal()
// We call the internal variant because we're already in a safe thread context.
refreshDisplaysInternal();

#if SDL_VERSION_ATLEAST(3, 0, 0)
SDL_Window* testWindow = SDL_CreateWindow("", 1280, 720,
#else
SDL_Window* testWindow = SDL_CreateWindow("", 0, 0, 1280, 720,
#endif
SDL_WINDOW_HIDDEN | StreamUtils::getPlatformWindowFlags());
if (!testWindow) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Failed to create test window with platform flags: %s",
SDL_GetError());

#if SDL_VERSION_ATLEAST(3, 0, 0)
testWindow = SDL_CreateWindow("", 1280, 720, SDL_WINDOW_HIDDEN);
#else
testWindow = SDL_CreateWindow("", 0, 0, 1280, 720, SDL_WINDOW_HIDDEN);
#endif
if (!testWindow) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Failed to create window for hardware decode test: %s",
Expand Down Expand Up @@ -197,7 +205,13 @@ void SystemProperties::refreshDisplaysInternal()
monitorNativeResolutions.clear();

SDL_DisplayMode bestMode;
#if SDL_VERSION_ATLEAST(3, 0, 0)
int numDisplays = 0;
SDL_DisplayID *displays = SDL_GetDisplays(&numDisplays);
for (int displayIndex = 0; displayIndex < numDisplays; displayIndex++) {
#else
for (int displayIndex = 0; displayIndex < SDL_GetNumVideoDisplays(); displayIndex++) {
#endif
SDL_DisplayMode desktopMode;

if (StreamUtils::getNativeDesktopMode(displayIndex, &desktopMode)) {
Expand All @@ -212,6 +226,20 @@ void SystemProperties::refreshDisplaysInternal()

// Start at desktop mode and work our way up
bestMode = desktopMode;
#if SDL_VERSION_ATLEAST(3, 0, 0)
int numModes = 0;
const SDL_DisplayMode **modes = SDL_GetFullscreenDisplayModes(displayIndex, &numModes);
for (int i = 0; i < numModes; i++) {
if (modes[i]->w == desktopMode.w && modes[i]->h == desktopMode.h) {
if (modes[i]->refresh_rate > bestMode.refresh_rate) {
bestMode = *modes[i];
}
}
}
if (modes) {
SDL_free(modes);
}
#else
for (int i = 0; i < SDL_GetNumDisplayModes(displayIndex); i++) {
SDL_DisplayMode mode;
if (SDL_GetDisplayMode(displayIndex, i, &mode) == 0) {
Expand All @@ -222,6 +250,7 @@ void SystemProperties::refreshDisplaysInternal()
}
}
}
#endif

// Try to normalize values around our our standard refresh rates.
// Some displays/OSes report values that are slightly off.
Expand All @@ -236,6 +265,11 @@ void SystemProperties::refreshDisplaysInternal()
}
}
}
#if SDL_VERSION_ATLEAST(3, 0, 0)
if (displays) {
SDL_free((void*)displays);
}
#endif

SDL_QuitSubSystem(SDL_INIT_VIDEO);
}
155 changes: 155 additions & 0 deletions app/gui/sdlgamepadkeynavigation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,21 @@ 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_VERSION_ATLEAST(3, 0, 0)
if (SDL_InitSubSystem(SDL_INIT_GAMEPAD) != 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_InitSubSystem(SDL_INIT_GAMEPAD) failed: %s",
SDL_GetError());
return;
}
#else
if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) != 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) failed: %s",
SDL_GetError());
return;
}
#endif

MappingManager mappingManager;
mappingManager.applyMappings();
Expand All @@ -51,6 +60,22 @@ void SdlGamepadKeyNavigation::enable()
// overlapping lifetimes of SdlGamepadKeyNavigation instances, so we
// will attach ourselves.
SDL_PumpEvents();
#if SDL_VERSION_ATLEAST(3, 0, 0)
SDL_FlushEvent(SDL_EVENT_GAMEPAD_ADDED);

// Open all currently attached game controllers
int numGamepads = 0;
SDL_JoystickID *gamepads = SDL_GetGamepads(&numGamepads);
for (int i = 0; i < numGamepads; i++) {
SDL_Gamepad* gp = SDL_OpenGamepad(gamepads[i]);
if (gp != nullptr) {
m_Gamepads.append(gp);
}
}
if (gamepads) {
SDL_free((void*)gamepads);
}
#else
SDL_FlushEvent(SDL_CONTROLLERDEVICEADDED);

// Open all currently attached game controllers
Expand All @@ -62,6 +87,7 @@ void SdlGamepadKeyNavigation::enable()
}
}
}
#endif

// Flush events on the first poll
m_FirstPoll = true;
Expand All @@ -80,12 +106,21 @@ void SdlGamepadKeyNavigation::disable()

m_PollingTimer->stop();

#if SDL_VERSION_ATLEAST(3, 0, 0)
while (!m_Gamepads.isEmpty()) {
SDL_CloseGamepad(m_Gamepads[0]);
m_Gamepads.removeAt(0);
}

SDL_QuitSubSystem(SDL_INIT_GAMEPAD);
#else
while (!m_Gamepads.isEmpty()) {
SDL_GameControllerClose(m_Gamepads[0]);
m_Gamepads.removeAt(0);
}

SDL_QuitSubSystem(SDL_INIT_GAMECONTROLLER);
#endif

m_Enabled = false;
}
Expand All @@ -99,14 +134,120 @@ void SdlGamepadKeyNavigation::onPollingTimerFired()
// stale input data from the stream session (like the quit combo).
if (m_FirstPoll) {
SDL_PumpEvents();
#if SDL_VERSION_ATLEAST(3, 0, 0)
SDL_FlushEvent(SDL_EVENT_GAMEPAD_BUTTON_DOWN);
SDL_FlushEvent(SDL_EVENT_GAMEPAD_BUTTON_UP);
#else
SDL_FlushEvent(SDL_CONTROLLERBUTTONDOWN);
SDL_FlushEvent(SDL_CONTROLLERBUTTONUP);
#endif

m_FirstPoll = false;
}

while (SDL_PollEvent(&event)) {
switch (event.type) {
#if SDL_VERSION_ATLEAST(3, 0, 0)
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_EVENT_GAMEPAD_BUTTON_DOWN:
case SDL_EVENT_GAMEPAD_BUTTON_UP:
{
QEvent::Type type =
event.type == SDL_EVENT_GAMEPAD_BUTTON_DOWN ?
QEvent::Type::KeyPress : QEvent::Type::KeyRelease;

// Swap face buttons if needed
if (prefs.swapFaceButtons) {
switch (event.button.button) {
case SDL_GAMEPAD_BUTTON_SOUTH:
event.button.button = SDL_GAMEPAD_BUTTON_EAST;
break;
case SDL_GAMEPAD_BUTTON_EAST:
event.button.button = SDL_GAMEPAD_BUTTON_SOUTH;
break;
case SDL_GAMEPAD_BUTTON_WEST:
event.button.button = SDL_GAMEPAD_BUTTON_NORTH;
break;
case SDL_GAMEPAD_BUTTON_NORTH:
event.button.button = SDL_GAMEPAD_BUTTON_WEST;
break;
}
}

switch (event.button.button) {
case SDL_GAMEPAD_BUTTON_DPAD_UP:
if (m_UiNavMode) {
// Back-tab
sendKey(type, Qt::Key_Tab, Qt::ShiftModifier);
}
else {
sendKey(type, Qt::Key_Up);
}
break;
case SDL_GAMEPAD_BUTTON_DPAD_DOWN:
if (m_UiNavMode) {
sendKey(type, Qt::Key_Tab);
}
else {
sendKey(type, Qt::Key_Down);
}
break;
case SDL_GAMEPAD_BUTTON_DPAD_LEFT:
sendKey(type, Qt::Key_Left);
break;
case SDL_GAMEPAD_BUTTON_DPAD_RIGHT:
sendKey(type, Qt::Key_Right);
break;
case SDL_GAMEPAD_BUTTON_SOUTH:
if (m_UiNavMode) {
sendKey(type, Qt::Key_Space);
}
else {
sendKey(type, Qt::Key_Return);
}
break;
case SDL_GAMEPAD_BUTTON_EAST:
sendKey(type, Qt::Key_Escape);
break;
case SDL_GAMEPAD_BUTTON_WEST:
sendKey(type, Qt::Key_Menu);
break;
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.
sendKey(type, Qt::Key_Hangup);
break;
default:
break;
}
break;
}
case SDL_EVENT_GAMEPAD_ADDED:
SDL_Gamepad* gp = SDL_OpenGamepad(event.jdevice.which);
if (gp != nullptr) {
// SDL_EVENT_GAMEPAD_ADDED can be reported multiple times for the same
// gamepad in rare cases, because SDL doesn't fixup the device index in
// the SDL_EVENT_GAMEPAD_ADDED event if an unopened gamepad disappears
// before we've processed the add event.
if (!m_Gamepads.contains(gp)) {
m_Gamepads.append(gp);
}
else {
// We already have this game controller open
SDL_CloseGamepad(gp);
}
}
break;
}
}
#else
case SDL_QUIT:
// SDL may send us a quit event since we initialize
// the video subsystem on startup. If we get one,
Expand Down Expand Up @@ -206,11 +347,18 @@ void SdlGamepadKeyNavigation::onPollingTimerFired()
break;
}
}
#endif

// Handle analog sticks by polling
#if SDL_VERSION_ATLEAST(3, 0, 0)
for (auto gp : m_Gamepads) {
short leftX = SDL_GetGamepadAxis(gp, SDL_GAMEPAD_AXIS_LEFTX);
short leftY = SDL_GetGamepadAxis(gp, SDL_GAMEPAD_AXIS_LEFTY);
#else
for (auto gc : m_Gamepads) {
short leftX = SDL_GameControllerGetAxis(gc, SDL_CONTROLLER_AXIS_LEFTX);
short leftY = SDL_GameControllerGetAxis(gc, SDL_CONTROLLER_AXIS_LEFTY);
#endif
if (SDL_GetTicks() - m_LastAxisNavigationEventTime < AXIS_NAVIGATION_REPEAT_DELAY) {
// Do nothing
}
Expand Down Expand Up @@ -272,11 +420,18 @@ int SdlGamepadKeyNavigation::getConnectedGamepads()
Q_ASSERT(m_Enabled);

int count = 0;
#if SDL_VERSION_ATLEAST(3, 0, 0)
SDL_JoystickID *gamepads = SDL_GetGamepads(&count);
if (gamepads) {
SDL_free((void*)gamepads);
}
#else
for (int i = 0; i < SDL_NumJoysticks(); i++) {
if (SDL_IsGameController(i)) {
count++;
}
}
#endif

return count;
}
8 changes: 8 additions & 0 deletions app/gui/sdlgamepadkeynavigation.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@
#include <QTimer>
#include <QEvent>

#if HAVE_SDL3
#include <SDL3/SDL.h>
#else
#include <SDL.h>
#endif

class SdlGamepadKeyNavigation : public QObject
{
Expand All @@ -30,7 +34,11 @@ private slots:

private:
QTimer* m_PollingTimer;
#if SDL_VERSION_ATLEAST(3, 0, 0)
QList<SDL_Gamepad*> m_Gamepads;
#else
QList<SDL_GameController*> m_Gamepads;
#endif
bool m_Enabled;
bool m_UiNavMode;
bool m_FirstPoll;
Expand Down
5 changes: 5 additions & 0 deletions app/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@
// doing the same thing. This needs to be before any headers
// that might include SDL.h themselves.
#define SDL_MAIN_HANDLED
#if HAVE_SDL3
#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
#else
#include <SDL.h>
#endif

#ifdef HAVE_FFMPEG
#include "streaming/video/ffmpeg.h"
Expand Down
4 changes: 4 additions & 0 deletions app/masterhook.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@
// redirection that happens when _FILE_OFFSET_BITS=64!
// See masterhook_internal.c for details.

#if HAVE_SDL3
#include <SDL3/SDL.h>
#else
#include <SDL.h>
#endif
#include <dlfcn.h>
#include <unistd.h>
#include <errno.h>
Expand Down
4 changes: 4 additions & 0 deletions app/masterhook_internal.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@

#define _GNU_SOURCE

#if HAVE_SDL3
#include <SDL3/SDL.h>
#else
#include <SDL.h>
#endif
#include <dlfcn.h>
#include <fcntl.h>
#include <unistd.h>
Expand Down
Loading

0 comments on commit f263d73

Please sign in to comment.