diff --git a/Makefile.common b/Makefile.common index 73152664bbc8..1cffd12c6561 100644 --- a/Makefile.common +++ b/Makefile.common @@ -1664,7 +1664,14 @@ endif endif -ifeq ($(HAVE_SDL2), 1) +ifeq ($(HAVE_SDL3), 1) + SDL3_CFLAGS = $(shell pkg-config sdl3 --cflags) + SDL3_LIBS = $(shell pkg-config sdl3 --libs) + DEF_FLAGS += $(SDL3_CFLAGS) + LIBS += $(SDL3_LIBS) + DEFINES += -DHAVE_SDL3 + OBJ += input/drivers_joypad/sdl3_joypad.o +else ifeq ($(HAVE_SDL2), 1) HAVE_SDL_COMMON = 1 OBJ += gfx/drivers/sdl2_gfx.o \ gfx/common/sdl2_common.o diff --git a/Makefile.win b/Makefile.win index ec1b9dfb692d..cd33af41feaa 100644 --- a/Makefile.win +++ b/Makefile.win @@ -34,6 +34,7 @@ HAVE_WINMM = 1 HAVE_SDL := 0 HAVE_SDL2 := 0 +HAVE_SDL3 := 0 HAVE_RSOUND := 0 HAVE_STB_FONT := 1 @@ -64,6 +65,11 @@ SDL2_LIBS := -lSDL2 SDL2_CFLAGS := -ISDL2 -DHAVE_SDL2 endif +ifeq ($(HAVE_SDL3), 1) +SDL3_LIBS := -lSDL3 +SDL3_CFLAGS := -ISDL3 -DHAVE_SDL3 +endif + ifeq ($(HAVE_D3D8), 1) D3D8_LIBS := -ld3d8 ifeq ($(HAVE_D3DX), 1) diff --git a/config.features.h b/config.features.h index 8e022251f3cd..21be83a98335 100644 --- a/config.features.h +++ b/config.features.h @@ -62,6 +62,12 @@ #define SUPPORTS_SDL2 false #endif +#ifdef HAVE_SDL3 +#define SUPPORTS_SDL3 true +#else +#define SUPPORTS_SDL3 false +#endif + #ifdef HAVE_THREADS #define SUPPORTS_THREAD true #else diff --git a/configuration.c b/configuration.c index 58c0437c16c7..220f81a9a3ee 100644 --- a/configuration.c +++ b/configuration.c @@ -1290,7 +1290,9 @@ const char *config_get_default_joypad(void) case JOYPAD_ANDROID: return "android"; case JOYPAD_SDL: -#ifdef HAVE_SDL2 +#ifdef HAVE_SDL3 + return "sdl3"; +#elif defined(HAVE_SDL2) return "sdl2"; #else return "sdl"; diff --git a/griffin/griffin.c b/griffin/griffin.c index 8b84bc0ad3d6..b82ff47bb3b7 100644 --- a/griffin/griffin.c +++ b/griffin/griffin.c @@ -894,7 +894,9 @@ AUDIO #include "../audio/drivers/xaudio.c" #endif -#if defined(HAVE_SDL2) +#if defined(HAVE_SDL3) +#include "../input/drivers_joypad/sdl3_joypad.c" +#elif defined(HAVE_SDL2) #include "../audio/drivers/sdl_audio.c" #include "../input/drivers/sdl_input.c" #include "../input/drivers_joypad/sdl_joypad.c" diff --git a/input/drivers_joypad/sdl3_joypad.c b/input/drivers_joypad/sdl3_joypad.c new file mode 100644 index 000000000000..2dfe44db5791 --- /dev/null +++ b/input/drivers_joypad/sdl3_joypad.c @@ -0,0 +1,543 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * Copyright (C) 2011-2017 - Daniel De Matteis + * Copyright (C) 2014-2017 - Higor Euripedes + * Copyright (C) 2023 - Carlo Refice + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include +#include + +#include + +#include "../input_driver.h" + +#include "../../tasks/tasks_internal.h" +#include "../../verbosity.h" + +typedef struct _sdl3_joypad +{ + SDL_Joystick *joypad; + SDL_Gamepad *gamepad; + SDL_JoystickID jid; /* 0 = Invalid ID */ + unsigned num_axes; + unsigned num_buttons; + unsigned num_hats; +} sdl3_joypad_t; + +/* TODO/FIXME - static globals */ +static sdl3_joypad_t sdl3_joypads[MAX_USERS]; + +static const char *sdl3_joypad_name(unsigned pad) +{ + if (pad >= MAX_USERS || !sdl3_joypads[pad].jid) + return NULL; + + if (sdl3_joypads[pad].gamepad) + return SDL_GetGamepadName(sdl3_joypads[pad].gamepad); + else if (sdl3_joypads[pad].joypad) + return SDL_GetJoystickName(sdl3_joypads[pad].joypad); + return NULL; +} + +static uint8_t sdl3_joypad_get_button(sdl3_joypad_t *pad, unsigned button) +{ + if (pad->gamepad) + return (uint8_t)SDL_GetGamepadButton(pad->gamepad, (SDL_GamepadButton)button); + else if (pad->joypad) + return (uint8_t)SDL_GetJoystickButton(pad->joypad, button); + return 0; +} + +static uint8_t sdl3_joypad_get_hat(sdl3_joypad_t *pad, unsigned hat) +{ + /* Gamepads always have num_hats=0; this is only reached for raw joysticks. */ + return SDL_GetJoystickHat(pad->joypad, hat); +} + +static int16_t sdl3_joypad_get_axis(sdl3_joypad_t *pad, unsigned axis) +{ + if (pad->gamepad) + return SDL_GetGamepadAxis(pad->gamepad, (SDL_GamepadAxis)axis); + else if (pad->joypad) + return SDL_GetJoystickAxis(pad->joypad, (int)axis); + return 0; +} + +static void sdl3_joypad_connect(SDL_JoystickID jid) +{ + int i; + int slot = -1; + int32_t vendor = 0; + int32_t product = 0; + sdl3_joypad_t *pad = NULL; + SDL_Gamepad *gamepad = NULL; + SDL_Joystick *joypad = NULL; + + /* Connect to the device. */ + if (SDL_IsGamepad(jid)) + { + gamepad = SDL_OpenGamepad(jid); + if (gamepad) + joypad = SDL_GetGamepadJoystick(gamepad); + + if (!gamepad || !joypad) + { + RARCH_ERR("[SDL3] Couldn't open gamepad %" SDL_PRIu32 ": %s.\n", jid, SDL_GetError()); + if (gamepad) + SDL_CloseGamepad(gamepad); + return; + } + } + else + { + joypad = SDL_OpenJoystick(jid); + if (!joypad) + { + RARCH_ERR("[SDL3] Couldn't open joystick %" SDL_PRIu32 ": %s.\n", jid, SDL_GetError()); + return; + } + } + + /* Gamepads allow restoring the player index, so re-use that for the slot if possible. */ + if (gamepad) + { + int player = SDL_GetGamepadPlayerIndex(gamepad); + if (player >= 0 && player < MAX_USERS && !sdl3_joypads[player].jid) + slot = player; + } + + /* Fallback to the first free slot. */ + if (slot < 0) + { + for (i = 0; i < MAX_USERS; i++) + { + if (!sdl3_joypads[i].jid) + { + slot = i; + break; + } + } + } + + /* Fail if a slot is still not found. */ + if (slot < 0) + { + RARCH_WARN("[SDL3] No free joypad slots for joystick %" SDL_PRIu32 ".\n", jid); + if (gamepad) + SDL_CloseGamepad(gamepad); + else + SDL_CloseJoystick(joypad); + return; + } + + pad = &sdl3_joypads[slot]; + pad->jid = jid; + pad->gamepad = gamepad; + pad->joypad = joypad; + + if (gamepad) + { + vendor = SDL_GetGamepadVendor(gamepad); + product = SDL_GetGamepadProduct(gamepad); + + /* Ensure the player index matches the slot. */ + if (SDL_GetGamepadPlayerIndex(gamepad) != slot) { + SDL_SetGamepadPlayerIndex(gamepad, (int)slot); + } + } + else + { + vendor = SDL_GetJoystickVendor(joypad); + product = SDL_GetJoystickProduct(joypad); + } + + input_autoconfigure_connect( + sdl3_joypad_name(slot), + NULL, NULL, + sdl_joypad.ident, + slot, + vendor, + product); + + if (gamepad) + { + /* SDL_Gamepad internally supports all axis/button IDs, even if + * the controller's mapping does not have a binding for it. + * + * So, we can claim to support all axes/buttons, and when we try to poll + * an unbound ID, SDL simply returns the correct unpressed value. + * + * Note that, in addition to 0 trackballs, we also have 0 hats. This is + * because the d-pad is in the button list, as the last 4 enum entries. + * + * -flibit + */ + pad->num_axes = SDL_GAMEPAD_AXIS_COUNT; + pad->num_buttons = SDL_GAMEPAD_BUTTON_COUNT; + pad->num_hats = 0; + } + else + { + pad->num_axes = SDL_GetNumJoystickAxes(joypad); + pad->num_buttons = SDL_GetNumJoystickButtons(joypad); + pad->num_hats = SDL_GetNumJoystickHats(joypad); + } +} + +static void sdl3_joypad_disconnect(SDL_JoystickID jid) +{ + for (int i = 0; i < MAX_USERS; i++) + { + if (sdl3_joypads[i].jid != jid) + continue; + + if (sdl3_joypads[i].gamepad) + SDL_CloseGamepad(sdl3_joypads[i].gamepad); + else if (sdl3_joypads[i].joypad) + SDL_CloseJoystick(sdl3_joypads[i].joypad); + + input_autoconfigure_disconnect(i, sdl_joypad.ident); + + memset(&sdl3_joypads[i], 0, sizeof(sdl3_joypads[i])); + return; + } +} + +static void sdl3_joypad_destroy(void) +{ + for (int i = 0; i < MAX_USERS; i++) + { + if (sdl3_joypads[i].gamepad) + SDL_CloseGamepad(sdl3_joypads[i].gamepad); + else if (sdl3_joypads[i].joypad) + SDL_CloseJoystick(sdl3_joypads[i].joypad); + } + + memset(sdl3_joypads, 0, sizeof(sdl3_joypads)); + SDL_QuitSubSystem(SDL_INIT_GAMEPAD); +} + +static void *sdl3_joypad_init(void *data) +{ + int i, count = 0; + SDL_InitFlags sdl_subsystem_flags = SDL_WasInit(0); + + if (sdl_subsystem_flags == 0) + { + if (!SDL_Init(SDL_INIT_GAMEPAD)) + return NULL; + } + else if ((sdl_subsystem_flags & SDL_INIT_GAMEPAD) == 0) + { + if (!SDL_InitSubSystem(SDL_INIT_GAMEPAD)) + return NULL; + } + + memset(sdl3_joypads, 0, sizeof(sdl3_joypads)); + + /* Joystick connected events are triggered after load, so no need to initialize them here. */ + + return (void*)-1; +} + +static int32_t sdl3_joypad_button_state(sdl3_joypad_t *pad, uint16_t joykey) +{ + unsigned hat_dir = GET_HAT_DIR(joykey); + + if (hat_dir) + { + uint8_t dir; + uint16_t hat = GET_HAT(joykey); + + if (hat >= pad->num_hats) + return 0; + + dir = sdl3_joypad_get_hat(pad, hat); + + switch (hat_dir) + { + case HAT_UP_MASK: + return (dir & SDL_HAT_UP); + case HAT_DOWN_MASK: + return (dir & SDL_HAT_DOWN); + case HAT_LEFT_MASK: + return (dir & SDL_HAT_LEFT); + case HAT_RIGHT_MASK: + return (dir & SDL_HAT_RIGHT); + default: + break; + } + /* hat requested and no hat button down */ + } + else if (joykey < pad->num_buttons) + return sdl3_joypad_get_button(pad, joykey); + + return 0; +} + +static int32_t sdl3_joypad_button(unsigned port, uint16_t joykey) +{ + if (port >= MAX_USERS) + return 0; + + if (!sdl3_joypads[port].joypad) + return 0; + + return sdl3_joypad_button_state(&sdl3_joypads[port], joykey); +} + +static int16_t sdl3_joypad_axis_state(sdl3_joypad_t *pad, uint32_t joyaxis) +{ + if (AXIS_NEG_GET(joyaxis) < pad->num_axes) + { + int16_t val = sdl3_joypad_get_axis(pad, AXIS_NEG_GET(joyaxis)); + if (val < 0) + { + /* Clamp - -0x8000 can cause trouble if we later abs() it. */ + if (val < -0x7fff) + return -0x7fff; + return val; + } + } + else if (AXIS_POS_GET(joyaxis) < pad->num_axes) + { + int16_t val = sdl3_joypad_get_axis(pad, AXIS_POS_GET(joyaxis)); + if (val > 0) + return val; + } + + return 0; +} + +static int16_t sdl3_joypad_axis(unsigned port, uint32_t joyaxis) +{ + if (port >= MAX_USERS) + return 0; + + if (!sdl3_joypads[port].joypad) + return 0; + + return sdl3_joypad_axis_state(&sdl3_joypads[port], joyaxis); +} + +static int16_t sdl3_joypad_state( + rarch_joypad_info_t *joypad_info, + const struct retro_keybind *binds, + unsigned port) +{ + int i; + int16_t ret = 0; + uint16_t port_idx = joypad_info->joy_idx; + sdl3_joypad_t *pad; + + if (port_idx >= MAX_USERS) + return 0; + + pad = &sdl3_joypads[port_idx]; + if (!pad->joypad) + return 0; + + for (i = 0; i < RARCH_FIRST_CUSTOM_BIND; i++) + { + /* Auto-binds are per joypad, not per user. */ + const uint64_t joykey = (binds[i].joykey != NO_BTN) + ? binds[i].joykey : joypad_info->auto_binds[i].joykey; + const uint32_t joyaxis = (binds[i].joyaxis != AXIS_NONE) + ? binds[i].joyaxis : joypad_info->auto_binds[i].joyaxis; + + if ( + (uint16_t)joykey != NO_BTN + && sdl3_joypad_button_state(pad, (uint16_t)joykey) + ) + ret |= (1 << i); + else if (joyaxis != AXIS_NONE && + ((float)abs(sdl3_joypad_axis_state(pad, joyaxis)) + / 0x8000) > joypad_info->axis_threshold) + ret |= (1 << i); + } + + return ret; +} + +static void sdl3_joypad_poll(void) +{ + SDL_Event event; + + SDL_PumpEvents(); + + while (SDL_PeepEvents(&event, 1, SDL_GETEVENT, + SDL_EVENT_JOYSTICK_ADDED, SDL_EVENT_JOYSTICK_REMOVED) > 0) + { + switch (event.type) + { + case SDL_EVENT_JOYSTICK_ADDED: + sdl3_joypad_connect(event.jdevice.which); + break; + case SDL_EVENT_JOYSTICK_REMOVED: + sdl3_joypad_disconnect(event.jdevice.which); + break; + } + } + + SDL_UpdateGamepads(); + /* Discard all remaining joystick/gamepad input events (axis, button, hat, + * etc.) - we sample state directly via SDL_GetGamepadAxis / SDL_GetJoystickButton + * and don't need the event copies piling up in the queue. */ + SDL_FlushEvents(SDL_EVENT_JOYSTICK_AXIS_MOTION, SDL_EVENT_GAMEPAD_REMAPPED); +} + +static bool sdl3_joypad_set_rumble(unsigned pad, + enum retro_rumble_effect effect, uint16_t strength) +{ + uint16_t low = 0; + uint16_t high = 0; + + if (pad >= MAX_USERS) + return false; + + switch (effect) + { + case RETRO_RUMBLE_STRONG: + low = strength; + break; + case RETRO_RUMBLE_WEAK: + high = strength; + break; + default: + return false; + } + + if (sdl3_joypads[pad].gamepad) + return SDL_RumbleGamepad(sdl3_joypads[pad].gamepad, low, high, 5000); + else if (sdl3_joypads[pad].joypad) + return SDL_RumbleJoystick(sdl3_joypads[pad].joypad, low, high, 5000); + + return false; +} + +/** + * Enables or disables a sensor on the specified gamepad. + * + * @param pad Index of the gamepad. + * @param action Sensor action to perform (enable/disable gyroscope or accelerometer). + * @param rate Requested sensor update rate (unused). + * @return true if the sensor state was set successfully, false otherwise. + */ +static bool sdl3_joypad_set_sensor_state(unsigned pad, + enum retro_sensor_action action, unsigned rate) +{ + if (pad >= MAX_USERS) + return false; + + if (!sdl3_joypads[pad].gamepad) + return false; + + switch (action) + { + case RETRO_SENSOR_GYROSCOPE_ENABLE: + case RETRO_SENSOR_GYROSCOPE_DISABLE: + if (SDL_GamepadHasSensor(sdl3_joypads[pad].gamepad, SDL_SENSOR_GYRO)) + return SDL_SetGamepadSensorEnabled(sdl3_joypads[pad].gamepad, SDL_SENSOR_GYRO, + action == RETRO_SENSOR_GYROSCOPE_ENABLE); + return false; + + case RETRO_SENSOR_ACCELEROMETER_ENABLE: + case RETRO_SENSOR_ACCELEROMETER_DISABLE: + if (SDL_GamepadHasSensor(sdl3_joypads[pad].gamepad, SDL_SENSOR_ACCEL)) + return SDL_SetGamepadSensorEnabled(sdl3_joypads[pad].gamepad, SDL_SENSOR_ACCEL, + action == RETRO_SENSOR_ACCELEROMETER_ENABLE); + return false; + + default: + return false; + } +} + +/** + * Retrieves input data from a connected sensor device, such as a gyroscope or accelerometer. + * + * @return True if the sensor input was successfully handled by this function, false otherwise. + */ +static bool sdl3_joypad_get_sensor_input(unsigned pad, unsigned id, float *value) +{ + SDL_SensorType sensor_type; + float sensor_data[3]; + + if (pad >= MAX_USERS) + return false; + + if (!sdl3_joypads[pad].gamepad) + return false; + + if ((id >= RETRO_SENSOR_ACCELEROMETER_X) && (id <= RETRO_SENSOR_ACCELEROMETER_Z)) + sensor_type = SDL_SENSOR_ACCEL; + else if ((id >= RETRO_SENSOR_GYROSCOPE_X) && (id <= RETRO_SENSOR_GYROSCOPE_Z)) + sensor_type = SDL_SENSOR_GYRO; + else + return false; + + if (!SDL_GetGamepadSensorData(sdl3_joypads[pad].gamepad, sensor_type, sensor_data, 3)) + return false; + + switch (id) + { + case RETRO_SENSOR_ACCELEROMETER_X: + *value = sensor_data[0] / SDL_STANDARD_GRAVITY; + break; + case RETRO_SENSOR_ACCELEROMETER_Y: + *value = sensor_data[2] / SDL_STANDARD_GRAVITY; + break; + case RETRO_SENSOR_ACCELEROMETER_Z: + *value = sensor_data[1] / SDL_STANDARD_GRAVITY; + break; + case RETRO_SENSOR_GYROSCOPE_X: + *value = sensor_data[0]; + break; + case RETRO_SENSOR_GYROSCOPE_Y: + *value = -sensor_data[2]; + break; + case RETRO_SENSOR_GYROSCOPE_Z: + *value = sensor_data[1]; + break; + } + + return true; +} + +static bool sdl3_joypad_query_pad(unsigned pad) +{ + if (pad >= MAX_USERS || !sdl3_joypads[pad].joypad) + return false; + if (sdl3_joypads[pad].gamepad) + return SDL_GamepadConnected(sdl3_joypads[pad].gamepad); + return SDL_JoystickConnected(sdl3_joypads[pad].joypad); +} + +input_device_driver_t sdl_joypad = { + sdl3_joypad_init, + sdl3_joypad_query_pad, + sdl3_joypad_destroy, + sdl3_joypad_button, + sdl3_joypad_state, + NULL, /* get_buttons */ + sdl3_joypad_axis, + sdl3_joypad_poll, + sdl3_joypad_set_rumble, + NULL, /* set_rumble_gain */ + sdl3_joypad_set_sensor_state, + sdl3_joypad_get_sensor_input, + sdl3_joypad_name, + "sdl3", /* TODO: Rename all the SDL Controller Drivers to "sdl"? You can't use them at the same time anyway, and it will fix autoconfigs. */ +}; diff --git a/input/input_autodetect_builtin.c b/input/input_autodetect_builtin.c index dc5ca1dea273..4d732d9fbb2b 100644 --- a/input/input_autodetect_builtin.c +++ b/input/input_autodetect_builtin.c @@ -32,11 +32,16 @@ #include #endif +#ifdef HAVE_SDL3 +#include +#endif + #define DECL_BTN(btn, bind) "input_" #btn "_btn = " #bind "\n" #define DECL_BTN_EX(btn, bind, name) "input_" #btn "_btn = " #bind "\ninput_" #btn "_btn_label = \"" name "\"\n" #define DECL_AXIS(axis, bind) "input_" #axis "_axis = " #bind "\n" #define DECL_AXIS_EX(axis, bind, name) "input_" #axis "_axis = " #bind "\ninput_" #axis "_axis_label = \"" name "\"\n" #define DECL_MENU(btn) "input_menu_toggle_btn = " #btn "\n" +#define DECL_MENU_EX(btn, name) "input_menu_toggle_btn = " #btn "\ninput_menu_toggle_btn_label = \"" name "\"\n" #define DECL_AUTOCONF_DEVICE(device, driver, binds) "input_device = \"" device "\"\ninput_driver = \"" driver "\"\n" binds #define DECL_AUTOCONF_PID(pid, vid, driver, binds) "input_product_id = " #pid "\ninput_vendor_id = " #vid "\ninput_driver = \"" driver "\"\n" binds @@ -66,6 +71,33 @@ DECL_AXIS(r_x_minus, -2) \ DECL_AXIS(r_y_plus, -3) \ DECL_AXIS(r_y_minus, +3) +#define SDL3_DEFAULT_BINDS \ +DECL_BTN_EX(a, SDL_GAMEPAD_BUTTON_EAST, "Right Face Button") \ +DECL_BTN_EX(b, SDL_GAMEPAD_BUTTON_SOUTH, "Bottom Face Button") \ +DECL_BTN_EX(x, SDL_GAMEPAD_BUTTON_NORTH, "Top Face Button") \ +DECL_BTN_EX(y, SDL_GAMEPAD_BUTTON_WEST, "Left Face Button") \ +DECL_BTN_EX(select, SDL_GAMEPAD_BUTTON_BACK, "Back") \ +DECL_BTN_EX(start, SDL_GAMEPAD_BUTTON_START, "Start") \ +DECL_BTN_EX(up, SDL_GAMEPAD_BUTTON_DPAD_UP, "D-Pad Up") \ +DECL_BTN_EX(down, SDL_GAMEPAD_BUTTON_DPAD_DOWN, "D-Pad Down") \ +DECL_BTN_EX(left, SDL_GAMEPAD_BUTTON_DPAD_LEFT, "D-Pad Left") \ +DECL_BTN_EX(right, SDL_GAMEPAD_BUTTON_DPAD_RIGHT, "D-Pad Right") \ +DECL_BTN_EX(l, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, "Left Shoulder") \ +DECL_BTN_EX(r, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, "Right Shoulder") \ +DECL_AXIS_EX(l2, +SDL_GAMEPAD_AXIS_LEFT_TRIGGER, "Left Trigger") \ +DECL_AXIS_EX(r2, +SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, "Right Trigger") \ +DECL_BTN_EX(l3, SDL_GAMEPAD_BUTTON_LEFT_STICK, "Left Stick") \ +DECL_BTN_EX(r3, SDL_GAMEPAD_BUTTON_RIGHT_STICK, "Right Stick") \ +DECL_AXIS_EX(l_x_plus, +SDL_GAMEPAD_AXIS_LEFTX, "Left Thumbstick X+") \ +DECL_AXIS_EX(l_x_minus, -SDL_GAMEPAD_AXIS_LEFTX, "Left Thumbstick X-") \ +DECL_AXIS_EX(l_y_plus, +SDL_GAMEPAD_AXIS_LEFTY, "Left Thumbstick Y+") \ +DECL_AXIS_EX(l_y_minus, -SDL_GAMEPAD_AXIS_LEFTY, "Left Thumbstick Y-") \ +DECL_AXIS_EX(r_x_plus, +SDL_GAMEPAD_AXIS_RIGHTX, "Right Thumbstick X+") \ +DECL_AXIS_EX(r_x_minus, -SDL_GAMEPAD_AXIS_RIGHTX, "Right Thumbstick X-") \ +DECL_AXIS_EX(r_y_plus, -SDL_GAMEPAD_AXIS_RIGHTY, "Right Thumbstick Y+") \ +DECL_AXIS_EX(r_y_minus, +SDL_GAMEPAD_AXIS_RIGHTY, "Right Thumbstick Y-") \ +DECL_MENU_EX(SDL_GAMEPAD_BUTTON_GUIDE, "Guide") + #if defined(DINGUX) && defined(HAVE_SDL_DINGUX) #define DINGUX_SDL_DEFAULT_BINDS \ DECL_BTN_EX(a, 8, "A") \ @@ -720,6 +752,9 @@ const char* const input_builtin_autoconfs[] = #ifdef HAVE_SDL2 DECL_AUTOCONF_DEVICE("Standard Gamepad", "sdl2", SDL2_DEFAULT_BINDS), #endif +#ifdef HAVE_SDL3 + DECL_AUTOCONF_DEVICE("Gamepad", "sdl3", SDL3_DEFAULT_BINDS), +#endif #if defined(DINGUX) && defined(HAVE_SDL_DINGUX) DECL_AUTOCONF_DEVICE("Dingux Gamepad", "sdl_dingux", DINGUX_SDL_DEFAULT_BINDS), #endif diff --git a/input/input_driver.c b/input/input_driver.c index 365e3746723b..9afef6956529 100644 --- a/input/input_driver.c +++ b/input/input_driver.c @@ -272,7 +272,7 @@ input_device_driver_t *joypad_drivers[] = { #ifdef ANDROID &android_joypad, #endif -#if defined(HAVE_SDL) || defined(HAVE_SDL2) +#if defined(HAVE_SDL3) || defined(HAVE_SDL) || defined(HAVE_SDL2) &sdl_joypad, #endif #if defined(DINGUX) && defined(HAVE_SDL_DINGUX) @@ -591,7 +591,7 @@ const input_device_driver_t *input_joypad_init_driver( } } /* Fall back to first available driver */ - return input_joypad_init_first(data); + return input_joypad_init_first(data); } static bool input_driver_button_combo_hold( @@ -3006,7 +3006,7 @@ void input_overlay_load_active( /** * input_overlay_next_move_touch_masks * @ol : Overlay handle. - * + * * Finds similar descs in the next overlay (i.e. same location and type) * and moves touch masks from active overlay to next. */ @@ -6167,14 +6167,14 @@ static void input_keys_pressed( else input_st->flags |= INP_FLAG_BLOCK_HOTKEY; } - + #ifdef HAVE_MENU /* Prevent triggering menu actions after binding */ if ( !(input_st->flags & INP_FLAG_MENU_PRESS_PENDING) && menu_state_get_ptr()->input_driver_flushing_input) input_st->flags |= INP_FLAG_WAIT_INPUT_RELEASE; #endif - + /* Check libretro input if emulated device type is active, * except device type must be always active in menu. */ if ( !(input_st->flags & INP_FLAG_BLOCK_LIBRETRO_INPUT) diff --git a/input/input_driver.h b/input/input_driver.h index 2cd9a23e0a85..c766e659b84c 100644 --- a/input/input_driver.h +++ b/input/input_driver.h @@ -1253,7 +1253,7 @@ extern input_device_driver_t linuxraw_joypad; extern input_device_driver_t parport_joypad; extern input_device_driver_t udev_joypad; extern input_device_driver_t xinput_joypad; -extern input_device_driver_t sdl_joypad; +extern input_device_driver_t sdl_joypad; /* SDL2 or SDL3. @see sdl_joypad.c, sdl3_joypad.c. */ extern input_device_driver_t sdl_dingux_joypad; extern input_device_driver_t ps4_joypad; extern input_device_driver_t ps3_joypad; diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index a872c3e78966..6e803d0e17d6 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -408,7 +408,7 @@ static int filebrowser_parse( ? FILE_TYPE_VIDEO_FONT : (enum msg_file_type)type_default; - if ( type == DISPLAYLIST_CORES_DETECTED + if ( type == DISPLAYLIST_CORES_DETECTED && path_is_compressed_file(file_path)) file_type = FILE_TYPE_CARCHIVE; break; @@ -631,7 +631,7 @@ static int menu_displaylist_parse_core_info( core_info = core_info_menu; } - if ( !core_info + if ( !core_info || !(core_info->flags & CORE_INFO_FLAG_HAS_INFO)) { if (menu_entries_append(list, @@ -2390,6 +2390,9 @@ static unsigned menu_displaylist_parse_system_info(file_list_t *list) #ifdef HAVE_SDL2 {SUPPORTS_SDL2, "SDL 2"}, #endif +#ifdef HAVE_SDL3 + {SUPPORTS_SDL3, "SDL 3"}, +#endif #ifdef HAVE_X11 {SUPPORTS_X11, "X11"}, #endif @@ -5059,7 +5062,7 @@ static unsigned menu_displaylist_parse_content_information( if (core_info_find(core_path, &core_info)) { - core_supports_no_game = (core_info->flags + core_supports_no_game = (core_info->flags & CORE_INFO_FLAG_SUPPORTS_NO_GAME); if (!string_is_empty(core_info->display_name)) strlcpy(core_name, core_info->display_name, sizeof(core_name)); @@ -10084,7 +10087,7 @@ unsigned menu_displaylist_build_list( MENU_ENUM_LABEL_VIDEO_HDR_SCANLINES, PARSE_ONLY_BOOL, false) == 0) count++; - + if(settings->bools.video_hdr_scanlines) { if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list, diff --git a/pkg/apple/RetroArch_Metal.xcodeproj/project.pbxproj b/pkg/apple/RetroArch_Metal.xcodeproj/project.pbxproj index 09ca583db37f..267474b68367 100644 --- a/pkg/apple/RetroArch_Metal.xcodeproj/project.pbxproj +++ b/pkg/apple/RetroArch_Metal.xcodeproj/project.pbxproj @@ -444,6 +444,7 @@ 05C5D58220E3DD0900654EE4 /* input_autodetect_builtin.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = input_autodetect_builtin.c; sourceTree = ""; }; 05C5D58320E3DD0900654EE4 /* input_keymaps.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = input_keymaps.c; sourceTree = ""; }; 05C5D58720E3DD0900654EE4 /* sdl_joypad.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = sdl_joypad.c; sourceTree = ""; }; + 05C5D58720E3DD0900654EE4 /* sdl3_joypad.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = sdl3_joypad.c; sourceTree = ""; }; 05C5D58D20E3DD0900654EE4 /* mfi_joypad.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = mfi_joypad.m; sourceTree = ""; }; 05C5D59820E3DD0A00654EE4 /* hid_joypad.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = hid_joypad.c; sourceTree = ""; }; 05C5D5A320E3DD0A00654EE4 /* input_driver.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = input_driver.c; sourceTree = ""; }; diff --git a/qb/config.libs.sh b/qb/config.libs.sh index 5c78baa8914f..294dce6f6436 100644 --- a/qb/config.libs.sh +++ b/qb/config.libs.sh @@ -273,7 +273,13 @@ check_val '' PIPEWIRE -lpipewire-0.3 '' libpipewire-0.3 '' '' false check_val '' PIPEWIRE_STABLE -lpipewire-0.3 '' libpipewire-0.3 1.0.0 '' false check_val '' SDL -lSDL SDL sdl 1.2.10 '' true check_val '' SDL2 -lSDL2 SDL2 sdl2 2.0.0 '' true +check_val '' SDL3 -lSDL3 SDL3 sdl3 3.2.20 '' true +if [ "$HAVE_SDL3" = 'yes' ] && { [ "$HAVE_SDL2" = 'yes' ] || [ "$HAVE_SDL" = 'yes' ]; }; then + die : 'Notice: SDL drivers will be replaced by SDL3 ones.' + HAVE_SDL=no + HAVE_SDL2=no +fi if [ "$HAVE_SDL2" = 'yes' ] && [ "$HAVE_SDL" = 'yes' ]; then die : 'Notice: SDL drivers will be replaced by SDL2 ones.' HAVE_SDL=no diff --git a/qb/config.params.sh b/qb/config.params.sh index e58f0460a5c2..5893e2bd3b3d 100644 --- a/qb/config.params.sh +++ b/qb/config.params.sh @@ -48,6 +48,7 @@ HAVE_DYNAMIC=yes # Dynamic loading of libretro library HAVE_SDL=auto # SDL support C89_SDL=no HAVE_SDL2=auto # SDL2 support (disables SDL 1.x) +HAVE_SDL3=auto # SDL3 support (disables SDL 1.x and SDL 2.x) C89_SDL2=no HAVE_LIBUSB=auto # Libusb HID support C89_LIBUSB=no diff --git a/retroarch.c b/retroarch.c index 3ee2db28af83..a59d50fd8573 100644 --- a/retroarch.c +++ b/retroarch.c @@ -220,7 +220,9 @@ #include "ai/game_ai.h" #endif -#if defined(HAVE_SDL) || defined(HAVE_SDL2) || defined(HAVE_SDL_DINGUX) +#if defined(HAVE_SDL3) +#include +#elif defined(HAVE_SDL) || defined(HAVE_SDL2) || defined(HAVE_SDL_DINGUX) #include "SDL.h" #endif @@ -4284,7 +4286,7 @@ bool command_event(enum event_command cmd, void *data) video_driver_state_t *video_st = video_state_get_ptr(); rarch_system_info_t *sys_info = &runloop_st->system; - + /* Restore unpaused state */ runloop_st->paused_hotkey = false; command_event(CMD_EVENT_UNPAUSE, NULL); @@ -5900,7 +5902,7 @@ static void global_free(struct rarch_state *p_rarch) retroarch_override_setting_free_state(); } -#if defined(HAVE_SDL) || defined(HAVE_SDL2) || defined(HAVE_SDL_DINGUX) +#if defined(HAVE_SDL) || defined(HAVE_SDL2) || defined(HAVE_SDL_DINGUX) || defined(HAVE_SDL3) static void sdl_exit(void) { /* Quit any SDL subsystems, then quit @@ -6002,7 +6004,7 @@ void main_exit(void *args) CoUninitialize(); #endif -#if defined(HAVE_SDL) || defined(HAVE_SDL2) || defined(HAVE_SDL_DINGUX) +#if defined(HAVE_SDL) || defined(HAVE_SDL2) || defined(HAVE_SDL_DINGUX) || defined(HAVE_SDL3) sdl_exit(); #endif } @@ -6466,6 +6468,9 @@ static void retroarch_print_features(void) #ifdef HAVE_SDL2 _len += _PSUPP_BUF(buf, _len, SUPPORTS_SDL2, "SDL2", "SDL2 input/audio/video drivers"); #endif +#ifdef HAVE_SDL3 + _len += _PSUPP_BUF(buf, _len, SUPPORTS_SDL3, "SDL3", "SDL3 joypad driver"); +#endif #ifdef HAVE_X11 _len += _PSUPP_BUF(buf, _len, SUPPORTS_X11, "X11", "X11 input/video drivers"); #endif