From 483b8d4d984a66e6e4b063828743afd768683fc3 Mon Sep 17 00:00:00 2001 From: Frank Praznik Date: Sat, 2 Aug 2025 17:31:41 -0400 Subject: [PATCH 01/61] x11: Implement precision/pixel scrolling Manual rebase of #5382 with some changes for SDL3 (thanks @wooosh). --- cmake/sdlchecks.cmake | 11 + include/build_config/SDL_build_config.h.cmake | 1 + src/video/x11/SDL_x11video.c | 1 + src/video/x11/SDL_x11window.c | 2 +- src/video/x11/SDL_x11xinput2.c | 265 +++++++++++++++--- src/video/x11/SDL_x11xinput2.h | 4 +- 6 files changed, 247 insertions(+), 37 deletions(-) diff --git a/cmake/sdlchecks.cmake b/cmake/sdlchecks.cmake index ea9d73b266..8eda0dfe56 100644 --- a/cmake/sdlchecks.cmake +++ b/cmake/sdlchecks.cmake @@ -416,6 +416,17 @@ macro(CheckX11) endif() set(SDL_VIDEO_DRIVER_X11_XINPUT2 1) + # Check for scroll info + check_c_source_compiles(" + #include + #include + #include + XIScrollClassInfo *s; + int main(int argc, char **argv) {}" HAVE_XINPUT2_SCROLLINFO) + if(HAVE_XINPUT2_SCROLLINFO) + set(SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_SCROLLINFO 1) + endif() + # Check for multitouch check_c_source_compiles_static(" #include diff --git a/include/build_config/SDL_build_config.h.cmake b/include/build_config/SDL_build_config.h.cmake index 51f8db6097..03d75e3864 100644 --- a/include/build_config/SDL_build_config.h.cmake +++ b/include/build_config/SDL_build_config.h.cmake @@ -429,6 +429,7 @@ #cmakedefine SDL_VIDEO_DRIVER_X11_XFIXES 1 #cmakedefine SDL_VIDEO_DRIVER_X11_XINPUT2 1 #cmakedefine SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH 1 +#cmakedefine SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_SCROLLINFO 1 #cmakedefine SDL_VIDEO_DRIVER_X11_XRANDR 1 #cmakedefine SDL_VIDEO_DRIVER_X11_XSCRNSAVER 1 #cmakedefine SDL_VIDEO_DRIVER_X11_XSHAPE 1 diff --git a/src/video/x11/SDL_x11video.c b/src/video/x11/SDL_x11video.c index dd4e635dc2..8b23186e1a 100644 --- a/src/video/x11/SDL_x11video.c +++ b/src/video/x11/SDL_x11video.c @@ -468,6 +468,7 @@ void X11_VideoQuit(SDL_VideoDevice *_this) } #endif + X11_QuitXinput2(_this); X11_QuitModes(_this); X11_QuitKeyboard(_this); X11_QuitMouse(_this); diff --git a/src/video/x11/SDL_x11window.c b/src/video/x11/SDL_x11window.c index b0f8fed6eb..52bec5f91d 100644 --- a/src/video/x11/SDL_x11window.c +++ b/src/video/x11/SDL_x11window.c @@ -475,7 +475,7 @@ static void SetupWindowInput(SDL_VideoDevice *_this, SDL_Window *window) } #endif - X11_Xinput2SelectTouch(_this, window); + X11_Xinput2Select(_this, window); { unsigned int x11_keyboard_events = KeyPressMask | KeyReleaseMask; diff --git a/src/video/x11/SDL_x11xinput2.c b/src/video/x11/SDL_x11xinput2.c index eb212378c6..d78c0be5e2 100644 --- a/src/video/x11/SDL_x11xinput2.c +++ b/src/video/x11/SDL_x11xinput2.c @@ -45,6 +45,28 @@ static bool xinput2_multitouch_supported; * this extension */ static int xinput2_opcode; +#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_SCROLLINFO +typedef struct +{ + int number; + int scroll_type; + double prev_value; + double increment; + bool prev_value_valid; +} SDL_XInput2ScrollInfo; + +typedef struct +{ + int device_id; + int scroll_info_count; + SDL_XInput2ScrollInfo *scroll_info; +} SDL_XInput2ScrollableDevice; + +static SDL_XInput2ScrollableDevice *scrollable_devices; +static int scrollable_device_count; +static bool xinput2_scrolling_supported; +#endif + static void parse_valuators(const double *input_values, const unsigned char *mask, int mask_len, double *output_values, int output_values_len) { @@ -95,6 +117,62 @@ static SDL_Window *xinput2_get_sdlwindow(SDL_VideoData *videodata, Window window return windowdata ? windowdata->window : NULL; } +#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_SCROLLINFO +static void xinput2_reset_scrollable_valuators(SDL_VideoData *videodata) +{ + for (int i = 0; i < scrollable_device_count; ++i) { + for (int j = 0; j < scrollable_devices[i].scroll_info_count; ++j) { + scrollable_devices[i].scroll_info[j].prev_value_valid = false; + } + } +} + +static void xinput2_parse_scrollable_valuators(const XIDeviceEvent *xev) +{ + for (int i = 0; i < scrollable_device_count; ++i) { + const SDL_XInput2ScrollableDevice *sd = &scrollable_devices[i]; + if (xev->sourceid == sd->device_id) { + int values_i = 0; + for (int j = 0; j < xev->valuators.mask_len * 8; ++j) { + if (!XIMaskIsSet(xev->valuators.mask, j)) { + continue; + } + + for (int k = 0; k < sd->scroll_info_count; ++k) { + SDL_XInput2ScrollInfo *info = &sd->scroll_info[k]; + if (info->number == j) { + const double current_val = xev->valuators.values[values_i]; + const double delta = (info->prev_value - current_val) / info->increment; + /* Ignore very large jumps that can happen as a result of overflowing + * the maximum range, as the driver will reset the position to zero + * at "something that's close to 2^32". + * + * The first scroll event is meaningless by itself and must be discarded, + * as it is only useful for establishing a baseline for future deltas. + * This is a known deficiency of the XInput2 scroll protocol, and, + * unfortunately, there is nothing we can do about it. + * + * http://who-t.blogspot.com/2012/06/xi-21-protocol-design-issues.html + */ + if (info->prev_value_valid && SDL_fabs(delta) < (double)SDL_MAX_SINT32 * 0.95) { + const double x = info->scroll_type == XIScrollTypeHorizontal ? delta : 0; + const double y = info->scroll_type == XIScrollTypeVertical ? delta : 0; + + SDL_Mouse *mouse = SDL_GetMouse(); + SDL_SendMouseWheel(xev->time, mouse->focus, (SDL_MouseID)xev->sourceid, (float)x, (float)y, SDL_MOUSEWHEEL_NORMAL); + } + info->prev_value = current_val; + info->prev_value_valid = true; + } + } + + ++values_i; + } + } + } +} +#endif // SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_SCROLLINFO + #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH static void xinput2_normalize_touch_coordinates(SDL_Window *window, double in_x, double in_y, float *out_x, float *out_y) { @@ -119,6 +197,24 @@ static void xinput2_normalize_touch_coordinates(SDL_Window *window, double in_x, #endif // SDL_VIDEO_DRIVER_X11_XINPUT2 +static bool X11_Xinput2IsMultitouchSupported(void) +{ +#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH + return xinput2_initialized && xinput2_multitouch_supported; +#else + return false; +#endif +} + +static bool X11_Xinput2IsScrollingSupported(void) +{ +#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_SCROLLINFO + return xinput2_initialized && xinput2_scrolling_supported; +#else + return false; +#endif +} + bool X11_InitXinput2(SDL_VideoDevice *_this) { #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2 @@ -126,7 +222,7 @@ bool X11_InitXinput2(SDL_VideoDevice *_this) int version = 0; XIEventMask eventmask; - unsigned char mask[4] = { 0, 0, 0, 0 }; + unsigned char mask[5] = { 0, 0, 0, 0, 0 }; int event, err; /* XInput2 is required for relative mouse mode, so you probably want to leave this enabled */ @@ -156,6 +252,10 @@ bool X11_InitXinput2(SDL_VideoDevice *_this) xinput2_initialized = true; +#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_SCROLLINFO // Smooth scrolling needs XInput 2.1 + xinput2_scrolling_supported = xinput2_version_atleast(version, 2, 1); +#endif + #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH // Multitouch needs XInput 2.2 xinput2_multitouch_supported = xinput2_version_atleast(version, 2, 2); #endif @@ -171,6 +271,12 @@ bool X11_InitXinput2(SDL_VideoDevice *_this) XISetMask(mask, XI_RawButtonPress); XISetMask(mask, XI_RawButtonRelease); +#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_SCROLLINFO + if (X11_Xinput2IsScrollingSupported()) { + XISetMask(mask, XI_Motion); + } +#endif + #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH // Enable raw touch events if supported if (X11_Xinput2IsMultitouchSupported()) { @@ -199,6 +305,18 @@ bool X11_InitXinput2(SDL_VideoDevice *_this) #endif } +void X11_QuitXinput2(SDL_VideoDevice *_this) +{ +#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_SCROLLINFO + for (int i = 0; i < scrollable_device_count; ++i) { + SDL_free(scrollable_devices[i].scroll_info); + } + SDL_free(scrollable_devices); + scrollable_devices = NULL; + scrollable_device_count = 0; +#endif +} + #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2 // xi2 device went away? take it out of the list. static void xinput2_remove_device_info(SDL_VideoData *videodata, const int device_id) @@ -409,6 +527,11 @@ void X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie) X11_PenHandle *pen = X11_FindPenByDeviceID(xev->sourceid); const int button = xev->detail; const bool down = (cookie->evtype == XI_ButtonPress); +#if defined(SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_SCROLLINFO) || defined(SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH) + bool pointer_emulated = (xev->flags & XIPointerEmulated) != 0; +#else + bool pointer_emulated = false; +#endif if (pen) { if (xev->deviceid != xev->sourceid) { @@ -429,8 +552,12 @@ void X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie) /* Discard wheel events from "Master" devices to avoid duplicates, * as coarse wheel events are stateless and can't be deduplicated. + * + * If the pointer emulation flag is set on a wheel event, it is being + * emulated from a scroll valuator, which will be handled natively. */ - if (xev->deviceid != xev->sourceid && X11_IsWheelEvent(button, &x_ticks, &y_ticks)) { + if ((pointer_emulated || xev->deviceid != xev->sourceid) && + X11_IsWheelEvent(button, &x_ticks, &y_ticks)) { break; } @@ -443,13 +570,19 @@ void X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie) } } break; +#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_SCROLLINFO + case XI_Enter: + xinput2_reset_scrollable_valuators(videodata); + break; +#endif + /* Register to receive XI_Motion (which deactivates MotionNotify), so that we can distinguish real mouse motions from synthetic ones, for multitouch and pen support. */ case XI_Motion: { const XIDeviceEvent *xev = (const XIDeviceEvent *)cookie->data; -#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH - bool pointer_emulated = ((xev->flags & XIPointerEmulated) != 0); +#if defined(SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_SCROLLINFO) || defined(SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH) + bool pointer_emulated = (xev->flags & XIPointerEmulated) != 0; #else bool pointer_emulated = false; #endif @@ -475,14 +608,22 @@ void X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie) SDL_SendPenAxis(0, pen->pen, window, (SDL_PenAxis) i, axes[i]); } } - } else if (!pointer_emulated && xev->deviceid == videodata->xinput_master_pointer_device) { - // Use the master device for non-relative motion, as the slave devices can seemingly lag behind. - SDL_Mouse *mouse = SDL_GetMouse(); - if (!mouse->relative_mode) { - SDL_Window *window = xinput2_get_sdlwindow(videodata, xev->event); - if (window) { - X11_ProcessHitTest(_this, window->internal, (float)xev->event_x, (float)xev->event_y, false); - SDL_SendMouseMotion(0, window, SDL_GLOBAL_MOUSE_ID, false, (float)xev->event_x, (float)xev->event_y); + } else if (!pointer_emulated) { +#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_SCROLLINFO + if (xev->deviceid == xev->sourceid) { + xinput2_parse_scrollable_valuators(xev); + } +#endif + + if (xev->deviceid == videodata->xinput_master_pointer_device) { + // Use the master device for non-relative motion, as the slave devices can seemingly lag behind. + SDL_Mouse *mouse = SDL_GetMouse(); + if (!mouse->relative_mode) { + SDL_Window *window = xinput2_get_sdlwindow(videodata, xev->event); + if (window) { + X11_ProcessHitTest(_this, window->internal, (float)xev->event_x, (float)xev->event_y, false); + SDL_SendMouseMotion(0, window, SDL_GLOBAL_MOUSE_ID, false, (float)xev->event_x, (float)xev->event_y); + } } } } @@ -524,29 +665,36 @@ void X11_InitXinput2Multitouch(SDL_VideoDevice *_this) { } -void X11_Xinput2SelectTouch(SDL_VideoDevice *_this, SDL_Window *window) +void X11_Xinput2Select(SDL_VideoDevice *_this, SDL_Window *window) { -#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH - SDL_VideoData *data = NULL; +#if defined(SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_SCROLLINFO) || defined(SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH) + SDL_VideoData *data = _this->internal; + SDL_WindowData *window_data = window->internal; XIEventMask eventmask; - unsigned char mask[4] = { 0, 0, 0, 0 }; - SDL_WindowData *window_data = NULL; + unsigned char mask[5] = { 0, 0, 0, 0, 0 }; - if (!X11_Xinput2IsMultitouchSupported()) { + if (!X11_Xinput2IsScrollingSupported() && !X11_Xinput2IsMultitouchSupported()) { return; } - data = _this->internal; - window_data = window->internal; - eventmask.deviceid = XIAllMasterDevices; eventmask.mask_len = sizeof(mask); eventmask.mask = mask; - XISetMask(mask, XI_TouchBegin); - XISetMask(mask, XI_TouchUpdate); - XISetMask(mask, XI_TouchEnd); - XISetMask(mask, XI_Motion); + if (X11_Xinput2IsScrollingSupported()) { + /* Track enter events that inform us that we need to update + * the previous scroll coordinates since we cannot track + * them outside our window. + */ + XISetMask(mask, XI_Enter); + } + + if (X11_Xinput2IsMultitouchSupported()) { + XISetMask(mask, XI_TouchBegin); + XISetMask(mask, XI_TouchUpdate); + XISetMask(mask, XI_TouchEnd); + XISetMask(mask, XI_Motion); + } X11_XISelectEvents(data->display, window_data->xwindow, &eventmask, 1); #endif @@ -610,15 +758,6 @@ bool X11_Xinput2SelectMouseAndKeyboard(SDL_VideoDevice *_this, SDL_Window *windo return false; } -bool X11_Xinput2IsMultitouchSupported(void) -{ -#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH - return xinput2_initialized && xinput2_multitouch_supported; -#else - return true; -#endif -} - void X11_Xinput2GrabTouch(SDL_VideoDevice *_this, SDL_Window *window) { #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH @@ -748,6 +887,16 @@ void X11_Xinput2UpdateDevices(SDL_VideoDevice *_this, bool initial_check) old_mice = SDL_GetMice(&old_mouse_count); old_touch_devices = SDL_GetTouchDevices(&old_touch_count); +#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_SCROLLINFO + // Scroll devices don't get add/remove events, so just rebuild the list. + for (int i = 0; i < scrollable_device_count; ++i) { + SDL_free(scrollable_devices[i].scroll_info); + } + SDL_free(scrollable_devices); + scrollable_devices = NULL; + scrollable_device_count = 0; +#endif + for (int i = 0; i < ndevices; i++) { XIDeviceInfo *dev = &info[i]; @@ -778,6 +927,54 @@ void X11_Xinput2UpdateDevices(SDL_VideoDevice *_this, bool initial_check) break; } +#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_SCROLLINFO + SDL_XInput2ScrollableDevice *sd = NULL; + int allocated_scroll_info_count = 0; + + for (int j = 0; j < dev->num_classes; j++) { + const XIAnyClassInfo *class = dev->classes[j]; + const XIScrollClassInfo *s = (XIScrollClassInfo *)class; + + if (class->type != XIScrollClass) { + continue; + } + + // Allocate a new scrollable device. + if (!sd) { + scrollable_devices = SDL_realloc(scrollable_devices, (scrollable_device_count + 1) * sizeof(SDL_XInput2ScrollableDevice)); + if (!scrollable_devices) { + // No memory; so just skip this. + break; + } + + sd = &scrollable_devices[scrollable_device_count]; + ++scrollable_device_count; + + SDL_zerop(sd); + sd->device_id = dev->deviceid; + } + + // Allocate new scroll info entries two at a time, as they typically come in a horizontal/vertical pair. + if (sd->scroll_info_count == allocated_scroll_info_count) { + sd->scroll_info = SDL_realloc(sd->scroll_info, (allocated_scroll_info_count + 2) * sizeof(SDL_XInput2ScrollInfo)); + if (!sd->scroll_info) { + // No memory; just skip this. + break; + } + + allocated_scroll_info_count += 2; + } + + SDL_XInput2ScrollInfo *scroll_info = &sd->scroll_info[sd->scroll_info_count]; + ++sd->scroll_info_count; + + SDL_zerop(scroll_info); + scroll_info->number = s->number; + scroll_info->scroll_type = s->scroll_type; + scroll_info->increment = s->increment; + } +#endif + #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH for (int j = 0; j < dev->num_classes; j++) { Uint64 touchID; diff --git a/src/video/x11/SDL_x11xinput2.h b/src/video/x11/SDL_x11xinput2.h index c96c020bc7..35f4bb8b8b 100644 --- a/src/video/x11/SDL_x11xinput2.h +++ b/src/video/x11/SDL_x11xinput2.h @@ -31,11 +31,11 @@ typedef struct XGenericEventCookie XGenericEventCookie; #endif extern bool X11_InitXinput2(SDL_VideoDevice *_this); +extern void X11_QuitXinput2(SDL_VideoDevice *_this); extern void X11_InitXinput2Multitouch(SDL_VideoDevice *_this); extern void X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie); extern bool X11_Xinput2IsInitialized(void); -extern bool X11_Xinput2IsMultitouchSupported(void); -extern void X11_Xinput2SelectTouch(SDL_VideoDevice *_this, SDL_Window *window); +extern void X11_Xinput2Select(SDL_VideoDevice *_this, SDL_Window *window); extern void X11_Xinput2GrabTouch(SDL_VideoDevice *_this, SDL_Window *window); extern void X11_Xinput2UngrabTouch(SDL_VideoDevice *_this, SDL_Window *window); extern bool X11_Xinput2SelectMouseAndKeyboard(SDL_VideoDevice *_this, SDL_Window *window); From bba6555bf18c1c6cc3895b558def0d739f0d3c2e Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Mon, 4 Aug 2025 10:21:58 -0700 Subject: [PATCH 02/61] Enable background input when using Microsoft GameInput --- src/joystick/gdk/SDL_gameinputjoystick.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/joystick/gdk/SDL_gameinputjoystick.cpp b/src/joystick/gdk/SDL_gameinputjoystick.cpp index bd1e9eaf63..e75a5bab30 100644 --- a/src/joystick/gdk/SDL_gameinputjoystick.cpp +++ b/src/joystick/gdk/SDL_gameinputjoystick.cpp @@ -279,6 +279,10 @@ static bool GAMEINPUT_JoystickInit(void) return false; } + // Allow background controller input + // SDL manages focus policy at a higher level, so we can set this unconditionally. + g_pGameInput->SetFocusPolicy(GameInputEnableBackgroundInput | GameInputEnableBackgroundGuideButton | GameInputEnableBackgroundShareButton); + hr = g_pGameInput->RegisterDeviceCallback(NULL, GameInputKindController, GameInputDeviceConnected, From 9a71e3fd50a751ec4df1b7030a69d65b33d34d5c Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Mon, 4 Aug 2025 10:39:34 -0700 Subject: [PATCH 03/61] Revert "Add SDL_IsTraySupported" This reverts commit 47d8bdd1c3e9c799ecaa7cb10d84ae8d88165b5c. There are runtime reasons why creating a tray can fail, so the correct approach is not to assume that just because a platform supports a tray that trays are available. Instead, you should create a tray at application startup, for the lifetime of the application, and handle failures at that point. Closes https://github.com/libsdl-org/SDL/pull/13632 --- include/SDL3/SDL_tray.h | 19 ------------------- src/dynapi/SDL_dynapi.sym | 1 - src/dynapi/SDL_dynapi_overrides.h | 1 - src/dynapi/SDL_dynapi_procs.h | 1 - src/tray/cocoa/SDL_tray.m | 10 ---------- src/tray/dummy/SDL_tray.c | 5 ----- src/tray/unix/SDL_tray.c | 18 ------------------ src/tray/windows/SDL_tray.c | 10 ---------- 8 files changed, 65 deletions(-) diff --git a/include/SDL3/SDL_tray.h b/include/SDL3/SDL_tray.h index ec7adfcf7f..1780b0ba52 100644 --- a/include/SDL3/SDL_tray.h +++ b/include/SDL3/SDL_tray.h @@ -96,25 +96,6 @@ typedef Uint32 SDL_TrayEntryFlags; */ typedef void (SDLCALL *SDL_TrayCallback)(void *userdata, SDL_TrayEntry *entry); -/** - * Check whether or not tray icons can be created. - * - * Note that this function does not guarantee that SDL_CreateTray() will or - * will not work; you should still check SDL_CreateTray() for errors. - * - * Using tray icons require the video subsystem. - * - * \returns true if trays are available, false otherwise. - * - * \threadsafety This function should only be called on the main thread. It - * will return false if not called on the main thread. - * - * \since This function is available since SDL 3.4.0. - * - * \sa SDL_CreateTray - */ -extern SDL_DECLSPEC bool SDLCALL SDL_IsTraySupported(void); - /** * Create an icon to be placed in the operating system's tray, or equivalent. * diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym index fcae64bea6..c69956e8b0 100644 --- a/src/dynapi/SDL_dynapi.sym +++ b/src/dynapi/SDL_dynapi.sym @@ -1253,7 +1253,6 @@ SDL3_0.0.0 { SDL_PutAudioStreamPlanarData; SDL_GetEventDescription; SDL_PutAudioStreamDataNoCopy; - SDL_IsTraySupported; # extra symbols go here (don't modify this line) local: *; }; diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h index 8873ff4734..61eb4b956d 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -1278,4 +1278,3 @@ #define SDL_PutAudioStreamPlanarData SDL_PutAudioStreamPlanarData_REAL #define SDL_GetEventDescription SDL_GetEventDescription_REAL #define SDL_PutAudioStreamDataNoCopy SDL_PutAudioStreamDataNoCopy_REAL -#define SDL_IsTraySupported SDL_IsTraySupported_REAL diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index f05ff2ff33..d1e362cdff 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -1286,4 +1286,3 @@ SDL_DYNAPI_PROC(SDL_Renderer*,SDL_CreateGPURenderer,(SDL_Window *a,SDL_GPUShader SDL_DYNAPI_PROC(bool,SDL_PutAudioStreamPlanarData,(SDL_AudioStream *a,const void * const*b,int c,int d),(a,b,c,d),return) SDL_DYNAPI_PROC(int,SDL_GetEventDescription,(const SDL_Event *a,char *b,int c),(a,b,c),return) SDL_DYNAPI_PROC(bool,SDL_PutAudioStreamDataNoCopy,(SDL_AudioStream *a,const void *b,int c,SDL_AudioStreamDataCompleteCallback d,void *e),(a,b,c,d,e),return) -SDL_DYNAPI_PROC(bool,SDL_IsTraySupported,(void),(),return) diff --git a/src/tray/cocoa/SDL_tray.m b/src/tray/cocoa/SDL_tray.m index d093972a2d..fd7f95517c 100644 --- a/src/tray/cocoa/SDL_tray.m +++ b/src/tray/cocoa/SDL_tray.m @@ -82,16 +82,6 @@ void SDL_UpdateTrays(void) { } -bool SDL_IsTraySupported(void) -{ - if (!SDL_IsMainThread()) { - SDL_SetError("This function should be called on the main thread"); - return false; - } - - return true; -} - SDL_Tray *SDL_CreateTray(SDL_Surface *icon, const char *tooltip) { if (!SDL_IsMainThread()) { diff --git a/src/tray/dummy/SDL_tray.c b/src/tray/dummy/SDL_tray.c index 3a95c6575b..766fb92584 100644 --- a/src/tray/dummy/SDL_tray.c +++ b/src/tray/dummy/SDL_tray.c @@ -29,11 +29,6 @@ void SDL_UpdateTrays(void) { } -bool SDL_IsTraySupported(void) -{ - return false; -} - SDL_Tray *SDL_CreateTray(SDL_Surface *icon, const char *tooltip) { SDL_Unsupported(); diff --git a/src/tray/unix/SDL_tray.c b/src/tray/unix/SDL_tray.c index 0671badda0..23863269ee 100644 --- a/src/tray/unix/SDL_tray.c +++ b/src/tray/unix/SDL_tray.c @@ -240,24 +240,6 @@ void SDL_UpdateTrays(void) SDL_UpdateGtk(); } -bool SDL_IsTraySupported(void) -{ - if (!SDL_IsMainThread()) { - SDL_SetError("This function should be called on the main thread"); - return false; - } - - static bool has_trays = false; - static bool has_been_detected_once = false; - - if (!has_been_detected_once) { - has_trays = init_appindicator() && SDL_Gtk_Init(); - has_been_detected_once = true; - } - - return has_trays; -} - SDL_Tray *SDL_CreateTray(SDL_Surface *icon, const char *tooltip) { if (!SDL_IsMainThread()) { diff --git a/src/tray/windows/SDL_tray.c b/src/tray/windows/SDL_tray.c index a3bd81ff10..15021ac798 100644 --- a/src/tray/windows/SDL_tray.c +++ b/src/tray/windows/SDL_tray.c @@ -216,16 +216,6 @@ void SDL_UpdateTrays(void) { } -bool SDL_IsTraySupported(void) -{ - if (!SDL_IsMainThread()) { - SDL_SetError("This function should be called on the main thread"); - return false; - } - - return true; -} - SDL_Tray *SDL_CreateTray(SDL_Surface *icon, const char *tooltip) { if (!SDL_IsMainThread()) { From 7bb045ca221e55076d08f9a8b546b2c47d118964 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Mon, 4 Aug 2025 13:04:17 -0700 Subject: [PATCH 04/61] Fixed Windows build --- src/joystick/gdk/SDL_gameinputjoystick.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/joystick/gdk/SDL_gameinputjoystick.cpp b/src/joystick/gdk/SDL_gameinputjoystick.cpp index e75a5bab30..7b4cc2e900 100644 --- a/src/joystick/gdk/SDL_gameinputjoystick.cpp +++ b/src/joystick/gdk/SDL_gameinputjoystick.cpp @@ -279,9 +279,11 @@ static bool GAMEINPUT_JoystickInit(void) return false; } +#if GAMEINPUT_API_VERSION >= 2 // Allow background controller input // SDL manages focus policy at a higher level, so we can set this unconditionally. g_pGameInput->SetFocusPolicy(GameInputEnableBackgroundInput | GameInputEnableBackgroundGuideButton | GameInputEnableBackgroundShareButton); +#endif hr = g_pGameInput->RegisterDeviceCallback(NULL, GameInputKindController, From 293b8b9fd60e6d6ee4fa78e9e1bad9ab9839c131 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Mon, 4 Aug 2025 16:53:09 -0700 Subject: [PATCH 05/61] x11: Fix regression reading GNOME content scale - Removed gtk-xft-dpi read from GetGlobalContentScale. Xrm either returns either the same value as gtk-xft-dpi or the integer scale value in cases where gtk-xft-dpi is 1. - Refactor SDL_x11settings handlers to defer to GetGlobalContentScale - GetGlobalContentScale is now exported and the XSettings and Gtk signal handlers now use it for consistency. This involves a bit of extra work reading from Xrm rather than the setting notification but ensures consistent handling based on signal origin and hints enabled. - Hook both gtk-xft-dpi in SDL_x11settings. This should generally result in only one being called based on which is updated. Since both signal handlers defer to X11_GetGlobalContentScale this will cause the same content scale to be applied multiple times. The gtk-xft-dpi signal is now only used to trigger content scale updates when the XSettings notification does not occur. --- src/video/x11/SDL_x11modes.c | 20 ++--------- src/video/x11/SDL_x11modes.h | 2 ++ src/video/x11/SDL_x11settings.c | 60 +++++++-------------------------- 3 files changed, 17 insertions(+), 65 deletions(-) diff --git a/src/video/x11/SDL_x11modes.c b/src/video/x11/SDL_x11modes.c index 2417d33f17..cb6e4386b7 100644 --- a/src/video/x11/SDL_x11modes.c +++ b/src/video/x11/SDL_x11modes.c @@ -48,7 +48,7 @@ */ // #define XRANDR_DISABLED_BY_DEFAULT -static float GetGlobalContentScale(SDL_VideoDevice *_this) +float X11_GetGlobalContentScale(SDL_VideoDevice *_this) { static double scale_factor = 0.0; @@ -63,20 +63,6 @@ static float GetGlobalContentScale(SDL_VideoDevice *_this) } } - // If that failed, try "Xft.dpi" from GTK if available. On XWayland this - // will retrieve the current scale factor which is not updated dynamically - // in the Xrm database. - SDL_GtkContext *gtk = SDL_Gtk_EnterContext(); - if (gtk) { - GtkSettings *gtksettings = gtk->gtk.settings_get_default(); - if (gtksettings) { - int dpi = 0; - gtk->g.object_get(gtksettings, "gtk-xft-dpi", &dpi, NULL); - scale_factor = dpi / 1024.0 / 96.0; - } - SDL_Gtk_ExitContext(gtk); - } - // If that failed, try "Xft.dpi" from the XResourcesDatabase... if (scale_factor <= 0.0) { @@ -505,7 +491,7 @@ static bool X11_FillXRandRDisplayInfo(SDL_VideoDevice *_this, Display *dpy, int display->name = display_name; } display->desktop_mode = mode; - display->content_scale = GetGlobalContentScale(_this); + display->content_scale = X11_GetGlobalContentScale(_this); display->internal = displaydata; return true; @@ -875,7 +861,7 @@ static bool X11_InitModes_StdXlib(SDL_VideoDevice *_this) display.name = (char *)"Generic X11 Display"; /* this is just copied and thrown away, it's safe to cast to char* here. */ display.desktop_mode = mode; display.internal = displaydata; - display.content_scale = GetGlobalContentScale(_this); + display.content_scale = X11_GetGlobalContentScale(_this); if (SDL_AddVideoDisplay(&display, true) == 0) { return false; } diff --git a/src/video/x11/SDL_x11modes.h b/src/video/x11/SDL_x11modes.h index 35fd86621b..77f445ace7 100644 --- a/src/video/x11/SDL_x11modes.h +++ b/src/video/x11/SDL_x11modes.h @@ -62,6 +62,8 @@ extern SDL_PixelFormat X11_GetPixelFormatFromVisualInfo(Display *display, XVisua extern bool X11_GetDisplayBounds(SDL_VideoDevice *_this, SDL_VideoDisplay *sdl_display, SDL_Rect *rect); extern bool X11_GetDisplayUsableBounds(SDL_VideoDevice *_this, SDL_VideoDisplay *sdl_display, SDL_Rect *rect); +extern float X11_GetGlobalContentScale(SDL_VideoDevice *_this); + #ifdef SDL_VIDEO_DRIVER_X11_XRANDR extern void X11_HandleXRandREvent(SDL_VideoDevice *_this, const XEvent *xevent); #endif diff --git a/src/video/x11/SDL_x11settings.c b/src/video/x11/SDL_x11settings.c index a1a9846d28..661d66db83 100644 --- a/src/video/x11/SDL_x11settings.c +++ b/src/video/x11/SDL_x11settings.c @@ -31,61 +31,26 @@ #define SDL_XSETTINGS_GDK_WINDOW_SCALING_FACTOR "Gdk/WindowScalingFactor" #define SDL_XSETTINGS_XFT_DPI "Xft/DPI" -static void X11_XsettingsNotify(const char *name, XSettingsAction action, XSettingsSetting *setting, void *data) +static void UpdateContentScale(SDL_VideoDevice *_this) { - SDL_VideoDevice *_this = data; - float scale_factor = 1.0; - int i; - - if (SDL_strcmp(name, SDL_XSETTINGS_GDK_WINDOW_SCALING_FACTOR) != 0 || - SDL_strcmp(name, SDL_XSETTINGS_XFT_DPI) != 0) { - return; - } - - if (setting->type != XSETTINGS_TYPE_INT) { - return; - } - - switch (action) { - case XSETTINGS_ACTION_NEW: - SDL_FALLTHROUGH; - case XSETTINGS_ACTION_CHANGED: - scale_factor = setting->data.v_int; - if (SDL_strcmp(name, SDL_XSETTINGS_XFT_DPI) == 0) { - scale_factor = scale_factor / 1024.0f / 96.0f; - } - break; - case XSETTINGS_ACTION_DELETED: - scale_factor = 1.0; - break; - } - if (_this) { - for (i = 0; i < _this->num_displays; ++i) { + float scale_factor = X11_GetGlobalContentScale(_this); + for (int i = 0; i < _this->num_displays; ++i) { SDL_SetDisplayContentScale(_this->displays[i], scale_factor); } } } +static void X11_XsettingsNotify(const char *name, XSettingsAction action, XSettingsSetting *setting, void *data) +{ + SDL_VideoDevice *_this = data; + UpdateContentScale(_this); +} + static void OnGtkXftDpi(GtkSettings *settings, GParamSpec *pspec, gpointer ptr) { SDL_VideoDevice *_this = (SDL_VideoDevice *)ptr; - - SDL_GtkContext *gtk = SDL_Gtk_EnterContext(); - if (gtk) { - int dpi = 0; - gtk->g.object_get(settings, "gtk-xft-dpi", &dpi, NULL); - - if (dpi != 0) { - float scale_factor = dpi / 1024.f / 96.f; - - for (int i = 0; i < _this->num_displays; ++i) { - SDL_VideoDisplay *display = _this->displays[i]; - SDL_SetDisplayContentScale(display, scale_factor); - } - } - SDL_Gtk_ExitContext(gtk); - } + UpdateContentScale(_this); } void X11_InitXsettings(SDL_VideoDevice *_this) @@ -110,11 +75,10 @@ void X11_InitXsettings(SDL_VideoDevice *_this) if (gtksettings && xft_dpi_signal_handler_id) { xsettings_data->gtksettings = gtksettings; xsettings_data->xft_dpi_signal_handler_id = xft_dpi_signal_handler_id; - } else { - xsettings_data->xsettings = xsettings_client_new(data->display, - DefaultScreen(data->display), X11_XsettingsNotify, NULL, _this); } + xsettings_data->xsettings = xsettings_client_new(data->display, + DefaultScreen(data->display), X11_XsettingsNotify, NULL, _this); } void X11_QuitXsettings(SDL_VideoDevice *_this) From ee8f2861e71f549a33af8ce9958b74515eee6b81 Mon Sep 17 00:00:00 2001 From: Frank Praznik Date: Mon, 4 Aug 2025 22:07:24 -0400 Subject: [PATCH 06/61] cocoa: Don't re-enter a fullscreen space if leaving to enter an exclusive mode Doing so can leave the window in a weird, offset state. --- src/video/SDL_video.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c index 1fd398bed7..19d849e12f 100644 --- a/src/video/SDL_video.c +++ b/src/video/SDL_video.c @@ -1927,11 +1927,15 @@ bool SDL_UpdateFullscreenMode(SDL_Window *window, SDL_FullscreenOp fullscreen, b goto done; } if (commit) { + bool skip_spaces_switch = false; // If we're switching between a fullscreen Space and exclusive fullscreen, we need to get back to normal first. if (fullscreen && Cocoa_IsWindowInFullscreenSpace(window) && !window->last_fullscreen_exclusive_display && window->fullscreen_exclusive) { if (!Cocoa_SetWindowFullscreenSpace(window, false, true)) { goto error; } + + // We just left spaces to go to an exclusive mode, so don't try to re-enter. + skip_spaces_switch = true; } else if (fullscreen && window->last_fullscreen_exclusive_display && !window->fullscreen_exclusive) { for (i = 0; i < _this->num_displays; ++i) { SDL_VideoDisplay *last_display = _this->displays[i]; @@ -1945,8 +1949,10 @@ bool SDL_UpdateFullscreenMode(SDL_Window *window, SDL_FullscreenOp fullscreen, b } } - if (Cocoa_SetWindowFullscreenSpace(window, !!fullscreen, syncHint)) { - goto done; + if (!skip_spaces_switch) { + if (Cocoa_SetWindowFullscreenSpace(window, !!fullscreen, syncHint)) { + goto done; + } } } } From a05aca51ec41324a44abb7a89522c571d6c594c7 Mon Sep 17 00:00:00 2001 From: Mathieu Eyraud <70028899+meyraud705@users.noreply.github.com> Date: Mon, 4 Aug 2025 11:14:52 +0200 Subject: [PATCH 07/61] Fix condition for setting HDR properties --- src/video/SDL_video.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c index 19d849e12f..a48a1309a8 100644 --- a/src/video/SDL_video.c +++ b/src/video/SDL_video.c @@ -1171,7 +1171,7 @@ float SDL_GetDisplayContentScale(SDL_DisplayID displayID) void SDL_SetWindowHDRProperties(SDL_Window *window, const SDL_HDROutputProperties *HDR, bool send_event) { - if (window->HDR.HDR_headroom != HDR->HDR_headroom || window->HDR.SDR_white_level != window->HDR.SDR_white_level) { + if (window->HDR.HDR_headroom != HDR->HDR_headroom || window->HDR.SDR_white_level != HDR->SDR_white_level) { SDL_PropertiesID window_props = SDL_GetWindowProperties(window); SDL_SetFloatProperty(window_props, SDL_PROP_WINDOW_HDR_HEADROOM_FLOAT, SDL_max(HDR->HDR_headroom, 1.0f)); From b6fa89ea74159ceb913437c3de0a5b8b58267ae2 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Mon, 4 Aug 2025 19:51:37 +0300 Subject: [PATCH 08/61] Fix directory globbing on Android --- src/core/android/SDL_android.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/android/SDL_android.c b/src/core/android/SDL_android.c index 1f9093ef58..887bcb91c0 100644 --- a/src/core/android/SDL_android.c +++ b/src/core/android/SDL_android.c @@ -1855,7 +1855,7 @@ bool Android_JNI_FileClose(void *userdata) bool Android_JNI_EnumerateAssetDirectory(const char *path, SDL_EnumerateDirectoryCallback cb, void *userdata) { - SDL_assert((*path == '\0') || (path[SDL_strlen(path) - 1] == '/')); // SDL_SYS_EnumerateDirectory() should have verified this. + SDL_assert(path != NULL); if (!asset_manager) { Internal_Android_Create_AssetManager(); From 3163e0cc9f62b47aaa74afa4132562bb71880960 Mon Sep 17 00:00:00 2001 From: Frank Praznik Date: Mon, 4 Aug 2025 23:49:15 -0400 Subject: [PATCH 09/61] Revert "cocoa: Don't re-enter a fullscreen space if leaving to enter an exclusive mode" This reverts commit ee8f2861e71f549a33af8ce9958b74515eee6b81. It turns out that the problem is elsewhere, related to needing to block mode changes until spaces transitions are complete. --- src/video/SDL_video.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c index a48a1309a8..a2536f89cf 100644 --- a/src/video/SDL_video.c +++ b/src/video/SDL_video.c @@ -1927,15 +1927,11 @@ bool SDL_UpdateFullscreenMode(SDL_Window *window, SDL_FullscreenOp fullscreen, b goto done; } if (commit) { - bool skip_spaces_switch = false; // If we're switching between a fullscreen Space and exclusive fullscreen, we need to get back to normal first. if (fullscreen && Cocoa_IsWindowInFullscreenSpace(window) && !window->last_fullscreen_exclusive_display && window->fullscreen_exclusive) { if (!Cocoa_SetWindowFullscreenSpace(window, false, true)) { goto error; } - - // We just left spaces to go to an exclusive mode, so don't try to re-enter. - skip_spaces_switch = true; } else if (fullscreen && window->last_fullscreen_exclusive_display && !window->fullscreen_exclusive) { for (i = 0; i < _this->num_displays; ++i) { SDL_VideoDisplay *last_display = _this->displays[i]; @@ -1949,10 +1945,8 @@ bool SDL_UpdateFullscreenMode(SDL_Window *window, SDL_FullscreenOp fullscreen, b } } - if (!skip_spaces_switch) { - if (Cocoa_SetWindowFullscreenSpace(window, !!fullscreen, syncHint)) { - goto done; - } + if (Cocoa_SetWindowFullscreenSpace(window, !!fullscreen, syncHint)) { + goto done; } } } From f44a98729c3ac66056f39ee690a44a2c7440274b Mon Sep 17 00:00:00 2001 From: Frank Praznik Date: Mon, 4 Aug 2025 23:54:40 -0400 Subject: [PATCH 10/61] cocoa: Wait for fullscreen spaces transitions to complete if switching to an exclusive mode If attempting to switch to an exclusive mode while a fullscreen spaces transition is active, wait until the transition is complete before trying to apply the changes, or the window can wind up in a weird, broken state if a mode switch occurs while in a fullscreen space. --- src/video/SDL_video.c | 11 +++++++++++ src/video/cocoa/SDL_cocoawindow.m | 13 +++++++++++++ 2 files changed, 24 insertions(+) diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c index a2536f89cf..bfdb6e6d83 100644 --- a/src/video/SDL_video.c +++ b/src/video/SDL_video.c @@ -180,6 +180,7 @@ static VideoBootStrap *bootstrap[] = { #if defined(SDL_PLATFORM_MACOS) && defined(SDL_VIDEO_DRIVER_COCOA) // Support for macOS fullscreen spaces, etc. extern bool Cocoa_IsWindowInFullscreenSpace(SDL_Window *window); +extern bool Cocoa_IsWindowInFullscreenSpaceTransition(SDL_Window *window); extern bool Cocoa_SetWindowFullscreenSpace(SDL_Window *window, bool state, bool blocking); extern bool Cocoa_IsShowingModalDialog(SDL_Window *window); #endif @@ -2121,6 +2122,16 @@ bool SDL_SetWindowFullscreenMode(SDL_Window *window, const SDL_DisplayMode *mode * is in progress. It will be overwritten if a new request is made. */ SDL_copyp(&window->current_fullscreen_mode, &window->requested_fullscreen_mode); + +#if defined(SDL_PLATFORM_MACOS) && defined(SDL_VIDEO_DRIVER_COCOA) + /* If this is called while in the middle of a Cocoa fullscreen spaces transition, + * wait until the transition has completed, or the window can wind up in a weird, + * broken state if a mode switch occurs while in a fullscreen space. + */ + if (SDL_strcmp(_this->name, "cocoa") == 0 && Cocoa_IsWindowInFullscreenSpaceTransition(window)) { + SDL_SyncWindow(window); + } +#endif if (SDL_WINDOW_FULLSCREEN_VISIBLE(window)) { SDL_UpdateFullscreenMode(window, SDL_FULLSCREEN_OP_UPDATE, true); SDL_SyncIfRequired(window); diff --git a/src/video/cocoa/SDL_cocoawindow.m b/src/video/cocoa/SDL_cocoawindow.m index ecf5980f47..1c790ccb01 100644 --- a/src/video/cocoa/SDL_cocoawindow.m +++ b/src/video/cocoa/SDL_cocoawindow.m @@ -411,6 +411,19 @@ bool Cocoa_IsWindowInFullscreenSpace(SDL_Window *window) } } +bool Cocoa_IsWindowInFullscreenSpaceTransition(SDL_Window *window) +{ + @autoreleasepool { + SDL_CocoaWindowData *data = (__bridge SDL_CocoaWindowData *)window->internal; + + if ([data.listener isInFullscreenSpaceTransition]) { + return true; + } else { + return false; + } + } +} + bool Cocoa_IsWindowZoomed(SDL_Window *window) { SDL_CocoaWindowData *data = (__bridge SDL_CocoaWindowData *)window->internal; From aae7736ec64f606bc5a65b111edb3186a3ef3286 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Tue, 5 Aug 2025 12:01:31 -0700 Subject: [PATCH 11/61] Added additional examples of paddle and misc buttons (thanks @AL2009man!) --- include/SDL3/SDL_gamepad.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/SDL3/SDL_gamepad.h b/include/SDL3/SDL_gamepad.h index 460ae98780..dc21d0d068 100644 --- a/include/SDL3/SDL_gamepad.h +++ b/include/SDL3/SDL_gamepad.h @@ -165,14 +165,14 @@ typedef enum SDL_GamepadButton SDL_GAMEPAD_BUTTON_DPAD_LEFT, SDL_GAMEPAD_BUTTON_DPAD_RIGHT, SDL_GAMEPAD_BUTTON_MISC1, /**< Additional button (e.g. Xbox Series X share button, PS5 microphone button, Nintendo Switch Pro capture button, Amazon Luna microphone button, Google Stadia capture button) */ - SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1, /**< Upper or primary paddle, under your right hand (e.g. Xbox Elite paddle P1) */ - SDL_GAMEPAD_BUTTON_LEFT_PADDLE1, /**< Upper or primary paddle, under your left hand (e.g. Xbox Elite paddle P3) */ - SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2, /**< Lower or secondary paddle, under your right hand (e.g. Xbox Elite paddle P2) */ - SDL_GAMEPAD_BUTTON_LEFT_PADDLE2, /**< Lower or secondary paddle, under your left hand (e.g. Xbox Elite paddle P4) */ + SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1, /**< Upper or primary paddle, under your right hand (e.g. Xbox Elite paddle P1, DualSense Edge RB button, Right Joy-Con SR button) */ + SDL_GAMEPAD_BUTTON_LEFT_PADDLE1, /**< Upper or primary paddle, under your left hand (e.g. Xbox Elite paddle P3, DualSense Edge LB button, Left Joy-Con SL button) */ + SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2, /**< Lower or secondary paddle, under your right hand (e.g. Xbox Elite paddle P2, DualSense Edge right Fn button, Right Joy-Con SL button) */ + SDL_GAMEPAD_BUTTON_LEFT_PADDLE2, /**< Lower or secondary paddle, under your left hand (e.g. Xbox Elite paddle P4, DualSense Edge left Fn button, Left Joy-Con SR button) */ SDL_GAMEPAD_BUTTON_TOUCHPAD, /**< PS4/PS5 touchpad button */ SDL_GAMEPAD_BUTTON_MISC2, /**< Additional button */ - SDL_GAMEPAD_BUTTON_MISC3, /**< Additional button */ - SDL_GAMEPAD_BUTTON_MISC4, /**< Additional button */ + SDL_GAMEPAD_BUTTON_MISC3, /**< Additional button (e.g. Nintendo GameCube left trigger click) */ + SDL_GAMEPAD_BUTTON_MISC4, /**< Additional button (e.g. Nintendo GameCube right trigger click) */ SDL_GAMEPAD_BUTTON_MISC5, /**< Additional button */ SDL_GAMEPAD_BUTTON_MISC6, /**< Additional button */ SDL_GAMEPAD_BUTTON_COUNT From 90a023007fd8aef3e7e61ab6051c4ab8e36639e3 Mon Sep 17 00:00:00 2001 From: Frank Praznik Date: Tue, 5 Aug 2025 15:51:50 -0400 Subject: [PATCH 12/61] win32: Use STYLE_BORDERLESS when showing a pending fullscreen window In addition to hiding the border on bordered windows that will immediately become fullscreen, The combination of flags used in STYLE_BORDERLESS_WINDOWED will still show the borders on borderless windows if the initial window size exactly matches the desktop, so STYLE_BORDERLESS must be used instead. --- src/video/windows/SDL_windowswindow.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/video/windows/SDL_windowswindow.c b/src/video/windows/SDL_windowswindow.c index aadfbd734b..147f6e8f36 100644 --- a/src/video/windows/SDL_windowswindow.c +++ b/src/video/windows/SDL_windowswindow.c @@ -1071,16 +1071,16 @@ void WIN_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window) WIN_SetWindowPosition(_this, window); } - // If the window isn't borderless and will be fullscreen, use the borderless style to hide the initial borders. + /* If the window will immediately become fullscreen, use the borderless style to hide the initial borders. + * + * Note: The combination of flags used in STYLE_BORDERLESS_WINDOWED will still briefly show the borders if + * the initial window size exactly matches the desktop, so STYLE_BORDERLESS must be used instead. + */ if (window->pending_flags & SDL_WINDOW_FULLSCREEN) { - if (!(window->flags & SDL_WINDOW_BORDERLESS)) { - window->flags |= SDL_WINDOW_BORDERLESS; - style = GetWindowLong(hwnd, GWL_STYLE); - style &= ~STYLE_MASK; - style |= GetWindowStyle(window); - SetWindowLong(hwnd, GWL_STYLE, style); - window->flags &= ~SDL_WINDOW_BORDERLESS; - } + style = GetWindowLong(hwnd, GWL_STYLE); + style &= ~STYLE_MASK; + style |= STYLE_BORDERLESS; + SetWindowLong(hwnd, GWL_STYLE, style); } style = GetWindowLong(hwnd, GWL_EXSTYLE); if (style & WS_EX_NOACTIVATE) { From cd0c660dead0d617bc048bb420b828eedf1e5d5e Mon Sep 17 00:00:00 2001 From: Frank Praznik Date: Tue, 5 Aug 2025 17:24:10 -0400 Subject: [PATCH 13/61] win32: Use the current flags to determine if NCCALCSIZE is required SDL_GetWindowFlags() also ORs in pending flags, whereas the current state is needed here, particularly when creating/showing a window. --- src/video/windows/SDL_windowsevents.c | 2 +- src/video/windows/SDL_windowswindow.c | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/video/windows/SDL_windowsevents.c b/src/video/windows/SDL_windowsevents.c index 030ce75a0c..bac6179c69 100644 --- a/src/video/windows/SDL_windowsevents.c +++ b/src/video/windows/SDL_windowsevents.c @@ -2175,7 +2175,7 @@ LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara case WM_NCCALCSIZE: { - SDL_WindowFlags window_flags = SDL_GetWindowFlags(data->window); + SDL_WindowFlags window_flags = data->window->flags; if (wParam == TRUE && (window_flags & SDL_WINDOW_BORDERLESS) && !(window_flags & SDL_WINDOW_FULLSCREEN)) { // When borderless, need to tell windows that the size of the non-client area is 0 NCCALCSIZE_PARAMS *params = (NCCALCSIZE_PARAMS *)lParam; diff --git a/src/video/windows/SDL_windowswindow.c b/src/video/windows/SDL_windowswindow.c index 147f6e8f36..aadfbd734b 100644 --- a/src/video/windows/SDL_windowswindow.c +++ b/src/video/windows/SDL_windowswindow.c @@ -1071,16 +1071,16 @@ void WIN_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window) WIN_SetWindowPosition(_this, window); } - /* If the window will immediately become fullscreen, use the borderless style to hide the initial borders. - * - * Note: The combination of flags used in STYLE_BORDERLESS_WINDOWED will still briefly show the borders if - * the initial window size exactly matches the desktop, so STYLE_BORDERLESS must be used instead. - */ + // If the window isn't borderless and will be fullscreen, use the borderless style to hide the initial borders. if (window->pending_flags & SDL_WINDOW_FULLSCREEN) { - style = GetWindowLong(hwnd, GWL_STYLE); - style &= ~STYLE_MASK; - style |= STYLE_BORDERLESS; - SetWindowLong(hwnd, GWL_STYLE, style); + if (!(window->flags & SDL_WINDOW_BORDERLESS)) { + window->flags |= SDL_WINDOW_BORDERLESS; + style = GetWindowLong(hwnd, GWL_STYLE); + style &= ~STYLE_MASK; + style |= GetWindowStyle(window); + SetWindowLong(hwnd, GWL_STYLE, style); + window->flags &= ~SDL_WINDOW_BORDERLESS; + } } style = GetWindowLong(hwnd, GWL_EXSTYLE); if (style & WS_EX_NOACTIVATE) { From b139821903b456021011cbc7ca6abaca6ced3f66 Mon Sep 17 00:00:00 2001 From: A1029384756 Date: Wed, 6 Aug 2025 01:21:47 -0400 Subject: [PATCH 14/61] tray: linux - use `.cache` directory for temporary icon paths --- src/core/unix/SDL_gtk.c | 5 ++-- src/core/unix/SDL_gtk.h | 1 + src/tray/unix/SDL_tray.c | 63 ++++++++++++++++++++++++++++------------ 3 files changed, 48 insertions(+), 21 deletions(-) diff --git a/src/core/unix/SDL_gtk.c b/src/core/unix/SDL_gtk.c index 323b417bd4..53af04b21c 100644 --- a/src/core/unix/SDL_gtk.c +++ b/src/core/unix/SDL_gtk.c @@ -114,8 +114,8 @@ static bool InitGtk(void) SDL_GTK_SYM(gtk, libgtk, gtk, menu_item_set_submenu); SDL_GTK_SYM(gtk, libgtk, gtk, menu_item_get_label); SDL_GTK_SYM(gtk, libgtk, gtk, menu_item_set_label); - SDL_GTK_SYM(gtk, libgtk, gtk, menu_shell_append); - SDL_GTK_SYM(gtk, libgtk, gtk, menu_shell_insert); + SDL_GTK_SYM(gtk, libgtk, gtk, menu_shell_append); + SDL_GTK_SYM(gtk, libgtk, gtk, menu_shell_insert); SDL_GTK_SYM(gtk, libgtk, gtk, check_menu_item_new_with_label); SDL_GTK_SYM(gtk, libgtk, gtk, check_menu_item_get_active); SDL_GTK_SYM(gtk, libgtk, gtk, check_menu_item_set_active); @@ -127,6 +127,7 @@ static bool InitGtk(void) SDL_GTK_SYM(gtk, libgdk, g, signal_connect_data); SDL_GTK_SYM(gtk, libgdk, g, mkdtemp); + SDL_GTK_SYM(gtk, libgdk, g, get_user_cache_dir); SDL_GTK_SYM(gtk, libgdk, g, object_ref); SDL_GTK_SYM(gtk, libgdk, g, object_ref_sink); SDL_GTK_SYM(gtk, libgdk, g, object_unref); diff --git a/src/core/unix/SDL_gtk.h b/src/core/unix/SDL_gtk.h index e966dac45e..2ec3ea0b09 100644 --- a/src/core/unix/SDL_gtk.h +++ b/src/core/unix/SDL_gtk.h @@ -80,6 +80,7 @@ typedef struct SDL_GtkContext gulong (*signal_connect_data)(gpointer instance, const gchar *detailed_signal, GCallback c_handler, gpointer data, GClosureNotify destroy_data, SDL_GConnectFlags connect_flags); void (*object_unref)(gpointer object); gchar *(*mkdtemp)(gchar *template); + gchar *(*get_user_cache_dir)(void); gpointer (*object_ref_sink)(gpointer object); gpointer (*object_ref)(gpointer object); void (*object_get)(gpointer object, const gchar *first_property_name, ...); diff --git a/src/tray/unix/SDL_tray.c b/src/tray/unix/SDL_tray.c index 23863269ee..a9863932b1 100644 --- a/src/tray/unix/SDL_tray.c +++ b/src/tray/unix/SDL_tray.c @@ -153,15 +153,11 @@ struct SDL_TrayEntry { SDL_TrayMenu *submenu; }; -/* Template for g_mkdtemp(). The Xs will get replaced with a random - * directory name, which is created safely and atomically. */ -#define ICON_DIR_TEMPLATE "/tmp/SDL-tray-XXXXXX" - struct SDL_Tray { AppIndicator *indicator; SDL_TrayMenu *menu; - char icon_dir[sizeof(ICON_DIR_TEMPLATE)]; - char icon_path[256]; + char *icon_dir; + char *icon_path; GtkMenuShell *menu_cached; }; @@ -188,13 +184,13 @@ static bool new_tmp_filename(SDL_Tray *tray) { static int count = 0; - int would_have_written = SDL_snprintf(tray->icon_path, sizeof(tray->icon_path), "%s/%d.bmp", tray->icon_dir, count++); + int would_have_written = SDL_asprintf(&tray->icon_path, "%s/%d.bmp", tray->icon_dir, count++); - if (would_have_written > 0 && ((unsigned) would_have_written) < sizeof(tray->icon_path) - 1) { + if (would_have_written >= 0) { return true; } - tray->icon_path[0] = '\0'; + tray->icon_path = NULL; SDL_SetError("Failed to format new temporary filename"); return false; } @@ -254,29 +250,47 @@ SDL_Tray *SDL_CreateTray(SDL_Surface *icon, const char *tooltip) SDL_Tray *tray = NULL; SDL_GtkContext *gtk = SDL_Gtk_EnterContext(); if (!gtk) { - goto error; + goto tray_error; } tray = (SDL_Tray *)SDL_calloc(1, sizeof(*tray)); if (!tray) { - goto error; + goto tray_error; + } + + const gchar *cache_dir = gtk->g.get_user_cache_dir(); + if (!cache_dir) { + SDL_SetError("Cannot get user cache directory: %s", strerror(errno)); + goto tray_error; + } + + char *sdl_dir; + SDL_asprintf(&sdl_dir, "%s/SDL", cache_dir); + if (!SDL_GetPathInfo(sdl_dir, NULL)) { + if (!SDL_CreateDirectory(sdl_dir)) { + SDL_SetError("Cannot create directory for tray icon: %s", strerror(errno)); + goto sdl_dir_error; + } } /* On success, g_mkdtemp edits its argument in-place to replace the Xs * with a random directory name, which it creates safely and atomically. * On failure, it sets errno. */ - SDL_strlcpy(tray->icon_dir, ICON_DIR_TEMPLATE, sizeof(tray->icon_dir)); + SDL_asprintf(&tray->icon_dir, "%s/tray-XXXXXX", sdl_dir); if (!gtk->g.mkdtemp(tray->icon_dir)) { SDL_SetError("Cannot create directory for tray icon: %s", strerror(errno)); - goto error; + goto icon_dir_error; } if (icon) { if (!new_tmp_filename(tray)) { - goto error; + goto icon_dir_error; } SDL_SaveBMP(icon, tray->icon_path); + } else { + // allocate a dummy icon path + SDL_asprintf(&tray->icon_path, " "); } tray->indicator = app_indicator_new(get_appindicator_id(), tray->icon_path, @@ -293,7 +307,13 @@ SDL_Tray *SDL_CreateTray(SDL_Surface *icon, const char *tooltip) return tray; -error: +icon_dir_error: + SDL_free(tray->icon_dir); + +sdl_dir_error: + SDL_free(sdl_dir); + +tray_error: if (tray) { SDL_free(tray); } @@ -311,8 +331,10 @@ void SDL_SetTrayIcon(SDL_Tray *tray, SDL_Surface *icon) return; } - if (*tray->icon_path) { + if (tray->icon_path) { SDL_RemovePath(tray->icon_path); + SDL_free(tray->icon_path); + tray->icon_path = NULL; } /* AppIndicator caches the icon files; always change filename to avoid caching */ @@ -321,7 +343,8 @@ void SDL_SetTrayIcon(SDL_Tray *tray, SDL_Surface *icon) SDL_SaveBMP(icon, tray->icon_path); app_indicator_set_icon(tray->indicator, tray->icon_path); } else { - *tray->icon_path = '\0'; + SDL_free(tray->icon_path); + tray->icon_path = NULL; app_indicator_set_icon(tray->indicator, NULL); } } @@ -714,12 +737,14 @@ void SDL_DestroyTray(SDL_Tray *tray) DestroySDLMenu(tray->menu); } - if (*tray->icon_path) { + if (tray->icon_path) { SDL_RemovePath(tray->icon_path); + SDL_free(tray->icon_path); } - if (*tray->icon_dir) { + if (tray->icon_dir) { SDL_RemovePath(tray->icon_dir); + SDL_free(tray->icon_dir); } SDL_GtkContext *gtk = SDL_Gtk_EnterContext(); From 31ba7efa487450ac4d8f003dbc1bdd2acef06b1c Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Wed, 6 Aug 2025 09:22:15 -0700 Subject: [PATCH 15/61] x11: Refactor dpi hooks, removing GTK dependency and fixing XSettings watcher - Removed GTK signal handler in x11settings. XSettings events are now properly dispatched to X11_XsettingsNotify. Previously events were not being passed to xsettings-client as no SDL xsettings_window was created. Now all events are filtered through xsettings_client_process_event allowing it to process the external window events that are selected. Global content scale is updated for changes to any recognized dpi settings. - X11_GetGlobalContent now reads the current RESOURCE_MANAGER prop off of the root window to ensure it sees the current value. XResourceManagerString is now only used if getting the current prop fails as it caches the current resource manager value per-display connection. - Clean up some warnings in SDL_gtk. --- src/core/unix/SDL_gtk.c | 3 +- src/core/unix/SDL_gtk.h | 2 +- src/events/SDL_events.c | 1 - src/tray/unix/SDL_tray.c | 4 +- src/video/x11/SDL_x11events.c | 17 +---- src/video/x11/SDL_x11modes.c | 117 +++++++++++++++++++------------- src/video/x11/SDL_x11settings.c | 50 +++----------- src/video/x11/SDL_x11settings.h | 6 +- src/video/x11/SDL_x11video.c | 1 + src/video/x11/SDL_x11video.h | 1 + 10 files changed, 87 insertions(+), 115 deletions(-) diff --git a/src/core/unix/SDL_gtk.c b/src/core/unix/SDL_gtk.c index 53af04b21c..d5ba754f67 100644 --- a/src/core/unix/SDL_gtk.c +++ b/src/core/unix/SDL_gtk.c @@ -27,7 +27,8 @@ ctx.sub.fn = (void *)SDL_LoadFunction(lib, #sym) #define SDL_GTK_SYM2(ctx, lib, sub, fn, sym) \ - if (!(ctx.sub.fn = (void *)SDL_LoadFunction(lib, #sym))) { \ + SDL_GTK_SYM2_OPTIONAL(ctx, lib, sub, fn, sym); \ + if (!ctx.sub.fn) { \ return SDL_SetError("Could not load GTK functions"); \ } diff --git a/src/core/unix/SDL_gtk.h b/src/core/unix/SDL_gtk.h index 2ec3ea0b09..73fef5a820 100644 --- a/src/core/unix/SDL_gtk.h +++ b/src/core/unix/SDL_gtk.h @@ -118,7 +118,7 @@ typedef struct SDL_GtkContext extern bool SDL_Gtk_Init(void); extern void SDL_Gtk_Quit(void); extern SDL_GtkContext *SDL_Gtk_EnterContext(void); -extern void SDL_Gtk_ExitContext(SDL_GtkContext *gtk); +extern void SDL_Gtk_ExitContext(SDL_GtkContext *ctx); extern void SDL_UpdateGtk(void); #endif // SDL_gtk_h_ diff --git a/src/events/SDL_events.c b/src/events/SDL_events.c index 19d3e37b47..c04d52b282 100644 --- a/src/events/SDL_events.c +++ b/src/events/SDL_events.c @@ -1445,7 +1445,6 @@ void SDL_PumpEventMaintenance(void) } #endif - // SDL_UpdateTrays will also pump GTK events if needed SDL_UpdateTrays(); SDL_SendPendingSignalEvents(); // in case we had a signal handler fire, etc. diff --git a/src/tray/unix/SDL_tray.c b/src/tray/unix/SDL_tray.c index a9863932b1..874df8b15d 100644 --- a/src/tray/unix/SDL_tray.c +++ b/src/tray/unix/SDL_tray.c @@ -233,7 +233,9 @@ static void DestroySDLMenu(SDL_TrayMenu *menu) void SDL_UpdateTrays(void) { - SDL_UpdateGtk(); + if (SDL_HasActiveTrays()) { + SDL_UpdateGtk(); + } } SDL_Tray *SDL_CreateTray(SDL_Surface *icon, const char *tooltip) diff --git a/src/video/x11/SDL_x11events.c b/src/video/x11/SDL_x11events.c index 4d585d3388..110da587a6 100644 --- a/src/video/x11/SDL_x11events.c +++ b/src/video/x11/SDL_x11events.c @@ -850,16 +850,6 @@ static void X11_HandleClipboardEvent(SDL_VideoDevice *_this, const XEvent *xeven } } -static void X11_HandleSettingsEvent(SDL_VideoDevice *_this, const XEvent *xevent) -{ - SDL_VideoData *videodata = _this->internal; - - SDL_assert(videodata->xsettings_window != None); - SDL_assert(xevent->xany.window == videodata->xsettings_window); - - X11_HandleXsettings(_this, xevent); -} - static Bool isMapNotify(Display *display, XEvent *ev, XPointer arg) { XUnmapEvent *unmap; @@ -1228,11 +1218,8 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent) return; } - if ((videodata->xsettings_window != None) && - (videodata->xsettings_window == xevent->xany.window)) { - X11_HandleSettingsEvent(_this, xevent); - return; - } + // xsettings internally filters events for the windows it watches + X11_HandleXsettingsEvent(_this, xevent); data = X11_FindWindow(_this, xevent->xany.window); diff --git a/src/video/x11/SDL_x11modes.c b/src/video/x11/SDL_x11modes.c index cb6e4386b7..e8cd6d6193 100644 --- a/src/video/x11/SDL_x11modes.c +++ b/src/video/x11/SDL_x11modes.c @@ -50,74 +50,93 @@ float X11_GetGlobalContentScale(SDL_VideoDevice *_this) { - static double scale_factor = 0.0; + double scale_factor = 0.0; - if (scale_factor <= 0.0) { + // First use the forced scaling factor specified by the app/user + const char *hint = SDL_GetHint(SDL_HINT_VIDEO_X11_SCALING_FACTOR); + if (hint && *hint) { + double value = SDL_atof(hint); + if (value >= 1.0f && value <= 10.0f) { + scale_factor = value; + } + } - // First use the forced scaling factor specified by the app/user - const char *hint = SDL_GetHint(SDL_HINT_VIDEO_X11_SCALING_FACTOR); - if (hint && *hint) { - double value = SDL_atof(hint); - if (value >= 1.0f && value <= 10.0f) { - scale_factor = value; - } + // If that failed, try "Xft.dpi" from the XResourcesDatabase... + // We attempt to read this directly to get the live value, XResourceManagerString + // is cached per display connection. + if (scale_factor <= 0.0) + { + SDL_VideoData *data = _this->internal; + Display *display = data->display; + int status, real_format; + Atom real_type; + unsigned long items_read, items_left; + char *resource_manager; + bool owns_resource_manager = false; + + X11_XrmInitialize(); + status = X11_XGetWindowProperty(display, RootWindow(display, DefaultScreen(display)), + data->atoms.RESOURCE_MANAGER, 0L, 8192L, False, XA_STRING, + &real_type, &real_format, &items_read, &items_left, + (unsigned char **)&resource_manager); + + if (status == Success && resource_manager) { + owns_resource_manager = true; + } else { + // Fall back to XResourceManagerString. This will not be updated if the + // dpi value is later changed but should allow getting the initial value. + resource_manager = X11_XResourceManagerString(display); } - // If that failed, try "Xft.dpi" from the XResourcesDatabase... - if (scale_factor <= 0.0) - { - SDL_VideoData *data = _this->internal; - Display *display = data->display; - char *resource_manager; + if (resource_manager) { XrmDatabase db; XrmValue value; char *type; - X11_XrmInitialize(); + db = X11_XrmGetStringDatabase(resource_manager); - resource_manager = X11_XResourceManagerString(display); - if (resource_manager) { - db = X11_XrmGetStringDatabase(resource_manager); - - // Get the value of Xft.dpi from the Database - if (X11_XrmGetResource(db, "Xft.dpi", "String", &type, &value)) { - if (value.addr && type && SDL_strcmp(type, "String") == 0) { - int dpi = SDL_atoi(value.addr); - scale_factor = dpi / 96.0; - } - } - X11_XrmDestroyDatabase(db); - } - } - - // If that failed, try the XSETTINGS keys... - if (scale_factor <= 0.0) { - scale_factor = X11_GetXsettingsIntKey(_this, "Gdk/WindowScalingFactor", -1); - - // The Xft/DPI key is stored in increments of 1024th - if (scale_factor <= 0.0) { - int dpi = X11_GetXsettingsIntKey(_this, "Xft/DPI", -1); - if (dpi > 0) { - scale_factor = (double) dpi / 1024.0; - scale_factor /= 96.0; + // Get the value of Xft.dpi from the Database + if (X11_XrmGetResource(db, "Xft.dpi", "String", &type, &value)) { + if (value.addr && type && SDL_strcmp(type, "String") == 0) { + int dpi = SDL_atoi(value.addr); + scale_factor = dpi / 96.0; } } - } + X11_XrmDestroyDatabase(db); - // If that failed, try the GDK_SCALE envvar... - if (scale_factor <= 0.0) { - const char *scale_str = SDL_getenv("GDK_SCALE"); - if (scale_str) { - scale_factor = SDL_atoi(scale_str); + if (owns_resource_manager) { + X11_XFree(resource_manager); } } + } - // Nothing or a bad value, just fall back to 1.0 + // If that failed, try the XSETTINGS keys... + if (scale_factor <= 0.0) { + scale_factor = X11_GetXsettingsIntKey(_this, "Gdk/WindowScalingFactor", -1); + + // The Xft/DPI key is stored in increments of 1024th if (scale_factor <= 0.0) { - scale_factor = 1.0; + int dpi = X11_GetXsettingsIntKey(_this, "Xft/DPI", -1); + if (dpi > 0) { + scale_factor = (double) dpi / 1024.0; + scale_factor /= 96.0; + } } } + // If that failed, try the GDK_SCALE envvar... + if (scale_factor <= 0.0) { + const char *scale_str = SDL_getenv("GDK_SCALE"); + if (scale_str) { + scale_factor = SDL_atoi(scale_str); + } + } + + // Nothing or a bad value, just fall back to 1.0 + if (scale_factor <= 0.0) { + scale_factor = 1.0; + } + return (float)scale_factor; } diff --git a/src/video/x11/SDL_x11settings.c b/src/video/x11/SDL_x11settings.c index 661d66db83..9ade109991 100644 --- a/src/video/x11/SDL_x11settings.c +++ b/src/video/x11/SDL_x11settings.c @@ -26,9 +26,8 @@ #include "SDL_x11video.h" #include "SDL_x11settings.h" -#include "core/unix/SDL_gtk.h" - #define SDL_XSETTINGS_GDK_WINDOW_SCALING_FACTOR "Gdk/WindowScalingFactor" +#define SDL_XSETTINGS_GDK_UNSCALED_DPI "Gdk/UnscaledDPI" #define SDL_XSETTINGS_XFT_DPI "Xft/DPI" static void UpdateContentScale(SDL_VideoDevice *_this) @@ -44,13 +43,12 @@ static void UpdateContentScale(SDL_VideoDevice *_this) static void X11_XsettingsNotify(const char *name, XSettingsAction action, XSettingsSetting *setting, void *data) { SDL_VideoDevice *_this = data; - UpdateContentScale(_this); -} -static void OnGtkXftDpi(GtkSettings *settings, GParamSpec *pspec, gpointer ptr) -{ - SDL_VideoDevice *_this = (SDL_VideoDevice *)ptr; - UpdateContentScale(_this); + if (SDL_strcmp(name, SDL_XSETTINGS_GDK_WINDOW_SCALING_FACTOR) == 0 || + SDL_strcmp(name, SDL_XSETTINGS_GDK_UNSCALED_DPI) == 0 || + SDL_strcmp(name, SDL_XSETTINGS_XFT_DPI) == 0) { + UpdateContentScale(_this); + } } void X11_InitXsettings(SDL_VideoDevice *_this) @@ -58,25 +56,6 @@ void X11_InitXsettings(SDL_VideoDevice *_this) SDL_VideoData *data = _this->internal; SDLX11_SettingsData *xsettings_data = &data->xsettings_data; - GtkSettings *gtksettings = NULL; - guint xft_dpi_signal_handler_id = 0; - - SDL_GtkContext *gtk = SDL_Gtk_EnterContext(); - if (gtk) { - // Prefer to listen for DPI changes from gtk. In XWayland this is necessary as XSettings - // are not updated dynamically. - gtksettings = gtk->gtk.settings_get_default(); - if (gtksettings) { - xft_dpi_signal_handler_id = gtk->g.signal_connect(gtksettings, "notify::gtk-xft-dpi", &OnGtkXftDpi, _this); - } - SDL_Gtk_ExitContext(gtk); - } - - if (gtksettings && xft_dpi_signal_handler_id) { - xsettings_data->gtksettings = gtksettings; - xsettings_data->xft_dpi_signal_handler_id = xft_dpi_signal_handler_id; - } - xsettings_data->xsettings = xsettings_client_new(data->display, DefaultScreen(data->display), X11_XsettingsNotify, NULL, _this); } @@ -89,28 +68,15 @@ void X11_QuitXsettings(SDL_VideoDevice *_this) if (xsettings_data->xsettings) { xsettings_client_destroy(xsettings_data->xsettings); } - - SDL_GtkContext *gtk = SDL_Gtk_EnterContext(); - if (gtk) { - if (xsettings_data->gtksettings && xsettings_data->xft_dpi_signal_handler_id) { - gtk->g.signal_handler_disconnect(xsettings_data->gtksettings, xsettings_data->xft_dpi_signal_handler_id); - } - SDL_Gtk_ExitContext(gtk); - } - - SDL_zero(xsettings_data); } -void X11_HandleXsettings(SDL_VideoDevice *_this, const XEvent *xevent) +void X11_HandleXsettingsEvent(SDL_VideoDevice *_this, const XEvent *xevent) { SDL_VideoData *data = _this->internal; SDLX11_SettingsData *xsettings_data = &data->xsettings_data; if (xsettings_data->xsettings) { - if (!xsettings_client_process_event(xsettings_data->xsettings, xevent)) { - xsettings_client_destroy(xsettings_data->xsettings); - xsettings_data->xsettings = NULL; - } + xsettings_client_process_event(xsettings_data->xsettings, xevent); } } diff --git a/src/video/x11/SDL_x11settings.h b/src/video/x11/SDL_x11settings.h index 39926b66c6..f91ed70ac7 100644 --- a/src/video/x11/SDL_x11settings.h +++ b/src/video/x11/SDL_x11settings.h @@ -27,17 +27,13 @@ #include #include "xsettings-client.h" -#include "core/unix/SDL_gtk.h" - typedef struct X11_SettingsData { XSettingsClient *xsettings; - GtkSettings *gtksettings; - guint xft_dpi_signal_handler_id; } SDLX11_SettingsData; extern void X11_InitXsettings(SDL_VideoDevice *_this); extern void X11_QuitXsettings(SDL_VideoDevice *_this); -extern void X11_HandleXsettings(SDL_VideoDevice *_this, const XEvent *xevent); +extern void X11_HandleXsettingsEvent(SDL_VideoDevice *_this, const XEvent *xevent); extern int X11_GetXsettingsIntKey(SDL_VideoDevice *_this, const char *key, int fallback_value); #endif // SDL_x11settings_h_ diff --git a/src/video/x11/SDL_x11video.c b/src/video/x11/SDL_x11video.c index 8b23186e1a..ce34f9e6e9 100644 --- a/src/video/x11/SDL_x11video.c +++ b/src/video/x11/SDL_x11video.c @@ -395,6 +395,7 @@ static bool X11_VideoInit(SDL_VideoDevice *_this) GET_ATOM(SDL_SELECTION); GET_ATOM(TARGETS); GET_ATOM(SDL_FORMATS); + GET_ATOM(RESOURCE_MANAGER); GET_ATOM(XdndAware); GET_ATOM(XdndEnter); GET_ATOM(XdndLeave); diff --git a/src/video/x11/SDL_x11video.h b/src/video/x11/SDL_x11video.h index a336a800f5..8d89bf5b76 100644 --- a/src/video/x11/SDL_x11video.h +++ b/src/video/x11/SDL_x11video.h @@ -103,6 +103,7 @@ struct SDL_VideoData Atom SDL_SELECTION; Atom TARGETS; Atom SDL_FORMATS; + Atom RESOURCE_MANAGER; Atom XdndAware; Atom XdndEnter; Atom XdndLeave; From 91be1b054ab5547f93e0d0485c0797042825ff41 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Wed, 6 Aug 2025 09:22:33 -0700 Subject: [PATCH 16/61] x11: Fix xsettings pointer not being reset in X11_QuitXsettings --- src/video/x11/SDL_x11settings.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/video/x11/SDL_x11settings.c b/src/video/x11/SDL_x11settings.c index 9ade109991..50bf773f3a 100644 --- a/src/video/x11/SDL_x11settings.c +++ b/src/video/x11/SDL_x11settings.c @@ -67,6 +67,7 @@ void X11_QuitXsettings(SDL_VideoDevice *_this) if (xsettings_data->xsettings) { xsettings_client_destroy(xsettings_data->xsettings); + xsettings_data->xsettings = NULL; } } From 67e513044185ff27d4f466900995341d79c6c012 Mon Sep 17 00:00:00 2001 From: Frank Praznik Date: Mon, 4 Aug 2025 19:43:42 -0400 Subject: [PATCH 17/61] x11: Check axis labels when searching for relative axes Prefer axes with the 'Rel X'/'Rel Y' labels, followed by 'Abs X'/'Abs Y', and only fall back to the old behavior of using the first two enumerated axes if no others are found. Fixes a FIXME when determining which axes to use for relative motion. --- src/video/x11/SDL_x11mouse.h | 1 + src/video/x11/SDL_x11xinput2.c | 129 +++++++++++++++++++++------------ 2 files changed, 83 insertions(+), 47 deletions(-) diff --git a/src/video/x11/SDL_x11mouse.h b/src/video/x11/SDL_x11mouse.h index 2a2973c3d7..db6f00c4eb 100644 --- a/src/video/x11/SDL_x11mouse.h +++ b/src/video/x11/SDL_x11mouse.h @@ -26,6 +26,7 @@ typedef struct SDL_XInput2DeviceInfo { int device_id; + int number[2]; bool relative[2]; double minval[2]; double maxval[2]; diff --git a/src/video/x11/SDL_x11xinput2.c b/src/video/x11/SDL_x11xinput2.c index d78c0be5e2..5da1bb791a 100644 --- a/src/video/x11/SDL_x11xinput2.c +++ b/src/video/x11/SDL_x11xinput2.c @@ -45,6 +45,11 @@ static bool xinput2_multitouch_supported; * this extension */ static int xinput2_opcode; +static Atom xinput2_rel_x_atom; +static Atom xinput2_rel_y_atom; +static Atom xinput2_abs_x_atom; +static Atom xinput2_abs_y_atom; + #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_SCROLLINFO typedef struct { @@ -67,23 +72,40 @@ static int scrollable_device_count; static bool xinput2_scrolling_supported; #endif -static void parse_valuators(const double *input_values, const unsigned char *mask, int mask_len, - double *output_values, int output_values_len) +static void parse_relative_valuators(SDL_XInput2DeviceInfo *devinfo, const XIRawEvent *rawev) { - int i = 0, z = 0; - int top = mask_len * 8; - if (top > MAX_AXIS) { - top = MAX_AXIS; + double processed_coords[2] = { 0.0, 0.0 }; + int values_i = 0, found = 0; + + for (int i = 0; i < rawev->valuators.mask_len * 8 && found < 2; ++i) { + if (!XIMaskIsSet(rawev->valuators.mask, i)) { + continue; + } + + for (int j = 0; j < 2; ++j) { + if (devinfo->number[j] == i) { + const double current_val = rawev->valuators.values[values_i]; + + if (devinfo->relative[j]) { + processed_coords[j] = current_val; + } else { + processed_coords[j] = devinfo->prev_coords[j] - current_val; // convert absolute to relative + } + + devinfo->prev_coords[j] = current_val; + ++found; + + break; + } + } + + ++values_i; } - SDL_memset(output_values, 0, output_values_len * sizeof(double)); - for (; i < top && z < output_values_len; i++) { - if (XIMaskIsSet(mask, i)) { - const int value = (int)*input_values; - output_values[z] = value; - input_values++; - } - z++; + // Relative mouse motion is delivered to the window with keyboard focus + SDL_Mouse *mouse = SDL_GetMouse(); + if (mouse->relative_mode && SDL_GetKeyboardFocus()) { + SDL_SendMouseMotion(rawev->time, mouse->focus, (SDL_MouseID)rawev->sourceid, true, (float)processed_coords[0], (float)processed_coords[1]); } } @@ -260,6 +282,12 @@ bool X11_InitXinput2(SDL_VideoDevice *_this) xinput2_multitouch_supported = xinput2_version_atleast(version, 2, 2); #endif + // Populate the atoms for finding relative axes + xinput2_rel_x_atom = X11_XInternAtom(data->display, "Rel X", False); + xinput2_rel_y_atom = X11_XInternAtom(data->display, "Rel Y", False); + xinput2_abs_x_atom = X11_XInternAtom(data->display, "Abs X", False); + xinput2_abs_y_atom = X11_XInternAtom(data->display, "Abs Y", False); + // Enable raw motion events for this display SDL_zero(eventmask); SDL_zeroa(mask); @@ -345,7 +373,6 @@ static SDL_XInput2DeviceInfo *xinput2_get_device_info(SDL_VideoData *videodata, SDL_XInput2DeviceInfo *prev = NULL; SDL_XInput2DeviceInfo *devinfo; XIDeviceInfo *xidevinfo; - int axis = 0; int i; for (devinfo = videodata->mouse_device_info; devinfo; devinfo = devinfo->next) { @@ -375,18 +402,49 @@ static SDL_XInput2DeviceInfo *xinput2_get_device_info(SDL_VideoData *videodata, devinfo->device_id = device_id; - /* !!! FIXME: this is sort of hacky because we only care about the first two axes we see, but any given - !!! FIXME: axis could be relative or absolute, and they might not even be the X and Y axes! - !!! FIXME: But we go on, for now. Maybe we need a more robust mouse API in SDL3... */ + /* Search for relative axes with the following priority: + * - Labelled 'Rel X'/'Rel Y' + * - Labelled 'Abs X'/'Abs Y' + * - The first two axes found + */ + bool have_rel_x = false, have_rel_y = false; + bool have_abs_x = false, have_abs_y = false; + int axis_index = 0; for (i = 0; i < xidevinfo->num_classes; i++) { const XIValuatorClassInfo *v = (const XIValuatorClassInfo *)xidevinfo->classes[i]; if (v->type == XIValuatorClass) { - devinfo->relative[axis] = (v->mode == XIModeRelative); - devinfo->minval[axis] = v->min; - devinfo->maxval[axis] = v->max; - if (++axis >= 2) { + if (v->label == xinput2_rel_x_atom || (v->label == xinput2_abs_x_atom && !have_rel_x) || + (axis_index == 0 && !have_rel_x && !have_abs_x)) { + devinfo->number[0] = v->number; + devinfo->relative[0] = (v->mode == XIModeRelative); + devinfo->minval[0] = v->min; + devinfo->maxval[0] = v->max; + + if (v->label == xinput2_rel_x_atom) { + have_rel_x = true; + } else if (v->label == xinput2_abs_x_atom) { + have_abs_x = true; + } + } else if (v->label == xinput2_rel_y_atom || (v->label == xinput2_abs_y_atom && !have_rel_y) || + (axis_index == 1 && !have_rel_y && !have_abs_y)) { + devinfo->number[1] = v->number; + devinfo->relative[1] = (v->mode == XIModeRelative); + devinfo->minval[1] = v->min; + devinfo->maxval[1] = v->max; + + if (v->label == xinput2_rel_y_atom) { + have_rel_y = true; + } else if (v->label == xinput2_abs_y_atom) { + have_abs_y = true; + } + } + + // If two relative axes were found, nothing more to do. + if (have_rel_x && have_rel_y) { break; } + + ++axis_index; } } @@ -437,41 +495,18 @@ void X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie) { const XIRawEvent *rawev = (const XIRawEvent *)cookie->data; const bool is_pen = X11_FindPenByDeviceID(rawev->sourceid) != NULL; - SDL_Mouse *mouse = SDL_GetMouse(); - SDL_XInput2DeviceInfo *devinfo; - double coords[2]; - double processed_coords[2]; - int i; - Uint64 timestamp = X11_GetEventTimestamp(rawev->time); videodata->global_mouse_changed = true; if (is_pen) { break; // Pens check for XI_Motion instead } - devinfo = xinput2_get_device_info(videodata, rawev->deviceid); + SDL_XInput2DeviceInfo *devinfo = xinput2_get_device_info(videodata, rawev->deviceid); if (!devinfo) { break; // oh well. } - parse_valuators(rawev->raw_values, rawev->valuators.mask, - rawev->valuators.mask_len, coords, 2); - - for (i = 0; i < 2; i++) { - if (devinfo->relative[i]) { - processed_coords[i] = coords[i]; - } else { - processed_coords[i] = devinfo->prev_coords[i] - coords[i]; // convert absolute to relative - } - } - - // Relative mouse motion is delivered to the window with keyboard focus - if (mouse->relative_mode && SDL_GetKeyboardFocus()) { - SDL_SendMouseMotion(timestamp, mouse->focus, (SDL_MouseID)rawev->sourceid, true, (float)processed_coords[0], (float)processed_coords[1]); - } - - devinfo->prev_coords[0] = coords[0]; - devinfo->prev_coords[1] = coords[1]; + parse_relative_valuators(devinfo, rawev); } break; case XI_KeyPress: From f439e4477181b6ea82ad532348dfc02025b83b8c Mon Sep 17 00:00:00 2001 From: Frank Praznik Date: Thu, 24 Jul 2025 15:11:20 -0400 Subject: [PATCH 18/61] x11: Modernize and optimize key handling - Use modern Xkb functions where appropriate and cleanly separate the modern and legacy paths. - Remove the deprecated XKeycodeToKeysym function in favor of directly querying the keymap on the legacy path. - Look up virtual modifiers by name on the Xkb path to better handle remapping (equivalent to the modifier handling under Wayland). - Optimize keymap creation on the Xkb path to cut keymap build times and enable fast group switching (equivalent to keymap handling on Wayland). - Enable and handle Xkb events to handle changes to the group, mapping, and modifier states. This is more reliable than using the legacy events (group changes may not arrive if the window lacks pointer focus), and better handles cases where modifiers are latched, locked, or activated externally rather than physically pressed. --- cmake/sdlchecks.cmake | 2 +- include/build_config/SDL_build_config.h.cmake | 2 +- src/video/x11/SDL_x11dyn.h | 2 +- src/video/x11/SDL_x11events.c | 391 ++++++++++++------ src/video/x11/SDL_x11keyboard.c | 385 ++++++++++------- src/video/x11/SDL_x11keyboard.h | 1 - src/video/x11/SDL_x11sym.h | 27 +- src/video/x11/SDL_x11video.h | 46 ++- 8 files changed, 551 insertions(+), 305 deletions(-) diff --git a/cmake/sdlchecks.cmake b/cmake/sdlchecks.cmake index 8eda0dfe56..865e28f6d5 100644 --- a/cmake/sdlchecks.cmake +++ b/cmake/sdlchecks.cmake @@ -390,7 +390,7 @@ macro(CheckX11) set(SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS 1) endif() - check_symbol_exists(XkbLookupKeySym "X11/Xlib.h;X11/XKBlib.h" SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM) + check_include_file("X11/XKBlib.h" SDL_VIDEO_DRIVER_X11_HAS_XKBLIB) if(SDL_X11_XCURSOR AND HAVE_XCURSOR_H AND XCURSOR_LIB) set(HAVE_X11_XCURSOR TRUE) diff --git a/include/build_config/SDL_build_config.h.cmake b/include/build_config/SDL_build_config.h.cmake index 03d75e3864..8965e6db48 100644 --- a/include/build_config/SDL_build_config.h.cmake +++ b/include/build_config/SDL_build_config.h.cmake @@ -422,7 +422,7 @@ #cmakedefine SDL_VIDEO_DRIVER_X11_DYNAMIC_XRANDR @SDL_VIDEO_DRIVER_X11_DYNAMIC_XRANDR@ #cmakedefine SDL_VIDEO_DRIVER_X11_DYNAMIC_XSS @SDL_VIDEO_DRIVER_X11_DYNAMIC_XSS@ #cmakedefine SDL_VIDEO_DRIVER_X11_DYNAMIC_XTEST @SDL_VIDEO_DRIVER_X11_DYNAMIC_XTEST@ -#cmakedefine SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM 1 +#cmakedefine SDL_VIDEO_DRIVER_X11_HAS_XKBLIB 1 #cmakedefine SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS 1 #cmakedefine SDL_VIDEO_DRIVER_X11_XCURSOR 1 #cmakedefine SDL_VIDEO_DRIVER_X11_XDBE 1 diff --git a/src/video/x11/SDL_x11dyn.h b/src/video/x11/SDL_x11dyn.h index b5bfbf5a09..23b829c2a9 100644 --- a/src/video/x11/SDL_x11dyn.h +++ b/src/video/x11/SDL_x11dyn.h @@ -28,7 +28,7 @@ #include #include -#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM +#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLIB #include #endif diff --git a/src/video/x11/SDL_x11events.c b/src/video/x11/SDL_x11events.c index 110da587a6..67128c85b5 100644 --- a/src/video/x11/SDL_x11events.c +++ b/src/video/x11/SDL_x11events.c @@ -248,94 +248,202 @@ static void X11_HandleGenericEvent(SDL_VideoDevice *_this, XEvent *xev) static void X11_UpdateSystemKeyModifiers(SDL_VideoData *viddata) { - Window junk_window; - int x, y; +#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLIB + if (viddata->keyboard.xkb_enabled) { + XkbStateRec xkb_state; + if (X11_XkbGetState(viddata->display, XkbUseCoreKbd, &xkb_state) == Success) { + viddata->keyboard.pressed_modifiers = xkb_state.base_mods; + viddata->keyboard.locked_modifiers = xkb_state.latched_mods | xkb_state.locked_mods; + } + } else +#endif + { + Window junk_window; + int x, y; + unsigned int mod_mask; - X11_XQueryPointer(viddata->display, DefaultRootWindow(viddata->display), &junk_window, &junk_window, &x, &y, &x, &y, &viddata->xkb.xkb_modifiers); + X11_XQueryPointer(viddata->display, DefaultRootWindow(viddata->display), &junk_window, &junk_window, &x, &y, &x, &y, &mod_mask); + viddata->keyboard.pressed_modifiers = mod_mask & (ShiftMask | ControlMask | Mod1Mask | Mod3Mask | Mod4Mask | Mod5Mask); + viddata->keyboard.locked_modifiers = mod_mask & (LockMask | viddata->keyboard.numlock_mask | viddata->keyboard.scrolllock_mask); + } } -static void X11_ReconcileModifiers(SDL_VideoData *viddata) +static void X11_ReconcileModifiers(SDL_VideoData *viddata, bool key_pressed) { - const Uint32 xk_modifiers = viddata->xkb.xkb_modifiers; - - /* If a modifier was activated by a keypress, it will be tied to the - * specific left/right key that initiated it. Otherwise, the ambiguous - * left/right combo is used. + /* Handle explicit pressed modifier state. This will correct the modifier state + * if common modifier keys were remapped and the modifiers presumed to be set + * during a key press event were incorrect, if the modifier was set to the + * pressed state via means other than pressing the physical key, or if the + * modifier state was set by a keypress before the corresponding key event + * was received. */ - if (xk_modifiers & ShiftMask) { - if (!(viddata->xkb.sdl_modifiers & SDL_KMOD_SHIFT)) { - viddata->xkb.sdl_modifiers |= SDL_KMOD_SHIFT; + if (key_pressed) { + if (viddata->keyboard.pressed_modifiers & ShiftMask) { + if (viddata->keyboard.sdl_physically_pressed_modifiers & SDL_KMOD_SHIFT) { + viddata->keyboard.sdl_pressed_modifiers &= ~SDL_KMOD_SHIFT; + viddata->keyboard.sdl_pressed_modifiers |= (viddata->keyboard.sdl_physically_pressed_modifiers & SDL_KMOD_SHIFT); + } + } + + if (viddata->keyboard.pressed_modifiers & ControlMask) { + if (viddata->keyboard.sdl_physically_pressed_modifiers & SDL_KMOD_CTRL) { + viddata->keyboard.sdl_pressed_modifiers &= ~SDL_KMOD_CTRL; + viddata->keyboard.sdl_pressed_modifiers |= (viddata->keyboard.sdl_physically_pressed_modifiers & SDL_KMOD_CTRL); + } + } + + if (viddata->keyboard.pressed_modifiers & viddata->keyboard.alt_mask) { + if (viddata->keyboard.sdl_physically_pressed_modifiers & SDL_KMOD_ALT) { + viddata->keyboard.sdl_pressed_modifiers &= ~SDL_KMOD_ALT; + viddata->keyboard.sdl_pressed_modifiers |= (viddata->keyboard.sdl_physically_pressed_modifiers & SDL_KMOD_ALT); + } + } + + if (viddata->keyboard.pressed_modifiers & viddata->keyboard.gui_mask) { + if (viddata->keyboard.sdl_physically_pressed_modifiers & SDL_KMOD_GUI) { + viddata->keyboard.sdl_pressed_modifiers &= ~SDL_KMOD_GUI; + viddata->keyboard.sdl_pressed_modifiers |= (viddata->keyboard.sdl_physically_pressed_modifiers & SDL_KMOD_GUI); + } } } else { - viddata->xkb.sdl_modifiers &= ~SDL_KMOD_SHIFT; + if (viddata->keyboard.pressed_modifiers & ShiftMask) { + if (!(viddata->keyboard.sdl_pressed_modifiers & SDL_KMOD_SHIFT)) { + viddata->keyboard.sdl_pressed_modifiers |= SDL_KMOD_SHIFT; + } + } else { + viddata->keyboard.sdl_pressed_modifiers &= ~SDL_KMOD_SHIFT; + } + + if (viddata->keyboard.pressed_modifiers & ControlMask) { + if (!(viddata->keyboard.sdl_pressed_modifiers & SDL_KMOD_CTRL)) { + viddata->keyboard.sdl_pressed_modifiers |= SDL_KMOD_CTRL; + } + } else { + viddata->keyboard.sdl_pressed_modifiers &= ~SDL_KMOD_CTRL; + } + + if (viddata->keyboard.pressed_modifiers & viddata->keyboard.alt_mask) { + if (!(viddata->keyboard.sdl_pressed_modifiers & SDL_KMOD_ALT)) { + viddata->keyboard.sdl_pressed_modifiers |= SDL_KMOD_ALT; + } + } else { + viddata->keyboard.sdl_pressed_modifiers &= ~SDL_KMOD_ALT; + } + + if (viddata->keyboard.pressed_modifiers & viddata->keyboard.gui_mask) { + if (!(viddata->keyboard.sdl_pressed_modifiers & SDL_KMOD_GUI)) { + viddata->keyboard.sdl_pressed_modifiers |= SDL_KMOD_GUI; + } + } else { + viddata->keyboard.sdl_pressed_modifiers &= ~SDL_KMOD_GUI; + } + + if (viddata->keyboard.pressed_modifiers & viddata->keyboard.level3_mask) { + if (!(viddata->keyboard.sdl_pressed_modifiers & SDL_KMOD_MODE)) { + viddata->keyboard.sdl_pressed_modifiers |= SDL_KMOD_MODE; + } + } else { + viddata->keyboard.sdl_pressed_modifiers &= ~SDL_KMOD_MODE; + } + + if (viddata->keyboard.pressed_modifiers & viddata->keyboard.level5_mask) { + if (!(viddata->keyboard.sdl_pressed_modifiers & SDL_KMOD_LEVEL5)) { + viddata->keyboard.sdl_pressed_modifiers |= SDL_KMOD_LEVEL5; + } + } else { + viddata->keyboard.sdl_pressed_modifiers &= ~SDL_KMOD_LEVEL5; + } } - if (xk_modifiers & ControlMask) { - if (!(viddata->xkb.sdl_modifiers & SDL_KMOD_CTRL)) { - viddata->xkb.sdl_modifiers |= SDL_KMOD_CTRL; + /* If a latch or lock was activated by a keypress, the latch/lock will + * be tied to the specific left/right key that initiated it. Otherwise, + * the ambiguous left/right combo is used. + * + * The modifier will remain active until the latch/lock is released by + * the system. + */ + if (viddata->keyboard.locked_modifiers & ShiftMask) { + if (viddata->keyboard.sdl_pressed_modifiers & SDL_KMOD_SHIFT) { + viddata->keyboard.sdl_locked_modifiers &= ~SDL_KMOD_SHIFT; + viddata->keyboard.sdl_locked_modifiers |= (viddata->keyboard.sdl_pressed_modifiers & SDL_KMOD_SHIFT); + } else if (!(viddata->keyboard.sdl_locked_modifiers & SDL_KMOD_SHIFT)) { + viddata->keyboard.sdl_locked_modifiers |= SDL_KMOD_SHIFT; } } else { - viddata->xkb.sdl_modifiers &= ~SDL_KMOD_CTRL; + viddata->keyboard.sdl_locked_modifiers &= ~SDL_KMOD_SHIFT; } - // Mod1 is used for the Alt keys - if (xk_modifiers & Mod1Mask) { - if (!(viddata->xkb.sdl_modifiers & SDL_KMOD_ALT)) { - viddata->xkb.sdl_modifiers |= SDL_KMOD_ALT; + if (viddata->keyboard.locked_modifiers & ControlMask) { + if (viddata->keyboard.sdl_pressed_modifiers & SDL_KMOD_CTRL) { + viddata->keyboard.sdl_locked_modifiers &= ~SDL_KMOD_CTRL; + viddata->keyboard.sdl_locked_modifiers |= (viddata->keyboard.sdl_pressed_modifiers & SDL_KMOD_CTRL); + } else if (!(viddata->keyboard.sdl_locked_modifiers & SDL_KMOD_CTRL)) { + viddata->keyboard.sdl_locked_modifiers |= SDL_KMOD_CTRL; } } else { - viddata->xkb.sdl_modifiers &= ~SDL_KMOD_ALT; + viddata->keyboard.sdl_locked_modifiers &= ~SDL_KMOD_CTRL; } - // Mod4 is used for the Super (aka GUI/Logo) keys. - if (xk_modifiers & Mod4Mask) { - if (!(viddata->xkb.sdl_modifiers & SDL_KMOD_GUI)) { - viddata->xkb.sdl_modifiers |= SDL_KMOD_GUI; + if (viddata->keyboard.locked_modifiers & viddata->keyboard.alt_mask) { + if (viddata->keyboard.sdl_pressed_modifiers & SDL_KMOD_ALT) { + viddata->keyboard.sdl_locked_modifiers &= ~SDL_KMOD_ALT; + viddata->keyboard.sdl_locked_modifiers |= (viddata->keyboard.sdl_pressed_modifiers & SDL_KMOD_ALT); + } else if (!(viddata->keyboard.sdl_locked_modifiers & SDL_KMOD_ALT)) { + viddata->keyboard.sdl_locked_modifiers |= SDL_KMOD_ALT; } } else { - viddata->xkb.sdl_modifiers &= ~SDL_KMOD_GUI; + viddata->keyboard.sdl_locked_modifiers &= ~SDL_KMOD_ALT; } - // Mod3 is typically Level 5 shift. - if (xk_modifiers & Mod3Mask) { - viddata->xkb.sdl_modifiers |= SDL_KMOD_LEVEL5; + if (viddata->keyboard.locked_modifiers & viddata->keyboard.gui_mask) { + if (viddata->keyboard.sdl_pressed_modifiers & SDL_KMOD_GUI) { + viddata->keyboard.sdl_locked_modifiers &= ~SDL_KMOD_GUI; + viddata->keyboard.sdl_locked_modifiers |= (viddata->keyboard.sdl_pressed_modifiers & SDL_KMOD_GUI); + } else if (!(viddata->keyboard.sdl_locked_modifiers & SDL_KMOD_GUI)) { + viddata->keyboard.sdl_locked_modifiers |= SDL_KMOD_GUI; + } } else { - viddata->xkb.sdl_modifiers &= ~SDL_KMOD_LEVEL5; + viddata->keyboard.sdl_locked_modifiers &= ~SDL_KMOD_GUI; } - // Mod5 is typically Level 3 shift (aka AltGr). - if (xk_modifiers & Mod5Mask) { - viddata->xkb.sdl_modifiers |= SDL_KMOD_MODE; + if (viddata->keyboard.locked_modifiers & viddata->keyboard.level3_mask) { + viddata->keyboard.sdl_locked_modifiers |= SDL_KMOD_MODE; } else { - viddata->xkb.sdl_modifiers &= ~SDL_KMOD_MODE; + viddata->keyboard.sdl_locked_modifiers &= ~SDL_KMOD_MODE; } - if (xk_modifiers & LockMask) { - viddata->xkb.sdl_modifiers |= SDL_KMOD_CAPS; + if (viddata->keyboard.locked_modifiers & viddata->keyboard.level5_mask) { + viddata->keyboard.sdl_locked_modifiers |= SDL_KMOD_LEVEL5; } else { - viddata->xkb.sdl_modifiers &= ~SDL_KMOD_CAPS; + viddata->keyboard.sdl_locked_modifiers &= ~SDL_KMOD_LEVEL5; } - if (xk_modifiers & viddata->xkb.numlock_mask) { - viddata->xkb.sdl_modifiers |= SDL_KMOD_NUM; + // Capslock, Numlock, and Scrolllock can only be locked, not pressed. + if (viddata->keyboard.locked_modifiers & LockMask) { + viddata->keyboard.sdl_locked_modifiers |= SDL_KMOD_CAPS; } else { - viddata->xkb.sdl_modifiers &= ~SDL_KMOD_NUM; + viddata->keyboard.sdl_locked_modifiers &= ~SDL_KMOD_CAPS; } - if (xk_modifiers & viddata->xkb.scrolllock_mask) { - viddata->xkb.sdl_modifiers |= SDL_KMOD_SCROLL; + if (viddata->keyboard.locked_modifiers & viddata->keyboard.numlock_mask) { + viddata->keyboard.sdl_locked_modifiers |= SDL_KMOD_NUM; } else { - viddata->xkb.sdl_modifiers &= ~SDL_KMOD_SCROLL; + viddata->keyboard.sdl_locked_modifiers &= ~SDL_KMOD_NUM; } - SDL_SetModState(viddata->xkb.sdl_modifiers); + if (viddata->keyboard.locked_modifiers & viddata->keyboard.scrolllock_mask) { + viddata->keyboard.sdl_locked_modifiers |= SDL_KMOD_SCROLL; + } else { + viddata->keyboard.sdl_locked_modifiers &= ~SDL_KMOD_SCROLL; + } + + SDL_SetModState(viddata->keyboard.sdl_pressed_modifiers | viddata->keyboard.sdl_locked_modifiers); } -static void X11_HandleModifierKeys(SDL_VideoData *viddata, SDL_Scancode scancode, bool pressed, bool allow_reconciliation) +static void X11_HandleModifierKeys(SDL_VideoData *viddata, SDL_Scancode scancode, bool pressed) { const SDL_Keycode keycode = SDL_GetKeyFromScancode(scancode, SDL_KMOD_NONE, false); SDL_Keymod mod = SDL_KMOD_NONE; - bool reconcile = false; /* SDL clients expect modifier state to be activated at the same time as the * source keypress, so we set pressed modifier state with the usual modifier @@ -378,48 +486,46 @@ static void X11_HandleModifierKeys(SDL_VideoData *viddata, SDL_Scancode scancode case SDLK_NUMLOCKCLEAR: case SDLK_SCROLLLOCK: { - /* For locking modifier keys, query the lock state directly, or we may have to wait until the next - * key press event to know if a lock was actually activated from the key event. - */ - unsigned int cur_mask = viddata->xkb.xkb_modifiers; - X11_UpdateSystemKeyModifiers(viddata); + // XKB provides the latched/locked state explicitly. + if (viddata->keyboard.xkb_enabled) { + /* For locking modifier keys, query the lock state directly, or we may have to wait until the next + * key press event to know if a lock was actually activated from the key event. + */ + unsigned int cur_mask = viddata->keyboard.locked_modifiers; + X11_UpdateSystemKeyModifiers(viddata); - if (viddata->xkb.xkb_modifiers & LockMask) { - cur_mask |= LockMask; - } else { - cur_mask &= ~LockMask; - } - if (viddata->xkb.xkb_modifiers & viddata->xkb.numlock_mask) { - cur_mask |= viddata->xkb.numlock_mask; - } else { - cur_mask &= ~viddata->xkb.numlock_mask; - } - if (viddata->xkb.xkb_modifiers & viddata->xkb.scrolllock_mask) { - cur_mask |= viddata->xkb.scrolllock_mask; - } else { - cur_mask &= ~viddata->xkb.scrolllock_mask; - } + if (viddata->keyboard.locked_modifiers & LockMask) { + cur_mask |= LockMask; + } else { + cur_mask &= ~LockMask; + } + if (viddata->keyboard.locked_modifiers & viddata->keyboard.numlock_mask) { + cur_mask |= viddata->keyboard.numlock_mask; + } else { + cur_mask &= ~viddata->keyboard.numlock_mask; + } + if (viddata->keyboard.locked_modifiers & viddata->keyboard.scrolllock_mask) { + cur_mask |= viddata->keyboard.scrolllock_mask; + } else { + cur_mask &= ~viddata->keyboard.scrolllock_mask; + } - viddata->xkb.xkb_modifiers = cur_mask; - } SDL_FALLTHROUGH; + viddata->keyboard.locked_modifiers = cur_mask; + } + } break; default: - reconcile = true; - break; + return; } if (pressed) { - viddata->xkb.sdl_modifiers |= mod; + viddata->keyboard.sdl_pressed_modifiers |= mod; + viddata->keyboard.sdl_physically_pressed_modifiers |= mod; } else { - viddata->xkb.sdl_modifiers &= ~mod; + viddata->keyboard.sdl_pressed_modifiers &= ~mod; + viddata->keyboard.sdl_physically_pressed_modifiers &= ~mod; } - if (allow_reconciliation) { - if (reconcile) { - X11_ReconcileModifiers(viddata); - } else { - SDL_SetModState(viddata->xkb.sdl_modifiers); - } - } + X11_ReconcileModifiers(viddata, true); } void X11_ReconcileKeyboardState(SDL_VideoDevice *_this) @@ -427,18 +533,25 @@ void X11_ReconcileKeyboardState(SDL_VideoDevice *_this) SDL_VideoData *videodata = _this->internal; Display *display = videodata->display; char keys[32]; - int keycode; - const bool *keyboardState; + + // Rebuild the modifier state in case it changed while focus was lost. + X11_UpdateSystemKeyModifiers(videodata); + X11_ReconcileModifiers(videodata, false); + + // Keep caps, num, and scroll, but clear the others until we have updated key state. + videodata->keyboard.sdl_pressed_modifiers = 0; + videodata->keyboard.sdl_physically_pressed_modifiers = 0; + videodata->keyboard.sdl_locked_modifiers &= SDL_KMOD_CAPS | SDL_KMOD_NUM | SDL_KMOD_SCROLL; + videodata->keyboard.pressed_modifiers = 0; + videodata->keyboard.locked_modifiers &= LockMask | videodata->keyboard.numlock_mask| videodata->keyboard.scrolllock_mask; X11_XQueryKeymap(display, keys); - keyboardState = SDL_GetKeyboardState(0); - for (keycode = 0; keycode < SDL_arraysize(videodata->key_layout); ++keycode) { - SDL_Scancode scancode = videodata->key_layout[keycode]; - bool x11KeyPressed = (keys[keycode / 8] & (1 << (keycode % 8))) != 0; - bool sdlKeyPressed = keyboardState[scancode]; + for (Uint32 keycode = 0; keycode < SDL_arraysize(videodata->keyboard.key_layout); ++keycode) { + const SDL_Scancode scancode = videodata->keyboard.key_layout[keycode]; + const bool x11KeyPressed = (keys[keycode / 8] & (1 << (keycode % 8))) != 0; - if (x11KeyPressed && !sdlKeyPressed) { + if (x11KeyPressed) { // Only update modifier state for keys that are pressed in another application switch (SDL_GetKeyFromScancode(scancode, SDL_KMOD_NONE, false)) { case SDLK_LCTRL: @@ -451,20 +564,18 @@ void X11_ReconcileKeyboardState(SDL_VideoDevice *_this) case SDLK_RGUI: case SDLK_MODE: case SDLK_LEVEL5_SHIFT: - X11_HandleModifierKeys(videodata, scancode, true, false); + X11_HandleModifierKeys(videodata, scancode, true); SDL_SendKeyboardKeyIgnoreModifiers(0, SDL_GLOBAL_KEYBOARD_ID, keycode, scancode, true); break; default: break; } - } else if (!x11KeyPressed && sdlKeyPressed) { - X11_HandleModifierKeys(videodata, scancode, false, false); - SDL_SendKeyboardKeyIgnoreModifiers(0, SDL_GLOBAL_KEYBOARD_ID, keycode, scancode, false); } } + // Update the latched/locked state for modifiers other than Caps, Num, and Scroll lock. X11_UpdateSystemKeyModifiers(videodata); - X11_ReconcileModifiers(videodata); + X11_ReconcileModifiers(videodata, false); } static void X11_DispatchFocusIn(SDL_VideoDevice *_this, SDL_WindowData *data) @@ -933,7 +1044,7 @@ void X11_HandleKeyEvent(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_ Status status = 0; bool handled_by_ime = false; bool pressed = (xevent->type == KeyPress); - SDL_Scancode scancode = videodata->key_layout[keycode]; + SDL_Scancode scancode = videodata->keyboard.key_layout[keycode]; Uint64 timestamp = X11_GetEventTimestamp(xevent->xkey.time); #ifdef DEBUG_XEVENTS @@ -951,7 +1062,12 @@ void X11_HandleKeyEvent(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_ #endif // DEBUG SCANCODES text[0] = '\0'; - videodata->xkb.xkb_modifiers = xevent->xkey.state; + + // XKB updates the modifiers explicitly via a state event. + if (!videodata->keyboard.xkb_enabled) { + videodata->keyboard.pressed_modifiers = xevent->xkey.state & (ShiftMask | ControlMask | Mod1Mask | Mod3Mask | Mod4Mask | Mod5Mask); + videodata->keyboard.locked_modifiers = xevent->xkey.state & (LockMask | videodata->keyboard.numlock_mask | videodata->keyboard.scrolllock_mask); + } if (SDL_TextInputActive(windowdata->window)) { // filter events catches XIM events and sends them to the correct handler @@ -979,7 +1095,7 @@ void X11_HandleKeyEvent(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_ if (!handled_by_ime) { if (pressed) { - X11_HandleModifierKeys(videodata, scancode, true, true); + X11_HandleModifierKeys(videodata, scancode, true); SDL_SendKeyboardKeyIgnoreModifiers(timestamp, keyboardID, keycode, scancode, true); if (*text && !(SDL_GetModState() & (SDL_KMOD_CTRL | SDL_KMOD_ALT))) { @@ -993,7 +1109,7 @@ void X11_HandleKeyEvent(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_ return; } - X11_HandleModifierKeys(videodata, scancode, false, true); + X11_HandleModifierKeys(videodata, scancode, false); SDL_SendKeyboardKeyIgnoreModifiers(timestamp, keyboardID, keycode, scancode, false); } } @@ -1225,37 +1341,78 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent) if (!data) { // The window for KeymapNotify, etc events is 0 +#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLIB + if (videodata->keyboard.xkb_enabled && xevent->type == videodata->keyboard.xkb.event) { + XkbEvent *xkbevent = (XkbEvent *)xevent; + switch (xkbevent->any.xkb_type) { + case XkbStateNotify: + { +#ifdef DEBUG_XEVENTS + SDL_Log("window 0x%lx: XkbStateNotify!", xevent->xany.window); +#endif + if ((xkbevent->state.changed & XkbGroupStateMask) && xkbevent->state.group != videodata->keyboard.xkb.current_group) { + videodata->keyboard.xkb.current_group = xkbevent->state.group; + SDL_SetKeymap(videodata->keyboard.xkb.keymaps[videodata->keyboard.xkb.current_group], true); + } + + if (xkbevent->state.changed & XkbModifierStateMask) { + videodata->keyboard.pressed_modifiers = xkbevent->state.base_mods; + videodata->keyboard.locked_modifiers = xkbevent->state.latched_mods | xkbevent->state.locked_mods; + X11_ReconcileModifiers(videodata, false); + } + } break; + + case XkbMapNotify: +#ifdef DEBUG_XEVENTS + SDL_Log("window 0x%lx: XkbMapNotify!", xevent->xany.window); + SDL_FALLTHROUGH; +#endif + case XkbNewKeyboardNotify: + { +#ifdef DEBUG_XEVENTS + if (xkbevent->any.xkb_type == XkbNewKeyboardNotify) { + SDL_Log("window 0x%lx: XkbNewKeyboardNotify!", xevent->xany.window); + } +#endif + X11_XkbRefreshKeyboardMapping(&xkbevent->map); + + // Don't redundantly rebuild the keymap if this is a duplicate event. + if (xkbevent->any.serial != videodata->keyboard.xkb.last_map_serial) { + videodata->keyboard.xkb.last_map_serial = xkbevent->any.serial; + X11_UpdateKeymap(_this, true); + } + } break; + + default: + break; + } + } else +#endif if (xevent->type == KeymapNotify) { #ifdef DEBUG_XEVENTS SDL_Log("window 0x%lx: KeymapNotify!", xevent->xany.window); #endif - if (SDL_GetKeyboardFocus() != NULL) { -#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM - if (videodata->xkb.desc_ptr) { - XkbStateRec state; - if (X11_XkbGetState(videodata->display, XkbUseCoreKbd, &state) == Success) { - if (state.group != videodata->xkb.current_group) { - // Only rebuild the keymap if the layout has changed. - videodata->xkb.current_group = state.group; - X11_UpdateKeymap(_this, true); - } - } + if (!videodata->keyboard.xkb_enabled) { + if (SDL_GetKeyboardFocus() != NULL) { + X11_UpdateKeymap(_this, true); } -#endif - X11_ReconcileKeyboardState(_this); } + + X11_ReconcileKeyboardState(_this); } else if (xevent->type == MappingNotify) { - // Has the keyboard layout changed? - const int request = xevent->xmapping.request; + if (!videodata->keyboard.xkb_enabled) { + // Has the keyboard layout changed? + const int request = xevent->xmapping.request; #ifdef DEBUG_XEVENTS - SDL_Log("window 0x%lx: MappingNotify!", xevent->xany.window); + SDL_Log("window 0x%lx: MappingNotify!", xevent->xany.window); #endif - if ((request == MappingKeyboard) || (request == MappingModifier)) { - X11_XRefreshKeyboardMapping(&xevent->xmapping); - } + if ((request == MappingKeyboard) || (request == MappingModifier)) { + X11_XRefreshKeyboardMapping(&xevent->xmapping); + } - X11_UpdateKeymap(_this, true); + X11_UpdateKeymap(_this, true); + } } else if (xevent->type == PropertyNotify && videodata && videodata->windowlist) { char *name_of_atom = X11_XGetAtomName(display, xevent->xproperty.atom); diff --git a/src/video/x11/SDL_x11keyboard.c b/src/video/x11/SDL_x11keyboard.c index 8406f48671..247d8e0a84 100644 --- a/src/video/x11/SDL_x11keyboard.c +++ b/src/video/x11/SDL_x11keyboard.c @@ -28,7 +28,10 @@ #include "../../events/SDL_scancode_tables_c.h" #include + +#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLIB #include +#endif #include "../../events/imKStoUCS.h" #include "../../events/SDL_keysym_to_scancode_c.h" @@ -70,6 +73,28 @@ static bool X11_ScancodeIsRemappable(SDL_Scancode scancode) } } +static KeySym X11_KeyCodeToSym(SDL_VideoDevice *_this, KeyCode keycode, unsigned int group, unsigned int level) +{ + SDL_VideoData *data = _this->internal; + KeySym keysym; + +#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLIB + if (data->keyboard.xkb_enabled) { + keysym = X11_XkbKeycodeToKeysym(data->display, keycode, group, level); + } else +#endif + { + // TODO: Handle groups on the legacy path. + if (keycode >= data->keyboard.core.min_keycode && keycode <= data->keyboard.core.max_keycode) { + keysym = data->keyboard.core.keysym_map[(keycode - data->keyboard.core.min_keycode) * data->keyboard.core.keysyms_per_key]; + } else { + keysym = NoSymbol; + } + } + + return keysym; +} + // This function only correctly maps letters and numbers for keyboards in US QWERTY layout static SDL_Scancode X11_KeyCodeToSDLScancode(SDL_VideoDevice *_this, KeyCode keycode) { @@ -82,48 +107,6 @@ static SDL_Scancode X11_KeyCodeToSDLScancode(SDL_VideoDevice *_this, KeyCode key return SDL_GetScancodeFromKeySym(keysym, keycode); } -KeySym X11_KeyCodeToSym(SDL_VideoDevice *_this, KeyCode keycode, unsigned char group, unsigned int mod_mask) -{ - SDL_VideoData *data = _this->internal; - KeySym keysym; - unsigned int mods_ret[16]; - - SDL_zero(mods_ret); - -#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM - if (data->xkb.desc_ptr) { - int num_groups = XkbKeyNumGroups(data->xkb.desc_ptr, keycode); - unsigned char info = XkbKeyGroupInfo(data->xkb.desc_ptr, keycode); - - if (num_groups && group >= num_groups) { - - int action = XkbOutOfRangeGroupAction(info); - - if (action == XkbRedirectIntoRange) { - group = XkbOutOfRangeGroupNumber(info); - if (group >= num_groups) { - group = 0; - } - } else if (action == XkbClampIntoRange) { - group = num_groups - 1; - } else { - group %= num_groups; - } - } - - if (X11_XkbLookupKeySym(data->display, keycode, XkbBuildCoreState(mod_mask, group), mods_ret, &keysym) == NoSymbol) { - keysym = NoSymbol; - } - } else -#endif - { - // TODO: Handle groups and modifiers on the legacy path. - keysym = X11_XKeycodeToKeysym(data->display, keycode, 0); - } - - return keysym; -} - bool X11_InitKeyboard(SDL_VideoDevice *_this) { SDL_VideoData *data = _this->internal; @@ -146,21 +129,33 @@ bool X11_InitKeyboard(SDL_VideoDevice *_this) int best_distance; int best_index; int distance; - Bool xkb_repeat = 0; -#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM - { - int xkb_major = XkbMajorVersion; - int xkb_minor = XkbMinorVersion; +#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLIB + int xkb_major = XkbMajorVersion; + int xkb_minor = XkbMinorVersion; - if (X11_XkbQueryExtension(data->display, NULL, &data->xkb.event, NULL, &xkb_major, &xkb_minor)) { - data->xkb.desc_ptr = X11_XkbGetMap(data->display, XkbAllClientInfoMask, XkbUseCoreKbd); - } + if (X11_XkbQueryExtension(data->display, NULL, &data->keyboard.xkb.event, NULL, &xkb_major, &xkb_minor)) { + Bool xkb_repeat = 0; + data->keyboard.xkb_enabled = true; + data->keyboard.xkb.desc_ptr = X11_XkbGetMap(data->display, XkbAllClientInfoMask, XkbUseCoreKbd); - // This will remove KeyRelease events for held keys + // This will remove KeyRelease events for held keys. X11_XkbSetDetectableAutoRepeat(data->display, True, &xkb_repeat); - } + + // Enable the key mapping and state events. + X11_XkbSelectEvents(data->display, XkbUseCoreKbd, + XkbNewKeyboardNotifyMask | XkbMapNotifyMask | XkbStateNotifyMask, + XkbNewKeyboardNotifyMask | XkbMapNotifyMask | XkbStateNotifyMask); + X11_XkbSelectEventDetails(data->display, XkbUseCoreKbd, XkbStateNotify, XkbGroupStateMask | XkbModifierStateMask, XkbGroupStateMask | XkbModifierStateMask); + } else #endif + { + // If XKB isn't available, initialize the legacy path. + X11_XDisplayKeycodes(data->display, &data->keyboard.core.min_keycode, &data->keyboard.core.max_keycode); + data->keyboard.core.keysym_map = X11_XGetKeyboardMapping(data->display, data->keyboard.core.min_keycode, + data->keyboard.core.max_keycode - data->keyboard.core.min_keycode, + &data->keyboard.core.keysyms_per_key); + } // Open a connection to the X input manager #ifdef X_HAVE_UTF8_STRING @@ -242,10 +237,10 @@ bool X11_InitKeyboard(SDL_VideoDevice *_this) SDL_Log("Using scancode set %d, min_keycode = %d, max_keycode = %d, table_size = %d", best_index, min_keycode, max_keycode, table_size); #endif // This should never happen, but just in case... - if (table_size > (SDL_arraysize(data->key_layout) - min_keycode)) { - table_size = (SDL_arraysize(data->key_layout) - min_keycode); + if (table_size > (SDL_arraysize(data->keyboard.key_layout) - min_keycode)) { + table_size = (SDL_arraysize(data->keyboard.key_layout) - min_keycode); } - SDL_memcpy(&data->key_layout[min_keycode], table, sizeof(SDL_Scancode) * table_size); + SDL_memcpy(&data->keyboard.key_layout[min_keycode], table, sizeof(SDL_Scancode) * table_size); /* Scancodes represent physical locations on the keyboard, unaffected by keyboard mapping. However, there are a number of extended scancodes that have no standard location, so use @@ -261,7 +256,7 @@ bool X11_InitKeyboard(SDL_VideoDevice *_this) (unsigned int)sym, sym == NoSymbol ? "NoSymbol" : X11_XKeysymToString(sym)); } #endif - if (scancode == data->key_layout[i]) { + if (scancode == data->keyboard.key_layout[i]) { continue; } if ((SDL_GetKeymapKeycode(NULL, scancode, SDL_KMOD_NONE) & (SDLK_SCANCODE_MASK | SDLK_EXTENDED_MASK)) && X11_ScancodeIsRemappable(scancode)) { @@ -269,7 +264,7 @@ bool X11_InitKeyboard(SDL_VideoDevice *_this) #ifdef DEBUG_KEYBOARD SDL_Log("Changing scancode, was %d (%s), now %d (%s)", data->key_layout[i], SDL_GetScancodeName(data->key_layout[i]), scancode, SDL_GetScancodeName(scancode)); #endif - data->key_layout[i] = scancode; + data->keyboard.key_layout[i] = scancode; } } } else { @@ -293,7 +288,7 @@ bool X11_InitKeyboard(SDL_VideoDevice *_this) SDL_Log("scancode = %d (%s)", scancode, SDL_GetScancodeName(scancode)); } #endif - data->key_layout[i] = scancode; + data->keyboard.key_layout[i] = scancode; } } @@ -306,149 +301,235 @@ bool X11_InitKeyboard(SDL_VideoDevice *_this) return true; } -static unsigned X11_GetNumLockModifierMask(SDL_VideoDevice *_this) +#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLIB +static unsigned int X11_GetXkbVirtualModifierMask(SDL_VideoDevice *_this, const char *vmod_name) +{ + SDL_VideoData *videodata = _this->internal; + unsigned int mod_mask = 0; + + if (videodata->keyboard.xkb_enabled) { + Atom vmod = X11_XInternAtom(videodata->display, vmod_name, True); + if (vmod != None) { + for (int i = 0; i < XkbNumVirtualMods; ++i) { + if (vmod == videodata->keyboard.xkb.desc_ptr->names->vmods[i]) { + mod_mask = videodata->keyboard.xkb.desc_ptr->server->vmods[i]; + break; + } + } + } + } + + return mod_mask; +} +#endif + +static unsigned X11_GetXModifierMask(SDL_VideoDevice *_this, SDL_Scancode scancode) { SDL_VideoData *videodata = _this->internal; Display *display = videodata->display; - unsigned num_mask = 0; - int i, j; - XModifierKeymap *xmods; - unsigned n; + unsigned int mod_mask = 0; - xmods = X11_XGetModifierMapping(display); - n = xmods->max_keypermod; - for (i = 3; i < 8; i++) { - for (j = 0; j < n; j++) { - KeyCode kc = xmods->modifiermap[i * n + j]; - if (videodata->key_layout[kc] == SDL_SCANCODE_NUMLOCKCLEAR) { - num_mask = 1 << i; + XModifierKeymap *xmods = X11_XGetModifierMapping(display); + unsigned int n = xmods->max_keypermod; + for (int i = 3; i < 8; i++) { + for (int j = 0; j < n; j++) { + const KeyCode kc = xmods->modifiermap[i * n + j]; + if (videodata->keyboard.key_layout[kc] == scancode) { + mod_mask = 1 << i; break; } } } X11_XFreeModifiermap(xmods); - return num_mask; + return mod_mask; } -static unsigned X11_GetScrollLockModifierMask(SDL_VideoDevice *_this) +static void X11_AddKeymapEntry(SDL_Keymap *keymap, Uint32 xkeycode, KeySym xkeysym, SDL_Scancode sdl_scancode, SDL_Keymod sdl_mod_mask) { - SDL_VideoData *videodata = _this->internal; - Display *display = videodata->display; - unsigned num_mask = 0; - int i, j; - XModifierKeymap *xmods; - unsigned n; + SDL_Keycode keycode = SDL_GetKeyCodeFromKeySym(xkeysym, xkeycode, sdl_mod_mask); - xmods = X11_XGetModifierMapping(display); - n = xmods->max_keypermod; - for (i = 3; i < 8; i++) { - for (j = 0; j < n; j++) { - KeyCode kc = xmods->modifiermap[i * n + j]; - if (videodata->key_layout[kc] == SDL_SCANCODE_SCROLLLOCK) { - num_mask = 1 << i; - break; - } + if (!keycode) { + switch (sdl_scancode) { + case SDL_SCANCODE_RETURN: + keycode = SDLK_RETURN; + break; + case SDL_SCANCODE_ESCAPE: + keycode = SDLK_ESCAPE; + break; + case SDL_SCANCODE_BACKSPACE: + keycode = SDLK_BACKSPACE; + break; + case SDL_SCANCODE_DELETE: + keycode = SDLK_DELETE; + break; + default: + keycode = SDL_SCANCODE_TO_KEYCODE(sdl_scancode); + break; } } - X11_XFreeModifiermap(xmods); - return num_mask; + SDL_SetKeymapEntry(keymap, sdl_scancode, sdl_mod_mask, keycode); } void X11_UpdateKeymap(SDL_VideoDevice *_this, bool send_event) { - struct Keymod_masks - { - SDL_Keymod sdl_mask; - unsigned int xkb_mask; - } const keymod_masks[] = { - { SDL_KMOD_NONE, 0 }, - { SDL_KMOD_SHIFT, ShiftMask }, - { SDL_KMOD_CAPS, LockMask }, - { SDL_KMOD_SHIFT | SDL_KMOD_CAPS, ShiftMask | LockMask }, - { SDL_KMOD_MODE, Mod5Mask }, - { SDL_KMOD_MODE | SDL_KMOD_SHIFT, Mod5Mask | ShiftMask }, - { SDL_KMOD_MODE | SDL_KMOD_CAPS, Mod5Mask | LockMask }, - { SDL_KMOD_MODE | SDL_KMOD_SHIFT | SDL_KMOD_CAPS, Mod5Mask | ShiftMask | LockMask }, - { SDL_KMOD_LEVEL5, Mod3Mask }, - { SDL_KMOD_LEVEL5 | SDL_KMOD_SHIFT, Mod3Mask | ShiftMask }, - { SDL_KMOD_LEVEL5 | SDL_KMOD_CAPS, Mod3Mask | LockMask }, - { SDL_KMOD_LEVEL5 | SDL_KMOD_SHIFT | SDL_KMOD_CAPS, Mod3Mask | ShiftMask | LockMask }, - { SDL_KMOD_LEVEL5 | SDL_KMOD_MODE, Mod5Mask | Mod3Mask }, - { SDL_KMOD_LEVEL5 | SDL_KMOD_MODE | SDL_KMOD_SHIFT, Mod3Mask | Mod5Mask | ShiftMask }, - { SDL_KMOD_LEVEL5 | SDL_KMOD_MODE | SDL_KMOD_CAPS, Mod3Mask | Mod5Mask | LockMask }, - { SDL_KMOD_LEVEL5 | SDL_KMOD_MODE | SDL_KMOD_SHIFT | SDL_KMOD_CAPS, Mod3Mask | Mod5Mask | ShiftMask | LockMask } - }; - SDL_VideoData *data = _this->internal; - SDL_Scancode scancode; - SDL_Keymap *keymap = SDL_CreateKeymap(true); -#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM - if (data->xkb.desc_ptr) { +#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLIB + if (data->keyboard.xkb_enabled) { XkbStateRec state; - X11_XkbGetUpdatedMap(data->display, XkbAllClientInfoMask, data->xkb.desc_ptr); + + SDL_SetKeymap(NULL, false); + for (unsigned int i = 0; i < XkbNumKbdGroups; ++i) { + SDL_DestroyKeymap(data->keyboard.xkb.keymaps[i]); + data->keyboard.xkb.keymaps[i] = SDL_CreateKeymap(false); + } + + X11_XkbGetNames(data->display, XkbVirtualModNamesMask, data->keyboard.xkb.desc_ptr); + X11_XkbGetUpdatedMap(data->display, XkbAllClientInfoMask | XkbVirtualModsMask, data->keyboard.xkb.desc_ptr); if (X11_XkbGetState(data->display, XkbUseCoreKbd, &state) == Success) { - data->xkb.current_group = state.group; + data->keyboard.xkb.current_group = state.group; } - } -#endif - for (int m = 0; m < SDL_arraysize(keymod_masks); ++m) { - for (int i = 0; i < SDL_arraysize(data->key_layout); ++i) { - // Make sure this is a valid scancode - scancode = data->key_layout[i]; + data->keyboard.alt_mask = X11_GetXkbVirtualModifierMask(_this, "Alt"); + if (!data->keyboard.alt_mask) { + data->keyboard.alt_mask = X11_GetXkbVirtualModifierMask(_this, "Meta"); + } + data->keyboard.gui_mask = X11_GetXkbVirtualModifierMask(_this, "Super"); + data->keyboard.level3_mask = X11_GetXkbVirtualModifierMask(_this, "LevelThree"); + data->keyboard.level5_mask = X11_GetXkbVirtualModifierMask(_this, "LevelFive"); + data->keyboard.numlock_mask = X11_GetXkbVirtualModifierMask(_this, "NumLock"); + data->keyboard.scrolllock_mask = X11_GetXkbVirtualModifierMask(_this, "ScrollLock"); + + const Uint32 valid_mod_mask = ShiftMask | LockMask | data->keyboard.alt_mask | data->keyboard.level3_mask | data->keyboard.level5_mask; + + for (Uint32 xkeycode = data->keyboard.xkb.desc_ptr->min_key_code; xkeycode < data->keyboard.xkb.desc_ptr->max_key_code; ++xkeycode) { + const SDL_Scancode scancode = data->keyboard.key_layout[xkeycode]; if (scancode == SDL_SCANCODE_UNKNOWN) { continue; } - const KeySym keysym = X11_KeyCodeToSym(_this, i, data->xkb.current_group, keymod_masks[m].xkb_mask); + for (Uint32 group = 0; group < XkbNumKbdGroups; ++group) { + SDL_Keymap *keymap = data->keyboard.xkb.keymaps[group]; - if (keysym != NoSymbol) { - SDL_Keycode keycode = SDL_GetKeyCodeFromKeySym(keysym, i, keymod_masks[m].sdl_mask); + Uint32 effective_group = group; + const unsigned char max_key_group = XkbKeyNumGroups(data->keyboard.xkb.desc_ptr, xkeycode); + const unsigned char key_group_info = XkbKeyGroupInfo(data->keyboard.xkb.desc_ptr, xkeycode); - if (!keycode) { - switch (scancode) { - case SDL_SCANCODE_RETURN: - keycode = SDLK_RETURN; - break; - case SDL_SCANCODE_ESCAPE: - keycode = SDLK_ESCAPE; - break; - case SDL_SCANCODE_BACKSPACE: - keycode = SDLK_BACKSPACE; - break; - case SDL_SCANCODE_DELETE: - keycode = SDLK_DELETE; - break; + if (max_key_group && effective_group >= max_key_group) { + const unsigned char action = XkbOutOfRangeGroupAction(key_group_info); + + switch (action) { default: - keycode = SDL_SCANCODE_TO_KEYCODE(scancode); + effective_group %= max_key_group; + break; + case XkbClampIntoRange: + effective_group = max_key_group - 1; + break; + case XkbRedirectIntoRange: + effective_group = XkbOutOfRangeGroupNumber(key_group_info); + if (effective_group >= max_key_group) { + effective_group = 0; + } break; } } - SDL_SetKeymapEntry(keymap, scancode, keymod_masks[m].sdl_mask, keycode); + XkbKeyTypePtr key_type = XkbKeyKeyType(data->keyboard.xkb.desc_ptr, xkeycode, effective_group); + + for (Uint32 level = 0; level < key_type->num_levels; ++level) { + const KeySym keysym = X11_KeyCodeToSym(_this, xkeycode, effective_group, level); + + if (keysym != NoSymbol) { + bool key_added = false; + + for (int map_idx = 0; map_idx < key_type->map_count; ++map_idx) { + if (key_type->map[map_idx].active && key_type->map[map_idx].level == level) { + const unsigned int xkb_mod_mask = key_type->map[map_idx].mods.mask; + if ((xkb_mod_mask | valid_mod_mask) == valid_mod_mask) { + const SDL_Keymod sdl_mod_mask = (xkb_mod_mask & ShiftMask ? SDL_KMOD_SHIFT : 0) | + (xkb_mod_mask & LockMask ? SDL_KMOD_CAPS : 0) | + (xkb_mod_mask & data->keyboard.alt_mask ? SDL_KMOD_ALT : 0) | + (xkb_mod_mask & data->keyboard.level3_mask ? SDL_KMOD_MODE : 0) | + (xkb_mod_mask & data->keyboard.level5_mask ? SDL_KMOD_LEVEL5 : 0); + + X11_AddKeymapEntry(keymap, xkeycode, keysym, scancode, sdl_mod_mask); + key_added = true; + } + } + } + + // Add the unmodified key for level 0. + if (!level && !key_added) { + X11_AddKeymapEntry(keymap, xkeycode, keysym, scancode, 0); + } + } + } } } - } - data->xkb.numlock_mask = X11_GetNumLockModifierMask(_this); - data->xkb.scrolllock_mask = X11_GetScrollLockModifierMask(_this); - SDL_SetKeymap(keymap, send_event); + SDL_SetKeymap(data->keyboard.xkb.keymaps[data->keyboard.xkb.current_group], send_event); + } else +#endif + { + SDL_Keymap *keymap = SDL_CreateKeymap(true); + + if (send_event) { + if (data->keyboard.core.keysym_map) { + X11_XFree(data->keyboard.core.keysym_map); + } + X11_XDisplayKeycodes(data->display, &data->keyboard.core.min_keycode, &data->keyboard.core.max_keycode); + data->keyboard.core.keysym_map = X11_XGetKeyboardMapping(data->display, data->keyboard.core.min_keycode, + data->keyboard.core.max_keycode - data->keyboard.core.min_keycode, + &data->keyboard.core.keysyms_per_key); + } + + for (Uint32 xkeycode = data->keyboard.core.min_keycode; xkeycode <= data->keyboard.core.max_keycode; ++xkeycode) { + const SDL_Scancode scancode = data->keyboard.key_layout[xkeycode]; + if (scancode == SDL_SCANCODE_UNKNOWN) { + continue; + } + + const KeySym keysym = X11_KeyCodeToSym(_this, xkeycode, 0, 0); + if (keysym != NoSymbol) { + X11_AddKeymapEntry(keymap, xkeycode, keysym, scancode, 0); + } + } + + data->keyboard.alt_mask = Mod1Mask; // Alt or Meta + data->keyboard.gui_mask = Mod4Mask; // Super + data->keyboard.level3_mask = Mod5Mask; // Note: Not a typo, Mod5 = level 3 shift, and Mod3 = level 5 shift. + data->keyboard.level5_mask = Mod3Mask; + data->keyboard.numlock_mask = X11_GetXModifierMask(_this, SDL_SCANCODE_NUMLOCKCLEAR); + data->keyboard.scrolllock_mask = X11_GetXModifierMask(_this, SDL_SCANCODE_SCROLLLOCK); + + SDL_SetKeymap(keymap, send_event); + } } void X11_QuitKeyboard(SDL_VideoDevice *_this) { SDL_VideoData *data = _this->internal; -#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM - if (data->xkb.desc_ptr) { - X11_XkbFreeKeyboard(data->xkb.desc_ptr, 0, True); - data->xkb.desc_ptr = NULL; - } +#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLIB + if (data->keyboard.xkb_enabled) { + for (int i = 0; i < XkbNumKbdGroups; ++i) { + SDL_DestroyKeymap(data->keyboard.xkb.keymaps[i]); + data->keyboard.xkb.keymaps[i] = NULL; + } + + if (data->keyboard.xkb_enabled) { + X11_XkbFreeKeyboard(data->keyboard.xkb.desc_ptr, 0, True); + data->keyboard.xkb.desc_ptr = NULL; + } + } else #endif + if (data->keyboard.core.keysym_map) { + X11_XFree(data->keyboard.core.keysym_map); + data->keyboard.core.keysym_map = NULL; + } } void X11_ClearComposition(SDL_WindowData *data) diff --git a/src/video/x11/SDL_x11keyboard.h b/src/video/x11/SDL_x11keyboard.h index a6cd2f7e97..bc053f5fbb 100644 --- a/src/video/x11/SDL_x11keyboard.h +++ b/src/video/x11/SDL_x11keyboard.h @@ -35,6 +35,5 @@ extern bool X11_HasScreenKeyboardSupport(SDL_VideoDevice *_this); extern void X11_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props); extern void X11_HideScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window); extern bool X11_IsScreenKeyboardShown(SDL_VideoDevice *_this, SDL_Window *window); -extern KeySym X11_KeyCodeToSym(SDL_VideoDevice *_this, KeyCode, unsigned char group, unsigned int mod_mask); #endif // SDL_x11keyboard_h_ diff --git a/src/video/x11/SDL_x11sym.h b/src/video/x11/SDL_x11sym.h index df9d59b36e..959aabce8c 100644 --- a/src/video/x11/SDL_x11sym.h +++ b/src/video/x11/SDL_x11sym.h @@ -71,6 +71,7 @@ SDL_X11_SYM(int,XFreePixmap,(Display* a,Pixmap b)) SDL_X11_SYM(void,XFreeStringList,(char** a)) SDL_X11_SYM(char*,XGetAtomName,(Display *a,Atom b)) SDL_X11_SYM(int,XGetInputFocus,(Display *a,Window *b,int *c)) +SDL_X11_SYM(KeySym*,XGetKeyboardMapping,(Display *a, KeyCode b, int c, int *d)) SDL_X11_SYM(int,XGetErrorDatabaseText,(Display* a,_Xconst char* b,_Xconst char* c,_Xconst char* d,char* e,int f)) SDL_X11_SYM(XModifierKeymap*,XGetModifierMapping,(Display* a)) SDL_X11_SYM(int,XGetPointerControl,(Display* a,int* b,int* c,int* d)) @@ -196,35 +197,21 @@ SDL_X11_SYM(Bool,XGetEventData,(Display* a,XGenericEventCookie* b)) SDL_X11_SYM(void,XFreeEventData,(Display* a,XGenericEventCookie* b)) #endif -#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM +#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLIB SDL_X11_SYM(Bool,XkbQueryExtension,(Display* a,int * b,int * c,int * d,int * e, int *f)) -#if NeedWidePrototypes -SDL_X11_SYM(Bool,XkbLookupKeySym,(Display* a, unsigned int b, unsigned int c, unsigned int* d, KeySym* e)) -#else -SDL_X11_SYM(Bool,XkbLookupKeySym,(Display* a, KeyCode b, unsigned int c, unsigned int* d, KeySym* e)) -#endif +SDL_X11_SYM(KeySym,XkbKeycodeToKeysym,(Display* a, KeyCode b, unsigned int c, unsigned int d)) +SDL_X11_SYM(Bool,XkbSelectEvents,(Display* a, unsigned int b, unsigned int c, unsigned long d)) +SDL_X11_SYM(Bool,XkbSelectEventDetails,(Display* a, unsigned int b, unsigned int c, unsigned long d, unsigned long e)) +SDL_X11_SYM(Status,XkbGetNames,(Display *a, unsigned int b, XkbDescPtr c)) SDL_X11_SYM(Status,XkbGetState,(Display* a,unsigned int b,XkbStatePtr c)) SDL_X11_SYM(Status,XkbGetUpdatedMap,(Display* a,unsigned int b,XkbDescPtr c)) SDL_X11_SYM(XkbDescPtr,XkbGetMap,(Display* a,unsigned int b,unsigned int c)) SDL_X11_SYM(void,XkbFreeClientMap,(XkbDescPtr a,unsigned int b, Bool c)) SDL_X11_SYM(void,XkbFreeKeyboard,(XkbDescPtr a,unsigned int b, Bool c)) +SDL_X11_SYM(Status,XkbRefreshKeyboardMapping,(XkbMapNotifyEvent *a)) SDL_X11_SYM(Bool,XkbSetDetectableAutoRepeat,(Display* a, Bool b, Bool* c)) #endif -// XKeycodeToKeysym is a deprecated function -#ifdef HAVE_GCC_DIAGNOSTIC_PRAGMA -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" -#endif -#if NeedWidePrototypes -SDL_X11_SYM(KeySym,XKeycodeToKeysym,(Display* a,unsigned int b,int c)) -#else -SDL_X11_SYM(KeySym,XKeycodeToKeysym,(Display* a,KeyCode b,int c)) -#endif -#ifdef HAVE_GCC_DIAGNOSTIC_PRAGMA -#pragma GCC diagnostic pop -#endif - #ifdef X_HAVE_UTF8_STRING SDL_X11_MODULE(UTF8) SDL_X11_SYM(int,Xutf8TextListToTextProperty,(Display* a,char** b,int c,XICCEncodingStyle d,XTextProperty* e)) diff --git a/src/video/x11/SDL_x11video.h b/src/video/x11/SDL_x11video.h index 8d89bf5b76..b5bdb67409 100644 --- a/src/video/x11/SDL_x11video.h +++ b/src/video/x11/SDL_x11video.h @@ -24,6 +24,7 @@ #define SDL_x11video_h_ #include "../SDL_sysvideo.h" +#include "../../events/SDL_keymap_c.h" #include "../../core/linux/SDL_dbus.h" #include "../../core/linux/SDL_ime.h" @@ -125,7 +126,6 @@ struct SDL_VideoData Atom pen_atom_wacom_tool_type; } atoms; - SDL_Scancode key_layout[256]; bool selection_waiting; bool selection_incr_waiting; @@ -144,21 +144,43 @@ struct SDL_VideoData int xrandr_event_base; struct { -#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM - XkbDescPtr desc_ptr; + bool xkb_enabled; + SDL_Scancode key_layout[256]; + + union + { +#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLIB + struct + { + XkbDescPtr desc_ptr; + SDL_Keymap *keymaps[XkbNumKbdGroups]; + unsigned long last_map_serial; + int event; + Uint32 current_group; + } xkb; // Modern XKB keyboard handling #endif - int event; - unsigned int current_group; - unsigned int xkb_modifiers; - - SDL_Keymod sdl_modifiers; + struct + { + KeySym *keysym_map; + int keysyms_per_key; + int min_keycode; + int max_keycode; + } core; // Legacy core keyboard handling + }; + Uint32 pressed_modifiers; + Uint32 locked_modifiers; + SDL_Keymod sdl_pressed_modifiers; + SDL_Keymod sdl_physically_pressed_modifiers; + SDL_Keymod sdl_locked_modifiers; + // Virtual modifiers looked up by name. + Uint32 alt_mask; + Uint32 gui_mask; + Uint32 level3_mask; + Uint32 level5_mask; Uint32 numlock_mask; Uint32 scrolllock_mask; - } xkb; - - KeyCode filter_code; - Time filter_time; + } keyboard; #ifdef SDL_VIDEO_VULKAN // Vulkan variables only valid if _this->vulkan_config.loader_handle is not NULL From 561c99ee1171f680088ff98c773de2efe94b0f5e Mon Sep 17 00:00:00 2001 From: Pino Toscano Date: Wed, 6 Aug 2025 23:40:02 +0200 Subject: [PATCH 19/61] SDL_endian.h: extend Linux way for GNU libc The currently used way to determine the endianness (i.e. include and use the __BYTE_ORDER macro) is provided in general by GNU libc. Thus, extend that to any platform/OS based on GNU libc. --- include/SDL3/SDL_endian.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/SDL3/SDL_endian.h b/include/SDL3/SDL_endian.h index 2a9b8a34d8..41ad0ce51a 100644 --- a/include/SDL3/SDL_endian.h +++ b/include/SDL3/SDL_endian.h @@ -128,7 +128,7 @@ _m_prefetch(void *__P) * \sa SDL_BIG_ENDIAN */ #define SDL_BYTEORDER SDL_LIL_ENDIAN___or_maybe___SDL_BIG_ENDIAN -#elif defined(SDL_PLATFORM_LINUX) +#elif defined(SDL_PLATFORM_LINUX) || defined(__GLIBC__) #include #define SDL_BYTEORDER __BYTE_ORDER #elif defined(SDL_PLATFORM_SOLARIS) From 33c899859884cfc1584b348c5e6f2533b0f3ac88 Mon Sep 17 00:00:00 2001 From: Frank Praznik Date: Wed, 6 Aug 2025 17:36:31 -0400 Subject: [PATCH 20/61] video: Use additional checks for determining on which display a window should be fullscreen Trying to determine where a window should be made fullscreen from the size and position can be unreliable if the window exceeds the display bounds. Add additional checks with the following priority: - If the window was positioned with a macro that explicitly passes a display ID, store and use the requested display as the explicit fullscreen target. - Check if the window position is an exact match for any display origins, and use that display if found, as positioning a fullscreen window by moving it to the origin of the destination display is common behavior. - Fall back to the existing center point check if the previous checks were not successful, as it is known behavior, and won't risk breaking existing clients that rely on it. --- src/events/SDL_windowevents.c | 6 ++++++ src/video/SDL_sysvideo.h | 1 + src/video/SDL_video.c | 24 +++++++++++++++++++++++- 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/events/SDL_windowevents.c b/src/events/SDL_windowevents.c index 62fca28b9f..2e194781af 100644 --- a/src/events/SDL_windowevents.c +++ b/src/events/SDL_windowevents.c @@ -99,6 +99,12 @@ bool SDL_SendWindowEvent(SDL_Window *window, SDL_EventType windowevent, int data case SDL_EVENT_WINDOW_MOVED: window->undefined_x = false; window->undefined_y = false; + /* Clear the pending display if this move was not the result of an explicit request, + * and the window is not scheduled to become fullscreen when shown. + */ + if (!window->last_position_pending && !(window->pending_flags & SDL_WINDOW_FULLSCREEN)) { + window->pending_displayID = 0; + } window->last_position_pending = false; if (!(window->flags & SDL_WINDOW_FULLSCREEN)) { window->windowed.x = data1; diff --git a/src/video/SDL_sysvideo.h b/src/video/SDL_sysvideo.h index c8cd3991cf..f1f7985bb2 100644 --- a/src/video/SDL_sysvideo.h +++ b/src/video/SDL_sysvideo.h @@ -61,6 +61,7 @@ struct SDL_Window bool fullscreen_exclusive; // The window is currently fullscreen exclusive SDL_DisplayID last_fullscreen_exclusive_display; // The last fullscreen_exclusive display SDL_DisplayID last_displayID; + SDL_DisplayID pending_displayID; /* Stored position and size for the window in the non-fullscreen state, * including when the window is maximized or tiled. diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c index bfdb6e6d83..ca01c117a8 100644 --- a/src/video/SDL_video.c +++ b/src/video/SDL_video.c @@ -1726,6 +1726,9 @@ SDL_VideoDisplay *SDL_GetVideoDisplayForFullscreenWindow(SDL_Window *window) * window position, or an async window manager hasn't yet actually moved the window, * the current position won't be updated at the time of the fullscreen call. */ + if (!displayID) { + displayID = window->pending_displayID; + } if (!displayID) { // Use the pending position and dimensions, if available, otherwise, use the current. const int x = window->last_position_pending ? window->pending.x : window->x; @@ -2364,6 +2367,7 @@ SDL_Window *SDL_CreateWindowWithProperties(SDL_PropertiesID props) SDL_Window *parent = (SDL_Window *)SDL_GetPointerProperty(props, SDL_PROP_WINDOW_CREATE_PARENT_POINTER, NULL); SDL_WindowFlags flags = SDL_GetWindowFlagProperties(props); SDL_WindowFlags type_flags, graphics_flags; + SDL_DisplayID displayID = 0; bool undefined_x = false; bool undefined_y = false; bool external_graphics_context = SDL_GetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_EXTERNAL_GRAPHICS_CONTEXT_BOOLEAN, false); @@ -2423,7 +2427,6 @@ SDL_Window *SDL_CreateWindowWithProperties(SDL_PropertiesID props) if (SDL_WINDOWPOS_ISUNDEFINED(x) || SDL_WINDOWPOS_ISUNDEFINED(y) || SDL_WINDOWPOS_ISCENTERED(x) || SDL_WINDOWPOS_ISCENTERED(y)) { - SDL_DisplayID displayID = 0; SDL_Rect bounds; if ((SDL_WINDOWPOS_ISUNDEFINED(x) || SDL_WINDOWPOS_ISCENTERED(x)) && (x & 0xFFFF)) { @@ -2506,6 +2509,7 @@ SDL_Window *SDL_CreateWindowWithProperties(SDL_PropertiesID props) window->floating.h = window->windowed.h = window->h = h; window->undefined_x = undefined_x; window->undefined_y = undefined_y; + window->pending_displayID = displayID; SDL_VideoDisplay *display = SDL_GetVideoDisplayForWindow(window); if (display) { @@ -2885,6 +2889,7 @@ bool SDL_SetWindowPosition(SDL_Window *window, int x, int y) const int h = window->last_size_pending ? window->pending.h : window->windowed.h; original_displayID = SDL_GetDisplayForWindow(window); + window->pending_displayID = 0; if (SDL_WINDOWPOS_ISUNDEFINED(x)) { x = window->windowed.x; @@ -2905,6 +2910,8 @@ bool SDL_SetWindowPosition(SDL_Window *window, int x, int y) displayID = SDL_GetPrimaryDisplay(); } + window->pending_displayID = displayID; + SDL_zero(bounds); if (!SDL_GetDisplayUsableBounds(displayID, &bounds) || w > bounds.w || h > bounds.h) { if (!SDL_GetDisplayBounds(displayID, &bounds)) { @@ -2917,6 +2924,21 @@ bool SDL_SetWindowPosition(SDL_Window *window, int x, int y) if (SDL_WINDOWPOS_ISCENTERED(y)) { y = bounds.y + (bounds.h - h) / 2; } + } else { + /* See if the requested window position matches the origin of any displays and set + * the pending fullscreen display ID if it does. This needs to be set early in case + * the window is prevented from moving to the exact origin due to struts. + */ + for (int i = 0; i < _this->num_displays; ++i) { + SDL_Rect rect; + const SDL_DisplayID cur_id = _this->displays[i]->id; + if (SDL_GetDisplayBounds(cur_id, &rect)) { + if (x == rect.x && y == rect.y) { + window->pending_displayID = cur_id; + break; + } + } + } } window->pending.x = x; From 6981522cd62bd4eee96d830c7e0ba7da2cace89d Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Thu, 7 Aug 2025 11:44:45 -0700 Subject: [PATCH 21/61] Revert "Support Google Play 16 KB Page Size Requirement (#13470)" This reverts commit dc2c83c3834f4eaa64698cc19cc80a3f21105e57 If you need to support the Google Play 16 kiB page size requirement, the recommendation is to use NDK r28c or newer, which automatically aligns binaries correctly. --- Android.mk | 4 ---- CMakeLists.txt | 5 ----- android-project/app/build.gradle | 2 +- android-project/app/jni/Application.mk | 3 --- android-project/app/jni/src/CMakeLists.txt | 5 ----- build-scripts/androidbuildlibs.sh | 7 ------- 6 files changed, 1 insertion(+), 25 deletions(-) diff --git a/Android.mk b/Android.mk index 0f3138e5f4..3ecc631d10 100644 --- a/Android.mk +++ b/Android.mk @@ -108,10 +108,6 @@ LOCAL_LDLIBS := -ldl -lGLESv1_CM -lGLESv2 -lOpenSLES -llog -landroid LOCAL_LDFLAGS := -Wl,--no-undefined -Wl,--no-undefined-version -Wl,--version-script=$(LOCAL_PATH)/src/dynapi/SDL_dynapi.sym -# https://developer.android.com/guide/practices/page-sizes -LOCAL_LDFLAGS += "-Wl,-z,max-page-size=16384" -LOCAL_LDFLAGS += "-Wl,-z,common-page-size=16384" - ifeq ($(NDK_DEBUG),1) cmd-strip := endif diff --git a/CMakeLists.txt b/CMakeLists.txt index 0a7992fee5..8176dc69ee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1512,11 +1512,6 @@ if(ANDROID) endif() endif() -if(TARGET SDL3-shared) - target_link_options(SDL3-shared PRIVATE "-Wl,-z,max-page-size=16384") - target_link_options(SDL3-shared PRIVATE "-Wl,-z,common-page-size=16384") -endif() - elseif(EMSCRIPTEN) # Hide noisy warnings that intend to aid mostly during initial stages of porting a new # project. Uncomment at will for verbose cross-compiling -I/../ path info. diff --git a/android-project/app/build.gradle b/android-project/app/build.gradle index f44cf26732..989ef34c71 100644 --- a/android-project/app/build.gradle +++ b/android-project/app/build.gradle @@ -19,7 +19,7 @@ android { abiFilters 'arm64-v8a' } cmake { - arguments "-DANDROID_PLATFORM=android-21", "-DANDROID_STL=c++_static", "-DAPP_SUPPORT_FLEXIBLE_PAGE_SIZES=true" + arguments "-DANDROID_PLATFORM=android-21", "-DANDROID_STL=c++_static" // abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' abiFilters 'arm64-v8a' } diff --git a/android-project/app/jni/Application.mk b/android-project/app/jni/Application.mk index 80b73fd6bc..1f7c0c10f9 100644 --- a/android-project/app/jni/Application.mk +++ b/android-project/app/jni/Application.mk @@ -8,6 +8,3 @@ APP_ABI := armeabi-v7a arm64-v8a x86 x86_64 # Min runtime API level APP_PLATFORM=android-21 - -# https://developer.android.com/guide/practices/page-sizes#update-packaging -APP_SUPPORT_FLEXIBLE_PAGE_SIZES := true \ No newline at end of file diff --git a/android-project/app/jni/src/CMakeLists.txt b/android-project/app/jni/src/CMakeLists.txt index df0a4d0f34..41a82f2af0 100644 --- a/android-project/app/jni/src/CMakeLists.txt +++ b/android-project/app/jni/src/CMakeLists.txt @@ -26,9 +26,4 @@ endif() add_library(main SHARED YourSourceHere.c ) - -#https://developer.android.com/guide/practices/page-sizes#update-packaging -target_link_options(main PRIVATE "-Wl,-z,max-page-size=16384") -target_link_options(main PRIVATE "-Wl,-z,common-page-size=16384") - target_link_libraries(main PRIVATE SDL3::SDL3) diff --git a/build-scripts/androidbuildlibs.sh b/build-scripts/androidbuildlibs.sh index 1004a98703..a903f36eea 100755 --- a/build-scripts/androidbuildlibs.sh +++ b/build-scripts/androidbuildlibs.sh @@ -30,7 +30,6 @@ abi="arm64-v8a" # "armeabi-v7a arm64-v8a x86 x86_64" obj= lib= ndk_args= -flexpage=true # Allow an external caller to specify locations and platform. while [ $# -gt 0 ]; do @@ -43,8 +42,6 @@ while [ $# -gt 0 ]; do platform=${arg#APP_PLATFORM=} elif [ "${arg:0:8}" == "APP_ABI=" ]; then abi=${arg#APP_ABI=} - elif [ "${arg:0:32}" == "APP_SUPPORT_FLEXIBLE_PAGE_SIZES=" ]; then - flexpage=${arg#APP_SUPPORT_FLEXIBLE_PAGE_SIZES=} else ndk_args="$ndk_args $arg" fi @@ -70,9 +67,6 @@ done # APP_* variables set in the environment here will not be seen by the # ndk-build makefile segments that use them, e.g., default-application.mk. # For consistency, pass all values on the command line. -# -# Add support for Google Play 16 KB Page size requirement: -# https://developer.android.com/guide/practices/page-sizes#ndk-build ndk-build \ NDK_PROJECT_PATH=null \ NDK_OUT=$obj \ @@ -81,5 +75,4 @@ ndk-build \ APP_ABI="$abi" \ APP_PLATFORM="$platform" \ APP_MODULES="SDL3" \ - APP_SUPPORT_FLEXIBLE_PAGE_SIZES="$flexpage" \ $ndk_args From fe6b2161bff74ce2da5d5b93c45d9f6fb7476d8d Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Thu, 7 Aug 2025 10:57:17 -0700 Subject: [PATCH 22/61] GPU: Fix uninitialized value in Vulkan command buffer structure --- src/gpu/vulkan/SDL_gpu_vulkan.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gpu/vulkan/SDL_gpu_vulkan.c b/src/gpu/vulkan/SDL_gpu_vulkan.c index 9b1fe3d390..f782dc2d0a 100644 --- a/src/gpu/vulkan/SDL_gpu_vulkan.c +++ b/src/gpu/vulkan/SDL_gpu_vulkan.c @@ -9587,6 +9587,7 @@ static SDL_GPUCommandBuffer *VULKAN_AcquireCommandBuffer( commandBuffer->autoReleaseFence = true; + commandBuffer->swapchainRequested = false; commandBuffer->isDefrag = 0; /* Reset the command buffer here to avoid resets being called From 40b941c82699b77f667cb4236dffb2bdf13f13fb Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Thu, 7 Aug 2025 11:54:20 +0100 Subject: [PATCH 23/61] hints: Rephrase documentation to improve grammar "This thing allows to do something" is not really grammatically correct. The closest rephrasing would be "allows one to do something" or "allows the user to do something", but I think the passive voice reads more naturally here. Detected by Debian's lintian QA tool. Signed-off-by: Simon McVittie --- include/SDL3/SDL_hints.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/SDL3/SDL_hints.h b/include/SDL3/SDL_hints.h index 09c918b13a..5e202827b3 100644 --- a/include/SDL3/SDL_hints.h +++ b/include/SDL3/SDL_hints.h @@ -595,7 +595,7 @@ extern "C" { * A variable that limits what CPU features are available. * * By default, SDL marks all features the current CPU supports as available. - * This hint allows to limit these to a subset. + * This hint allows the enabled features to be limited to a subset. * * When the hint is unset, or empty, SDL will enable all detected CPU * features. From d83503f80e485fae212e7389c115ac1b4d593a82 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Thu, 7 Aug 2025 11:58:25 +0100 Subject: [PATCH 24/61] Fix some typos detected by Debian's lintian QA tool I assume the demoninator is a typo, rather than an indication that someone has been playing too much Doom :-) Signed-off-by: Simon McVittie --- include/SDL3/SDL_camera.h | 2 +- include/SDL3/SDL_gpu.h | 4 ++-- include/SDL3/SDL_render.h | 2 +- include/SDL3/SDL_stdinc.h | 18 +++++++++--------- include/SDL3/SDL_video.h | 2 +- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/include/SDL3/SDL_camera.h b/include/SDL3/SDL_camera.h index 5f3911fdf9..c0e2ee0fed 100644 --- a/include/SDL3/SDL_camera.h +++ b/include/SDL3/SDL_camera.h @@ -119,7 +119,7 @@ typedef struct SDL_CameraSpec int width; /**< Frame width */ int height; /**< Frame height */ int framerate_numerator; /**< Frame rate numerator ((num / denom) == FPS, (denom / num) == duration in seconds) */ - int framerate_denominator; /**< Frame rate demoninator ((num / denom) == FPS, (denom / num) == duration in seconds) */ + int framerate_denominator; /**< Frame rate denominator ((num / denom) == FPS, (denom / num) == duration in seconds) */ } SDL_CameraSpec; /** diff --git a/include/SDL3/SDL_gpu.h b/include/SDL3/SDL_gpu.h index 0e82e2b040..85e63baf6d 100644 --- a/include/SDL3/SDL_gpu.h +++ b/include/SDL3/SDL_gpu.h @@ -1158,7 +1158,7 @@ typedef enum SDL_GPUCompareOp SDL_GPU_COMPAREOP_LESS_OR_EQUAL, /**< The comparison evaluates reference <= test. */ SDL_GPU_COMPAREOP_GREATER, /**< The comparison evaluates reference > test. */ SDL_GPU_COMPAREOP_NOT_EQUAL, /**< The comparison evaluates reference != test. */ - SDL_GPU_COMPAREOP_GREATER_OR_EQUAL, /**< The comparison evalutes reference >= test. */ + SDL_GPU_COMPAREOP_GREATER_OR_EQUAL, /**< The comparison evaluates reference >= test. */ SDL_GPU_COMPAREOP_ALWAYS /**< The comparison always evaluates true. */ } SDL_GPUCompareOp; @@ -2878,7 +2878,7 @@ extern SDL_DECLSPEC void SDLCALL SDL_InsertGPUDebugLabel( const char *text); /** - * Begins a debug group with an arbitary name. + * Begins a debug group with an arbitrary name. * * Used for denoting groups of calls when viewing the command buffer * callstream in a graphics debugging tool. diff --git a/include/SDL3/SDL_render.h b/include/SDL3/SDL_render.h index 8fcc1de8a7..bfb758235f 100644 --- a/include/SDL3/SDL_render.h +++ b/include/SDL3/SDL_render.h @@ -2747,7 +2747,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_RenderDebugText(SDL_Renderer *renderer, flo * Draw debug text to an SDL_Renderer. * * This function will render a printf()-style format string to a renderer. - * Note that this is a convinence function for debugging, with severe + * Note that this is a convenience function for debugging, with severe * limitations, and is not intended to be used for production apps and games. * * For the full list of limitations and other useful information, see diff --git a/include/SDL3/SDL_stdinc.h b/include/SDL3/SDL_stdinc.h index 206ff1dbf4..21266e81e4 100644 --- a/include/SDL3/SDL_stdinc.h +++ b/include/SDL3/SDL_stdinc.h @@ -3426,7 +3426,7 @@ extern SDL_DECLSPEC size_t SDLCALL SDL_utf8strnlen(const char *str, size_t bytes * Convert an integer into a string. * * This requires a radix to specified for string format. Specifying 10 - * produces a decimal number, 16 hexidecimal, etc. Must be in the range of 2 + * produces a decimal number, 16 hexadecimal, etc. Must be in the range of 2 * to 36. * * Note that this function will overflow a buffer if `str` is not large enough @@ -3454,7 +3454,7 @@ extern SDL_DECLSPEC char * SDLCALL SDL_itoa(int value, char *str, int radix); * Convert an unsigned integer into a string. * * This requires a radix to specified for string format. Specifying 10 - * produces a decimal number, 16 hexidecimal, etc. Must be in the range of 2 + * produces a decimal number, 16 hexadecimal, etc. Must be in the range of 2 * to 36. * * Note that this function will overflow a buffer if `str` is not large enough @@ -3482,7 +3482,7 @@ extern SDL_DECLSPEC char * SDLCALL SDL_uitoa(unsigned int value, char *str, int * Convert a long integer into a string. * * This requires a radix to specified for string format. Specifying 10 - * produces a decimal number, 16 hexidecimal, etc. Must be in the range of 2 + * produces a decimal number, 16 hexadecimal, etc. Must be in the range of 2 * to 36. * * Note that this function will overflow a buffer if `str` is not large enough @@ -3510,7 +3510,7 @@ extern SDL_DECLSPEC char * SDLCALL SDL_ltoa(long value, char *str, int radix); * Convert an unsigned long integer into a string. * * This requires a radix to specified for string format. Specifying 10 - * produces a decimal number, 16 hexidecimal, etc. Must be in the range of 2 + * produces a decimal number, 16 hexadecimal, etc. Must be in the range of 2 * to 36. * * Note that this function will overflow a buffer if `str` is not large enough @@ -3540,7 +3540,7 @@ extern SDL_DECLSPEC char * SDLCALL SDL_ultoa(unsigned long value, char *str, int * Convert a long long integer into a string. * * This requires a radix to specified for string format. Specifying 10 - * produces a decimal number, 16 hexidecimal, etc. Must be in the range of 2 + * produces a decimal number, 16 hexadecimal, etc. Must be in the range of 2 * to 36. * * Note that this function will overflow a buffer if `str` is not large enough @@ -3568,7 +3568,7 @@ extern SDL_DECLSPEC char * SDLCALL SDL_lltoa(long long value, char *str, int rad * Convert an unsigned long long integer into a string. * * This requires a radix to specified for string format. Specifying 10 - * produces a decimal number, 16 hexidecimal, etc. Must be in the range of 2 + * produces a decimal number, 16 hexadecimal, etc. Must be in the range of 2 * to 36. * * Note that this function will overflow a buffer if `str` is not large enough @@ -3923,7 +3923,7 @@ extern SDL_DECLSPEC int SDLCALL SDL_strcasecmp(const char *str1, const char *str extern SDL_DECLSPEC int SDLCALL SDL_strncasecmp(const char *str1, const char *str2, size_t maxlen); /** - * Searches a string for the first occurence of any character contained in a + * Searches a string for the first occurrence of any character contained in a * breakset, and returns a pointer from the string to that character. * * \param str The null-terminated string to be searched. Must not be NULL, and @@ -3931,7 +3931,7 @@ extern SDL_DECLSPEC int SDLCALL SDL_strncasecmp(const char *str1, const char *st * \param breakset A null-terminated string containing the list of characters * to look for. Must not be NULL, and must not overlap with * `str`. - * \returns A pointer to the location, in str, of the first occurence of a + * \returns A pointer to the location, in str, of the first occurrence of a * character present in the breakset, or NULL if none is found. * * \threadsafety It is safe to call this function from any thread. @@ -5821,7 +5821,7 @@ extern SDL_DECLSPEC int SDLCALL SDL_iconv_close(SDL_iconv_t cd); * This function converts text between encodings, reading from and writing to * a buffer. * - * It returns the number of succesful conversions on success. On error, + * It returns the number of successful conversions on success. On error, * SDL_ICONV_E2BIG is returned when the output buffer is too small, or * SDL_ICONV_EILSEQ is returned when an invalid input sequence is encountered, * or SDL_ICONV_EINVAL is returned when an incomplete input sequence is diff --git a/include/SDL3/SDL_video.h b/include/SDL3/SDL_video.h index 09b55ad1c6..b012ee3df0 100644 --- a/include/SDL3/SDL_video.h +++ b/include/SDL3/SDL_video.h @@ -1519,7 +1519,7 @@ extern SDL_DECLSPEC SDL_Window * SDLCALL SDL_GetWindowParent(SDL_Window *window) * - `SDL_PROP_WINDOW_COCOA_WINDOW_POINTER`: the `(__unsafe_unretained)` * NSWindow associated with the window * - `SDL_PROP_WINDOW_COCOA_METAL_VIEW_TAG_NUMBER`: the NSInteger tag - * assocated with metal views on the window + * associated with metal views on the window * * On OpenVR: * From 248bcf6b29de94e9f1d6e82e75613ec672fe851c Mon Sep 17 00:00:00 2001 From: Pino Toscano Date: Thu, 7 Aug 2025 07:40:29 +0200 Subject: [PATCH 25/61] ime: fcitx: use SDL_GetExeName() in GetAppName() Use the existing SDL_GetExeName(), available for all the UNIX platforms, in the internal GetAppName(); this has few advantanges: - SDL_GetExeName() (and SDL_GetAppID() that builds on top of it) are used in various places already; since it caches the executable name, this may remove one extra read of the application name - SDL_GetExeName() has a non-dummy implementation in more OSes than GetAppName(), thus providing a small improvement for this IME As drive-by change: since SDL_GetExeName() provides a constant string, there is no more need to allocate a new string in GetAppName(), which is used as constant string anyway. Hence, return a constant string in GetAppName() too. --- src/core/linux/SDL_fcitx.c | 33 +++++++-------------------------- 1 file changed, 7 insertions(+), 26 deletions(-) diff --git a/src/core/linux/SDL_fcitx.c b/src/core/linux/SDL_fcitx.c index d7a9ed6656..66b21d0471 100644 --- a/src/core/linux/SDL_fcitx.c +++ b/src/core/linux/SDL_fcitx.c @@ -25,6 +25,7 @@ #include "SDL_fcitx.h" #include "../../video/SDL_sysvideo.h" #include "../../events/SDL_keyboard_c.h" +#include "../../core/unix/SDL_appid.h" #include "SDL_dbus.h" #ifdef SDL_VIDEO_DRIVER_X11 @@ -53,32 +54,14 @@ typedef struct FcitxClient static FcitxClient fcitx_client; -static char *GetAppName(void) +static const char *GetAppName(void) { -#if defined(SDL_PLATFORM_LINUX) || defined(SDL_PLATFORM_FREEBSD) - char *spot; - char procfile[1024]; - char linkfile[1024]; - int linksize; - -#ifdef SDL_PLATFORM_LINUX - (void)SDL_snprintf(procfile, sizeof(procfile), "/proc/%d/exe", getpid()); -#elif defined(SDL_PLATFORM_FREEBSD) - (void)SDL_snprintf(procfile, sizeof(procfile), "/proc/%d/file", getpid()); -#endif - linksize = readlink(procfile, linkfile, sizeof(linkfile) - 1); - if (linksize > 0) { - linkfile[linksize] = '\0'; - spot = SDL_strrchr(linkfile, '/'); - if (spot) { - return SDL_strdup(spot + 1); - } else { - return SDL_strdup(linkfile); - } + const char *exe_name = SDL_GetExeName(); + if (exe_name) { + return exe_name; } -#endif // SDL_PLATFORM_LINUX || SDL_PLATFORM_FREEBSD - return SDL_strdup("SDL_App"); + return "SDL_App"; } static size_t Fcitx_GetPreeditString(SDL_DBusContext *dbus, @@ -281,7 +264,7 @@ static bool FcitxCreateInputContext(SDL_DBusContext *dbus, const char *appname, static bool FcitxClientCreateIC(FcitxClient *client) { - char *appname = GetAppName(); + const char *appname = GetAppName(); char *ic_path = NULL; SDL_DBusContext *dbus = client->dbus; @@ -290,8 +273,6 @@ static bool FcitxClientCreateIC(FcitxClient *client) ic_path = NULL; // just in case. } - SDL_free(appname); - if (ic_path) { SDL_free(client->ic_path); client->ic_path = SDL_strdup(ic_path); From 72526333459c4357001cf24aa6395a0a0fd0a461 Mon Sep 17 00:00:00 2001 From: Frank Praznik Date: Fri, 8 Aug 2025 11:10:53 -0400 Subject: [PATCH 26/61] video: Check the display origin when a fullscreen window is moved In certain cases when moving fullscreen windows in scaled desktop configurations, the window origin might overlap two displays at once. Check if the window is at the origin of a specific display before falling back to the generic window rectangle check. Fixes rare fullscreen window misplacement when moving fullscreen windows via a desktop shortcut while using the Wayland scale-to-display mode. --- src/video/SDL_video.c | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c index ca01c117a8..c6e4112dd6 100644 --- a/src/video/SDL_video.c +++ b/src/video/SDL_video.c @@ -1674,6 +1674,21 @@ SDL_DisplayID SDL_GetDisplayForRect(const SDL_Rect *rect) return GetDisplayForRect(rect->x, rect->y, rect->w, rect->h); } +static SDL_DisplayID GetDisplayAtOrigin(int x, int y) +{ + for (int i = 0; i < _this->num_displays; ++i) { + SDL_Rect rect; + const SDL_DisplayID cur_id = _this->displays[i]->id; + if (SDL_GetDisplayBounds(cur_id, &rect)) { + if (x == rect.x && y == rect.y) { + return cur_id; + } + } + } + + return 0; +} + SDL_DisplayID SDL_GetDisplayForWindowPosition(SDL_Window *window) { int x, y; @@ -1736,7 +1751,11 @@ SDL_VideoDisplay *SDL_GetVideoDisplayForFullscreenWindow(SDL_Window *window) const int w = window->last_size_pending ? window->pending.w : window->w; const int h = window->last_size_pending ? window->pending.h : window->h; - displayID = GetDisplayForRect(x, y, w, h); + // Check if the window is exactly at the origin of a display. Otherwise, fall back to the generic check. + displayID = GetDisplayAtOrigin(x, y); + if (!displayID) { + displayID = GetDisplayForRect(x, y, w, h); + } } if (!displayID) { // Use the primary display for a window if we can't find it anywhere else @@ -2929,16 +2948,7 @@ bool SDL_SetWindowPosition(SDL_Window *window, int x, int y) * the pending fullscreen display ID if it does. This needs to be set early in case * the window is prevented from moving to the exact origin due to struts. */ - for (int i = 0; i < _this->num_displays; ++i) { - SDL_Rect rect; - const SDL_DisplayID cur_id = _this->displays[i]->id; - if (SDL_GetDisplayBounds(cur_id, &rect)) { - if (x == rect.x && y == rect.y) { - window->pending_displayID = cur_id; - break; - } - } - } + window->pending_displayID = GetDisplayAtOrigin(x, y); } window->pending.x = x; From b63c32e79076e886d9efdc38f815951bfb2817b2 Mon Sep 17 00:00:00 2001 From: Petar Popovic Date: Fri, 8 Aug 2025 19:50:12 +0200 Subject: [PATCH 27/61] SDL_SetRenderDrawBlendMode(): Remove redundant param check --- src/render/SDL_render.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c index 076b0dd51d..b9c15f8b34 100644 --- a/src/render/SDL_render.c +++ b/src/render/SDL_render.c @@ -3366,10 +3366,6 @@ bool SDL_SetRenderDrawBlendMode(SDL_Renderer *renderer, SDL_BlendMode blendMode) return SDL_InvalidParamError("blendMode"); } - if (blendMode == SDL_BLENDMODE_INVALID) { - return SDL_InvalidParamError("blendMode"); - } - if (!IsSupportedBlendMode(renderer, blendMode)) { return SDL_Unsupported(); } From 171885010dc232a3d971239253283f493d7828de Mon Sep 17 00:00:00 2001 From: Pino Toscano Date: Thu, 7 Aug 2025 23:35:39 +0200 Subject: [PATCH 28/61] Add GNU/Hurd as platform SDL has been building on GNU/Hurd for a long time, using either drivers based on external libraries (e.g. X11, pulseaudio, sndio, etc) or dummy drivers. This commit introduces it explicitly as platform, so it can be recognized, and tweaked as needed. In particular: - introduce the SDL_PLATFORM_HURD define - tighten/improve the platform detection in cmake, and use "Hurd" as identifier - return the platform name in SDL_GetPlatform() - tweak the CFLAGS/LDFLAGS so pthreads can be used properly - implement SDL_GetExeName(), using /proc/self/exe as provided by the basic Linux-like procfs - enable GLES 2 in tests (mostly for consistency with Linux) --- cmake/sdlchecks.cmake | 3 +++ cmake/sdlplatform.cmake | 4 ++-- include/SDL3/SDL_platform_defines.h | 10 ++++++++++ src/SDL.c | 2 ++ src/core/unix/SDL_appid.c | 4 ++-- test/testgles2.c | 2 +- 6 files changed, 20 insertions(+), 5 deletions(-) diff --git a/cmake/sdlchecks.cmake b/cmake/sdlchecks.cmake index 865e28f6d5..c5a51edd1d 100644 --- a/cmake/sdlchecks.cmake +++ b/cmake/sdlchecks.cmake @@ -884,6 +884,9 @@ macro(CheckPTHREAD) set(PTHREAD_LDFLAGS "-pthread") elseif(QNX) # pthread support is baked in + elseif(HURD) + set(PTHREAD_CFLAGS "-D_REENTRANT") + set(PTHREAD_LDFLAGS "-pthread") else() set(PTHREAD_CFLAGS "-D_REENTRANT") set(PTHREAD_LDFLAGS "-lpthread") diff --git a/cmake/sdlplatform.cmake b/cmake/sdlplatform.cmake index 60e0e77fd9..6b5777aeca 100644 --- a/cmake/sdlplatform.cmake +++ b/cmake/sdlplatform.cmake @@ -36,8 +36,8 @@ function(SDL_DetectCMakePlatform) set(sdl_cmake_platform NetBSD) elseif(CMAKE_SYSTEM_NAME MATCHES "kOpenBSD.*|OpenBSD.*") set(sdl_cmake_platform OpenBSD) - elseif(CMAKE_SYSTEM_NAME MATCHES ".*GNU.*") - set(sdl_cmake_platform GNU) + elseif(CMAKE_SYSTEM_NAME STREQUAL "GNU") + set(sdl_cmake_platform Hurd) elseif(CMAKE_SYSTEM_NAME MATCHES ".*BSDI.*") set(sdl_cmake_platform BSDi) elseif(CMAKE_SYSTEM_NAME MATCHES "DragonFly.*|FreeBSD") diff --git a/include/SDL3/SDL_platform_defines.h b/include/SDL3/SDL_platform_defines.h index f7f14be005..848dac3db8 100644 --- a/include/SDL3/SDL_platform_defines.h +++ b/include/SDL3/SDL_platform_defines.h @@ -484,4 +484,14 @@ #define SDL_PLATFORM_NGAGE 1 #endif +#ifdef __GNU__ + +/** + * A preprocessor macro that is only defined if compiling for GNU/Hurd. + * + * \since This macro is available since SDL 3.4.0. + */ +#define SDL_PLATFORM_HURD 1 +#endif + #endif /* SDL_platform_defines_h_ */ diff --git a/src/SDL.c b/src/SDL.c index 41a8435e6a..9b5ee8713f 100644 --- a/src/SDL.c +++ b/src/SDL.c @@ -770,6 +770,8 @@ const char *SDL_GetPlatform(void) return "PlayStation Vita"; #elif defined(SDL_PLATFORM_3DS) return "Nintendo 3DS"; +#elif defined(SDL_PLATFORM_HURD) + return "GNU/Hurd"; #elif defined(__managarm__) return "Managarm"; #else diff --git a/src/core/unix/SDL_appid.c b/src/core/unix/SDL_appid.c index 996e216cd5..8bf3349877 100644 --- a/src/core/unix/SDL_appid.c +++ b/src/core/unix/SDL_appid.c @@ -30,11 +30,11 @@ const char *SDL_GetExeName(void) // TODO: Use a fallback if BSD has no mounted procfs (OpenBSD has no procfs at all) if (!proc_name) { -#if defined(SDL_PLATFORM_LINUX) || defined(SDL_PLATFORM_FREEBSD) || defined (SDL_PLATFORM_NETBSD) +#if defined(SDL_PLATFORM_LINUX) || defined(SDL_PLATFORM_FREEBSD) || defined (SDL_PLATFORM_NETBSD) || defined(SDL_PLATFORM_HURD) static char linkfile[1024]; int linksize; -#if defined(SDL_PLATFORM_LINUX) +#if defined(SDL_PLATFORM_LINUX) || defined(SDL_PLATFORM_HURD) const char *proc_path = "/proc/self/exe"; #elif defined(SDL_PLATFORM_FREEBSD) const char *proc_path = "/proc/curproc/file"; diff --git a/test/testgles2.c b/test/testgles2.c index 1184eab7cb..ba3e0ddcda 100644 --- a/test/testgles2.c +++ b/test/testgles2.c @@ -19,7 +19,7 @@ #include -#if defined(SDL_PLATFORM_IOS) || defined(SDL_PLATFORM_ANDROID) || defined(SDL_PLATFORM_EMSCRIPTEN) || defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_LINUX) +#if defined(SDL_PLATFORM_IOS) || defined(SDL_PLATFORM_ANDROID) || defined(SDL_PLATFORM_EMSCRIPTEN) || defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_LINUX) || defined(SDL_PLATFORM_HURD) #define HAVE_OPENGLES2 #endif From de742e9f9a392730e4f7049bf1521806590f22bf Mon Sep 17 00:00:00 2001 From: Anonymous Maarten Date: Fri, 8 Aug 2025 04:55:32 +0200 Subject: [PATCH 29/61] cmake: detect RISCOS platform before GNU/Hurd --- cmake/sdlplatform.cmake | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cmake/sdlplatform.cmake b/cmake/sdlplatform.cmake index 6b5777aeca..f16fe3f2ef 100644 --- a/cmake/sdlplatform.cmake +++ b/cmake/sdlplatform.cmake @@ -26,6 +26,8 @@ function(SDL_DetectCMakePlatform) set(sdl_cmake_platform ngage) elseif(PS2) set(sdl_cmake_platform ps2) + elseif(RISCOS) + set(sdl_cmake_platform RISCOS) elseif(VITA) set(sdl_cmake_platform Vita) elseif(CMAKE_SYSTEM_NAME MATCHES ".*Linux") @@ -37,6 +39,7 @@ function(SDL_DetectCMakePlatform) elseif(CMAKE_SYSTEM_NAME MATCHES "kOpenBSD.*|OpenBSD.*") set(sdl_cmake_platform OpenBSD) elseif(CMAKE_SYSTEM_NAME STREQUAL "GNU") + # GNU/Hurd must be checked AFTER RISCOS set(sdl_cmake_platform Hurd) elseif(CMAKE_SYSTEM_NAME MATCHES ".*BSDI.*") set(sdl_cmake_platform BSDi) From d9c20cfd0a2320348bc62caea64204e5e8859a08 Mon Sep 17 00:00:00 2001 From: Petar Popovic Date: Fri, 8 Aug 2025 20:52:24 +0200 Subject: [PATCH 30/61] SDL_SendJoystickVirtualSensorDataInner(): Fix max_sensor_events increment --- src/joystick/virtual/SDL_virtualjoystick.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/joystick/virtual/SDL_virtualjoystick.c b/src/joystick/virtual/SDL_virtualjoystick.c index 69256627d7..8600c0242e 100644 --- a/src/joystick/virtual/SDL_virtualjoystick.c +++ b/src/joystick/virtual/SDL_virtualjoystick.c @@ -471,7 +471,7 @@ bool SDL_SendJoystickVirtualSensorDataInner(SDL_Joystick *joystick, SDL_SensorTy return false; } hwdata->sensor_events = sensor_events; - hwdata->max_sensor_events = hwdata->max_sensor_events; + hwdata->max_sensor_events = new_max_sensor_events; } VirtualSensorEvent *event = &hwdata->sensor_events[hwdata->num_sensor_events++]; From aff1a48bd9979f1793ccc4521eaf3a6cadf17f6e Mon Sep 17 00:00:00 2001 From: Frank Praznik Date: Fri, 8 Aug 2025 17:53:21 -0400 Subject: [PATCH 31/61] wayland: Cleanup and return if keymap allocation fails --- src/video/wayland/SDL_waylandevents.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/video/wayland/SDL_waylandevents.c b/src/video/wayland/SDL_waylandevents.c index 227246cc92..43f31552cd 100644 --- a/src/video/wayland/SDL_waylandevents.c +++ b/src/video/wayland/SDL_waylandevents.c @@ -1591,9 +1591,18 @@ static void keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard, seat->keyboard.xkb.num_layouts = WAYLAND_xkb_keymap_num_layouts(seat->keyboard.xkb.keymap); if (seat->keyboard.xkb.num_layouts) { seat->keyboard.sdl_keymap = SDL_calloc(seat->keyboard.xkb.num_layouts, sizeof(SDL_Keymap *)); + if (!seat->keyboard.sdl_keymap) { + return; + } + for (xkb_layout_index_t i = 0; i < seat->keyboard.xkb.num_layouts; ++i) { seat->keyboard.sdl_keymap[i] = SDL_CreateKeymap(false); - if (!seat->keyboard.sdl_keymap) { + if (!seat->keyboard.sdl_keymap[i]) { + for (xkb_layout_index_t j = 0; j < i; ++j) { + SDL_DestroyKeymap(seat->keyboard.sdl_keymap[j]); + } + SDL_free(seat->keyboard.sdl_keymap); + seat->keyboard.sdl_keymap = NULL; return; } } @@ -2095,7 +2104,7 @@ static void keyboard_handle_key(void *data, struct wl_keyboard *keyboard, Wayland_HandleModifierKeys(seat, scancode, state == WL_KEYBOARD_KEY_STATE_PRESSED); // If we have a key with unknown scancode, check if the keysym corresponds to a valid Unicode value, and assign it a reserved scancode. - if (scancode == SDL_SCANCODE_UNKNOWN && syms) { + if (scancode == SDL_SCANCODE_UNKNOWN && syms && seat->keyboard.sdl_keymap) { const SDL_Keycode keycode = (SDL_Keycode)SDL_KeySymToUcs4(syms[0]); if (keycode != SDLK_UNKNOWN) { SDL_Keymod modstate = SDL_KMOD_NONE; From f4c124e4bf85d6232cad75c10a82a832a1c2da09 Mon Sep 17 00:00:00 2001 From: Mohamed Shazan Date: Sat, 9 Aug 2025 04:50:59 +0530 Subject: [PATCH 32/61] SDL_TriggerBreakpoint() will default to __debugbreak() on MinGW toolchain on windows --- include/SDL3/SDL_assert.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/SDL3/SDL_assert.h b/include/SDL3/SDL_assert.h index c64fcd76e7..48f17f5fb2 100644 --- a/include/SDL3/SDL_assert.h +++ b/include/SDL3/SDL_assert.h @@ -126,7 +126,7 @@ extern "C" { */ #define SDL_TriggerBreakpoint() TriggerABreakpointInAPlatformSpecificManner -#elif defined(_MSC_VER) && _MSC_VER >= 1310 +#elif defined(__MINGW32__) || (defined(_MSC_VER) && _MSC_VER >= 1310) /* Don't include intrin.h here because it contains C++ code */ extern void __cdecl __debugbreak(void); #define SDL_TriggerBreakpoint() __debugbreak() From 7017fbaa8e593ac00de518d662a233f4cfd5b7b0 Mon Sep 17 00:00:00 2001 From: Anonymous Maarten Date: Sat, 9 Aug 2025 04:39:12 +0200 Subject: [PATCH 33/61] release: build aarch64 libraries with 16kiB page size [ci skip] --- build-scripts/build-release.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/build-scripts/build-release.py b/build-scripts/build-release.py index c00743e97b..b256e4d54c 100755 --- a/build-scripts/build-release.py +++ b/build-scripts/build-release.py @@ -542,6 +542,9 @@ class AndroidApiVersion: def __repr__(self) -> str: return f"<{self.name} ({'.'.join(str(v) for v in self.ints)})>" +ANDROID_ABI_EXTRA_LINK_OPTIONS = { + "arm64-v8a": "-Wl,-z,max-page-size=16384 -Wl,-z,common-page-size=16384", +} class Releaser: def __init__(self, release_info: dict, commit: str, revision: str, root: Path, dist_path: Path, section_printer: SectionPrinter, executer: Executer, cmake_generator: str, deps_path: Path, overwrite: bool, github: bool, fast: bool): @@ -1013,6 +1016,7 @@ class Releaser: android_devel_file_tree = ArchiveFileTree() for android_abi in android_abis: + extra_link_options = ANDROID_ABI_EXTRA_LINK_OPTIONS.get(android_abi, "") with self.section_printer.group(f"Building for Android {android_api} {android_abi}"): build_dir = self.root / "build-android" / f"{android_abi}-build" install_dir = self.root / "install-android" / f"{android_abi}-install" @@ -1026,6 +1030,8 @@ class Releaser: # NDK 21e does not support -ffile-prefix-map # f'''-DCMAKE_C_FLAGS="-ffile-prefix-map={self.root}=/src/{self.project}"''', # f'''-DCMAKE_CXX_FLAGS="-ffile-prefix-map={self.root}=/src/{self.project}"''', + f"-DCMAKE_EXE_LINKER_FLAGS={extra_link_options}", + f"-DCMAKE_SHARED_LINKER_FLAGS={extra_link_options}", f"-DCMAKE_TOOLCHAIN_FILE={cmake_toolchain_file}", f"-DCMAKE_PREFIX_PATH={str(android_deps_path)}", f"-DCMAKE_FIND_ROOT_PATH_MODE_PACKAGE=BOTH", From d4819db5e6abc992128c38356fae6453652a8fc4 Mon Sep 17 00:00:00 2001 From: Petar Popovic Date: Sat, 9 Aug 2025 15:36:42 +0200 Subject: [PATCH 34/61] SDL_waylandvideo.c:display_remove_global(): Check pointer when removing mouse --- src/video/wayland/SDL_waylandvideo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video/wayland/SDL_waylandvideo.c b/src/video/wayland/SDL_waylandvideo.c index e9a97a98f1..68fb9961c8 100644 --- a/src/video/wayland/SDL_waylandvideo.c +++ b/src/video/wayland/SDL_waylandvideo.c @@ -1374,7 +1374,7 @@ static void display_remove_global(void *data, struct wl_registry *registry, uint if (seat->keyboard.wl_keyboard) { SDL_RemoveKeyboard(seat->keyboard.sdl_id, true); } - if (seat->keyboard.wl_keyboard) { + if (seat->pointer.wl_pointer) { SDL_RemoveMouse(seat->pointer.sdl_id, true); } Wayland_SeatDestroy(seat, true); From 43f39913980b85a400f6b20648708372d2c71739 Mon Sep 17 00:00:00 2001 From: Petar Popovic Date: Sat, 9 Aug 2025 18:00:04 +0200 Subject: [PATCH 35/61] linux/SDL_syshaptic.c:SDL_SYS_HapticStopAll(): Fix return on error --- src/haptic/linux/SDL_syshaptic.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/haptic/linux/SDL_syshaptic.c b/src/haptic/linux/SDL_syshaptic.c index 842f577cfa..fa49e457bd 100644 --- a/src/haptic/linux/SDL_syshaptic.c +++ b/src/haptic/linux/SDL_syshaptic.c @@ -1117,13 +1117,12 @@ bool SDL_SYS_HapticResume(SDL_Haptic *haptic) */ bool SDL_SYS_HapticStopAll(SDL_Haptic *haptic) { - int i, ret; + int i; // Linux does not support this natively so we have to loop. for (i = 0; i < haptic->neffects; i++) { if (haptic->effects[i].hweffect != NULL) { - ret = SDL_SYS_HapticStopEffect(haptic, &haptic->effects[i]); - if (ret < 0) { + if (!SDL_SYS_HapticStopEffect(haptic, &haptic->effects[i])) { return SDL_SetError("Haptic: Error while trying to stop all playing effects."); } } From 44ce826b57f2d19a4df2de5dc3fe6a3a062dee1f Mon Sep 17 00:00:00 2001 From: SDL Wiki Bot Date: Sat, 9 Aug 2025 18:34:27 +0000 Subject: [PATCH 36/61] Sync SDL3 wiki -> header [ci skip] --- include/SDL3/SDL_rect.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/SDL3/SDL_rect.h b/include/SDL3/SDL_rect.h index e09f92a460..7ff10904ff 100644 --- a/include/SDL3/SDL_rect.h +++ b/include/SDL3/SDL_rect.h @@ -88,8 +88,7 @@ typedef struct SDL_Rect /** - * A rectangle, with the origin at the upper left (using floating point - * values). + * A rectangle stored using floating point values. * * \since This struct is available since SDL 3.2.0. * From 3970acd1c25a619f6b1eed26c9af77e7ea7e3350 Mon Sep 17 00:00:00 2001 From: SDL Wiki Bot Date: Sun, 10 Aug 2025 02:21:43 +0000 Subject: [PATCH 37/61] Sync SDL3 wiki -> header [ci skip] --- include/SDL3/SDL_filesystem.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/SDL3/SDL_filesystem.h b/include/SDL3/SDL_filesystem.h index e428258218..bd5f92176b 100644 --- a/include/SDL3/SDL_filesystem.h +++ b/include/SDL3/SDL_filesystem.h @@ -373,7 +373,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_RemovePath(const char *path); /** * Rename a file or directory. * - * If the file at `newpath` already exists, it will replaced. + * If the file at `newpath` already exists, it will be replaced. * * Note that this will not copy files across filesystems/drives/volumes, as * that is a much more complicated (and possibly time-consuming) operation. From 40ec9592f1da0f91a185f0698bb055472b54604c Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sat, 9 Aug 2025 22:07:14 -0700 Subject: [PATCH 38/61] Restored text missing during wiki sync (thanks @sezero!) --- include/SDL3/SDL_rect.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/SDL3/SDL_rect.h b/include/SDL3/SDL_rect.h index 7ff10904ff..ff79ad4e47 100644 --- a/include/SDL3/SDL_rect.h +++ b/include/SDL3/SDL_rect.h @@ -90,6 +90,9 @@ typedef struct SDL_Rect /** * A rectangle stored using floating point values. * + * The origin of the coordinate space is in the top-left, with increasing values moving down and right. + * The properties `x` and `y` represent the coordinates of the top-left corner of the rectangle. + * * \since This struct is available since SDL 3.2.0. * * \sa SDL_RectEmptyFloat From f2df279adce63055156d575128e9c7e2105fc7e7 Mon Sep 17 00:00:00 2001 From: SDL Wiki Bot Date: Sun, 10 Aug 2025 05:08:26 +0000 Subject: [PATCH 39/61] Sync SDL3 wiki -> header [ci skip] --- include/SDL3/SDL_rect.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/SDL3/SDL_rect.h b/include/SDL3/SDL_rect.h index ff79ad4e47..5911bd3b2b 100644 --- a/include/SDL3/SDL_rect.h +++ b/include/SDL3/SDL_rect.h @@ -90,8 +90,9 @@ typedef struct SDL_Rect /** * A rectangle stored using floating point values. * - * The origin of the coordinate space is in the top-left, with increasing values moving down and right. - * The properties `x` and `y` represent the coordinates of the top-left corner of the rectangle. + * The origin of the coordinate space is in the top-left, with increasing + * values moving down and right. The properties `x` and `y` represent the + * coordinates of the top-left corner of the rectangle. * * \since This struct is available since SDL 3.2.0. * From e699f3dca1c03051a2736cde6fb6d45839f33062 Mon Sep 17 00:00:00 2001 From: Beyley Cardellio Date: Sun, 10 Aug 2025 00:13:14 -0700 Subject: [PATCH 40/61] GPU: Hold submit lock before waiting for device idle --- src/gpu/vulkan/SDL_gpu_vulkan.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/gpu/vulkan/SDL_gpu_vulkan.c b/src/gpu/vulkan/SDL_gpu_vulkan.c index f782dc2d0a..a1e074802a 100644 --- a/src/gpu/vulkan/SDL_gpu_vulkan.c +++ b/src/gpu/vulkan/SDL_gpu_vulkan.c @@ -10495,11 +10495,18 @@ static bool VULKAN_Wait( VkResult result; Sint32 i; + SDL_LockMutex(renderer->submitLock); + result = renderer->vkDeviceWaitIdle(renderer->logicalDevice); - CHECK_VULKAN_ERROR_AND_RETURN(result, vkDeviceWaitIdle, false); - - SDL_LockMutex(renderer->submitLock); + if (result != VK_SUCCESS) { + if (renderer->debugMode) { + SDL_LogError(SDL_LOG_CATEGORY_GPU, "%s %s", "vkDeviceWaitIdle", VkErrorMessages(result)); + } + SDL_SetError("%s %s", "vkDeviceWaitIdle", VkErrorMessages(result)); + SDL_UnlockMutex(renderer->submitLock); + return false; + } for (i = renderer->submittedCommandBufferCount - 1; i >= 0; i -= 1) { commandBuffer = renderer->submittedCommandBuffers[i]; From 6e422e5ff2a615fefb666e840ddcedc6b345ce95 Mon Sep 17 00:00:00 2001 From: Chase Knowlden Date: Tue, 12 Aug 2025 10:45:57 -0400 Subject: [PATCH 41/61] Update NDK version to 28 (#13729) * Update NDK version to 28 and add 16kb page size linker flags to x86_64 * Remove Android Linker Options 16kb page size is now the default since NDK r28c * Update Android CI to use NDK 28 --- .github/workflows/release.yml | 2 +- build-scripts/build-release.py | 4 +--- build-scripts/release-info.json | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6493b72b2a..13c50ec7ab 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -536,7 +536,7 @@ jobs: uses: nttld/setup-ndk@v1 with: local-cache: true - ndk-version: r21e + ndk-version: r28c - name: 'Setup Java JDK' uses: actions/setup-java@v4 with: diff --git a/build-scripts/build-release.py b/build-scripts/build-release.py index b256e4d54c..e25bda8c11 100755 --- a/build-scripts/build-release.py +++ b/build-scripts/build-release.py @@ -542,9 +542,7 @@ class AndroidApiVersion: def __repr__(self) -> str: return f"<{self.name} ({'.'.join(str(v) for v in self.ints)})>" -ANDROID_ABI_EXTRA_LINK_OPTIONS = { - "arm64-v8a": "-Wl,-z,max-page-size=16384 -Wl,-z,common-page-size=16384", -} +ANDROID_ABI_EXTRA_LINK_OPTIONS = {} class Releaser: def __init__(self, release_info: dict, commit: str, revision: str, root: Path, dist_path: Path, section_printer: SectionPrinter, executer: Executer, cmake_generator: str, deps_path: Path, overwrite: bool, github: bool, fast: bool): diff --git a/build-scripts/release-info.json b/build-scripts/release-info.json index 8ba213ae27..4847751414 100644 --- a/build-scripts/release-info.json +++ b/build-scripts/release-info.json @@ -183,7 +183,7 @@ ], "api-minimum": 21, "api-target": 35, - "ndk-minimum": 21, + "ndk-minimum": 28, "aar-files": { "": [ "android-project/app/proguard-rules.pro:proguard.txt", From 970c0bfe961ba55a7dce4043dec79e4d68314748 Mon Sep 17 00:00:00 2001 From: Sylvain Date: Fri, 8 Aug 2025 11:43:42 +0200 Subject: [PATCH 42/61] Fixed bug #13493: Assertion failure at SDL_AddTouch with Android API 28 Java touch id should be -1 because it's reserved for internal SDL synthetic events. It should also not be 0, because this is SDL invalid value. --- src/core/android/SDL_android.c | 3 ++- src/video/android/SDL_androidtouch.c | 25 ++++++++++++++++++++----- src/video/android/SDL_androidtouch.h | 1 + 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/core/android/SDL_android.c b/src/core/android/SDL_android.c index 887bcb91c0..e0745a0a57 100644 --- a/src/core/android/SDL_android.c +++ b/src/core/android/SDL_android.c @@ -1080,7 +1080,8 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeAddTouch)( { const char *utfname = (*env)->GetStringUTFChars(env, name, NULL); - SDL_AddTouch((SDL_TouchID)touchId, SDL_TOUCH_DEVICE_DIRECT, utfname); + SDL_AddTouch(Android_ConvertJavaTouchID(touchId), + SDL_TOUCH_DEVICE_DIRECT, utfname); (*env)->ReleaseStringUTFChars(env, name, utfname); } diff --git a/src/video/android/SDL_androidtouch.c b/src/video/android/SDL_androidtouch.c index 661c9a1674..2e1b6026b1 100644 --- a/src/video/android/SDL_androidtouch.c +++ b/src/video/android/SDL_androidtouch.c @@ -47,6 +47,25 @@ void Android_QuitTouch(void) { } + +SDL_TouchID Android_ConvertJavaTouchID(int touchID) +{ + SDL_TouchID retval = touchID; + + if (touchID < 0) { + // Touch ID -1 appears when using Android emulator, eg: + // adb shell input mouse tap 100 100 + // adb shell input touchscreen tap 100 100 + // + // Prevent to be -1, since it's used in SDL internal for synthetic events: + retval -= 1; + } else { + // Touch ID 0 is invalid + retval += 1; + } + return retval; +} + void Android_OnTouch(SDL_Window *window, int touch_device_id_in, int pointer_finger_id_in, int action, float x, float y, float p) { SDL_TouchID touchDeviceId = 0; @@ -56,11 +75,7 @@ void Android_OnTouch(SDL_Window *window, int touch_device_id_in, int pointer_fin return; } - /* Touch device -1 appears when using Android emulator, eg: - * adb shell input mouse tap 100 100 - * adb shell input touchscreen tap 100 100 - */ - touchDeviceId = (SDL_TouchID)(touch_device_id_in + 2); + touchDeviceId = Android_ConvertJavaTouchID(touch_device_id_in); // Finger ID should be greater than 0 fingerId = (SDL_FingerID)(pointer_finger_id_in + 1); diff --git a/src/video/android/SDL_androidtouch.h b/src/video/android/SDL_androidtouch.h index 2aef82264d..ae10365e94 100644 --- a/src/video/android/SDL_androidtouch.h +++ b/src/video/android/SDL_androidtouch.h @@ -25,3 +25,4 @@ extern void Android_InitTouch(void); extern void Android_QuitTouch(void); extern void Android_OnTouch(SDL_Window *window, int touch_device_id_in, int pointer_finger_id_in, int action, float x, float y, float p); +extern SDL_TouchID Android_ConvertJavaTouchID(int touchID); From 4b2a87a5d383219ffe9b3b9fef1ac5fd99b50180 Mon Sep 17 00:00:00 2001 From: SDL Wiki Bot Date: Tue, 12 Aug 2025 16:53:08 +0000 Subject: [PATCH 43/61] Sync SDL3 wiki -> header [ci skip] --- include/SDL3/SDL_video.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/SDL3/SDL_video.h b/include/SDL3/SDL_video.h index b012ee3df0..aec627967c 100644 --- a/include/SDL3/SDL_video.h +++ b/include/SDL3/SDL_video.h @@ -1106,7 +1106,8 @@ extern SDL_DECLSPEC SDL_Window ** SDLCALL SDL_GetWindows(int *count); * - `SDL_WINDOW_TRANSPARENT`: window with transparent buffer * - `SDL_WINDOW_NOT_FOCUSABLE`: window should not be focusable * - * The SDL_Window is implicitly shown if SDL_WINDOW_HIDDEN is not set. + * The SDL_Window will be shown if SDL_WINDOW_HIDDEN is not set. If hidden at + * creation time, SDL_ShowWindow() can be used to show it later. * * On Apple's macOS, you **must** set the NSHighResolutionCapable Info.plist * property to YES, otherwise you will not receive a High-DPI OpenGL canvas. From 4725213eeff211267fa9c8f71bee1baad406f590 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Tue, 12 Aug 2025 09:53:41 -0700 Subject: [PATCH 44/61] Support the "ambient" value for SDL_HINT_AUDIO_CATEGORY Fixes https://github.com/libsdl-org/SDL/issues/13732 --- src/audio/coreaudio/SDL_coreaudio.m | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/audio/coreaudio/SDL_coreaudio.m b/src/audio/coreaudio/SDL_coreaudio.m index e7d9e58a39..3a2217802f 100644 --- a/src/audio/coreaudio/SDL_coreaudio.m +++ b/src/audio/coreaudio/SDL_coreaudio.m @@ -420,7 +420,8 @@ static bool UpdateAudioSession(SDL_AudioDevice *device, bool open, bool allow_pl hint = SDL_GetHint(SDL_HINT_AUDIO_CATEGORY); if (hint) { - if (SDL_strcasecmp(hint, "AVAudioSessionCategoryAmbient") == 0) { + if (SDL_strcasecmp(hint, "AVAudioSessionCategoryAmbient") == 0 || + SDL_strcasecmp(hint, "ambient") == 0) { category = AVAudioSessionCategoryAmbient; } else if (SDL_strcasecmp(hint, "AVAudioSessionCategorySoloAmbient") == 0) { category = AVAudioSessionCategorySoloAmbient; From 7487880e4cfa0b35b9d4fa5cd935eb27113f6e39 Mon Sep 17 00:00:00 2001 From: SDL Wiki Bot Date: Tue, 12 Aug 2025 17:05:23 +0000 Subject: [PATCH 45/61] Sync SDL3 wiki -> header [ci skip] --- include/SDL3/SDL_video.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/SDL3/SDL_video.h b/include/SDL3/SDL_video.h index aec627967c..e78ba111df 100644 --- a/include/SDL3/SDL_video.h +++ b/include/SDL3/SDL_video.h @@ -1077,8 +1077,6 @@ extern SDL_DECLSPEC SDL_Window ** SDLCALL SDL_GetWindows(int *count); * * - `SDL_WINDOW_FULLSCREEN`: fullscreen window at desktop resolution * - `SDL_WINDOW_OPENGL`: window usable with an OpenGL context - * - `SDL_WINDOW_OCCLUDED`: window partially or completely obscured by another - * window * - `SDL_WINDOW_HIDDEN`: window is not visible * - `SDL_WINDOW_BORDERLESS`: no window decoration * - `SDL_WINDOW_RESIZABLE`: window can be resized From 87543ba18c5c592be4aebbc750c978b0ee4ed15f Mon Sep 17 00:00:00 2001 From: Mitch Cairns Date: Tue, 5 Aug 2025 17:35:38 -0700 Subject: [PATCH 46/61] SInput: Mapping updates for GCUltimate/ProGCC - In previous firmwares for my gamepads, the buttons were sent in a static order for ABXY. - To better be 'transparent' to the driver and save myself from future fragmentation, I am updating the mappings to be consistent, where the HID report should always expect to receive the button inputs based on cardinal button directions rather than button labels. - This better aligns with the existing 'fallback' behavior of a generic device. - Coincides with firmware library update: https://github.com/HandHeldLegend/HOJA-LIB-RP2040/pull/30 --- src/joystick/SDL_gamepad.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/joystick/SDL_gamepad.c b/src/joystick/SDL_gamepad.c index 123b534b2a..6c021df9e8 100644 --- a/src/joystick/SDL_gamepad.c +++ b/src/joystick/SDL_gamepad.c @@ -823,7 +823,7 @@ static GamepadMapping_t *SDL_CreateMappingForHIDAPIGamepad(SDL_GUID guid) switch (sub_type) { default: // ProGCC Primary Mapping - SDL_strlcat(mapping_string, "a:b1,b:b0,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b4,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b7,rightstick:b5,righttrigger:b9,rightx:a2,righty:a3,start:b10,x:b3,y:b2,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,", sizeof(mapping_string)); + SDL_strlcat(mapping_string, "b:b0,a:b1,y:b2,x:b3,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b4,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b7,rightstick:b5,righttrigger:b9,rightx:a2,righty:a3,start:b10,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,", sizeof(mapping_string)); break; } break; @@ -831,7 +831,7 @@ static GamepadMapping_t *SDL_CreateMappingForHIDAPIGamepad(SDL_GUID guid) switch (sub_type) { default: // GC Ultimate Primary Map - SDL_strlcat(mapping_string, "a:b0,b:b2,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b4,lefttrigger:a4,leftx:a0,lefty:a1,misc1:b13,misc2:b14,rightshoulder:b7,rightstick:b5,righttrigger:a5,rightx:a2,righty:a3,start:b10,x:b1,y:b3,misc3:b8,misc4:b9,hint:!SDL_GAMECONTROLLER_USE_GAMECUBE_LABELS:=1,", sizeof(mapping_string)); + SDL_strlcat(mapping_string, "b:b0,a:b1,y:b2,x:b3,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b4,lefttrigger:a4,leftx:a0,lefty:a1,misc1:b13,misc2:b14,rightshoulder:b7,rightstick:b5,righttrigger:a5,rightx:a2,righty:a3,start:b10,misc3:b8,misc4:b9,hint:!SDL_GAMECONTROLLER_USE_GAMECUBE_LABELS:=1,", sizeof(mapping_string)); break; } break; From 62b9ac3a098f3535e1d32aebec5f63c5fb036618 Mon Sep 17 00:00:00 2001 From: Mitch Cairns Date: Tue, 5 Aug 2025 18:00:12 -0700 Subject: [PATCH 47/61] SEWN Button Convention --- src/joystick/SDL_gamepad.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/joystick/SDL_gamepad.c b/src/joystick/SDL_gamepad.c index 6c021df9e8..dca3cfcb91 100644 --- a/src/joystick/SDL_gamepad.c +++ b/src/joystick/SDL_gamepad.c @@ -823,7 +823,7 @@ static GamepadMapping_t *SDL_CreateMappingForHIDAPIGamepad(SDL_GUID guid) switch (sub_type) { default: // ProGCC Primary Mapping - SDL_strlcat(mapping_string, "b:b0,a:b1,y:b2,x:b3,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b4,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b7,rightstick:b5,righttrigger:b9,rightx:a2,righty:a3,start:b10,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,", sizeof(mapping_string)); + SDL_strlcat(mapping_string, "a:b0,b:b1,x:b2,y:b3,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b4,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b7,rightstick:b5,righttrigger:b9,rightx:a2,righty:a3,start:b10,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,", sizeof(mapping_string)); break; } break; @@ -831,7 +831,7 @@ static GamepadMapping_t *SDL_CreateMappingForHIDAPIGamepad(SDL_GUID guid) switch (sub_type) { default: // GC Ultimate Primary Map - SDL_strlcat(mapping_string, "b:b0,a:b1,y:b2,x:b3,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b4,lefttrigger:a4,leftx:a0,lefty:a1,misc1:b13,misc2:b14,rightshoulder:b7,rightstick:b5,righttrigger:a5,rightx:a2,righty:a3,start:b10,misc3:b8,misc4:b9,hint:!SDL_GAMECONTROLLER_USE_GAMECUBE_LABELS:=1,", sizeof(mapping_string)); + SDL_strlcat(mapping_string, "a:b0,b:b1,x:b2,y:b3,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b4,lefttrigger:a4,leftx:a0,lefty:a1,misc1:b13,misc2:b14,rightshoulder:b7,rightstick:b5,righttrigger:a5,rightx:a2,righty:a3,start:b10,misc3:b8,misc4:b9,hint:!SDL_GAMECONTROLLER_USE_GAMECUBE_LABELS:=1,", sizeof(mapping_string)); break; } break; @@ -839,7 +839,7 @@ static GamepadMapping_t *SDL_CreateMappingForHIDAPIGamepad(SDL_GUID guid) switch (sub_type) { default: // Default Fully Exposed Mapping (Development Purposes) - SDL_strlcat(mapping_string, "leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,b:b0,a:b1,y:b2,x:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftstick:b4,rightstick:b5,leftshoulder:b6,rightshoulder:b7,paddle1:b10,paddle2:b11,start:b12,back:b13,guide:b14,misc1:b15,paddle3:b16,paddle4:b17,touchpad:b18,misc2:b19,misc3:b20,misc4:b21,misc5:b22,misc6:b23", sizeof(mapping_string)); + SDL_strlcat(mapping_string, "leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,a:b0,b:b1,x:b2,y:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftstick:b4,rightstick:b5,leftshoulder:b6,rightshoulder:b7,paddle1:b10,paddle2:b11,start:b12,back:b13,guide:b14,misc1:b15,paddle3:b16,paddle4:b17,touchpad:b18,misc2:b19,misc3:b20,misc4:b21,misc5:b22,misc6:b23", sizeof(mapping_string)); break; } break; From 10478c59dbcabf349d06166b3c4e98a2d602b3fa Mon Sep 17 00:00:00 2001 From: ChaseKnowlden Date: Tue, 12 Aug 2025 18:29:22 -0400 Subject: [PATCH 48/61] Keep MSVC Flags Consistent across CMake runs --- CMakeLists.txt | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8176dc69ee..8c953ab88d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -184,29 +184,6 @@ if(MSVC) set(SDL_RELOCATABLE_DEFAULT ON) endif() -if(MSVC) - if(NOT SDL_LIBC) - # Make sure /RTC1 is disabled, otherwise it will use functions from the CRT - foreach(flag_var - CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE - CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO - CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE - CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) - string(REGEX REPLACE "/RTC(su|[1su])" "" ${flag_var} "${${flag_var}}") - endforeach(flag_var) - set(CMAKE_MSVC_RUNTIME_CHECKS "") - endif() - - if(MSVC_CLANG) - # clang-cl treats /W4 as '-Wall -Wextra' -- we don't need -Wextra - foreach(flag_var - CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE - CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO) - string(REGEX REPLACE "/W4" "/W3" ${flag_var} "${${flag_var}}") - endforeach(flag_var) - endif() -endif() - set(SDL_SHARED_DEFAULT ON) set(SDL_STATIC_DEFAULT ON) @@ -444,6 +421,29 @@ if(SDL_PRESEED) SDL_Preseed_CMakeCache() endif() +if(MSVC) + if(NOT SDL_LIBC) + # Make sure /RTC1 is disabled, otherwise it will use functions from the CRT + foreach(flag_var + CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE + CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO + CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE + CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) + string(REGEX REPLACE "/RTC(su|[1su])" "" ${flag_var} "${${flag_var}}") + endforeach(flag_var) + set(CMAKE_MSVC_RUNTIME_CHECKS "") + endif() + + if(MSVC_CLANG) + # clang-cl treats /W4 as '-Wall -Wextra' -- we don't need -Wextra + foreach(flag_var + CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE + CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO) + string(REGEX REPLACE "/W4" "/W3" ${flag_var} "${${flag_var}}") + endforeach(flag_var) + endif() +endif() + if(SDL_SHARED) add_library(SDL3-shared SHARED) add_library(SDL3::SDL3-shared ALIAS SDL3-shared) From bd7d4708e35bd2edf68f873131b4623d506f1917 Mon Sep 17 00:00:00 2001 From: Salman Alshamrani Date: Mon, 23 Dec 2024 18:15:59 -0500 Subject: [PATCH 49/61] Fix text field resetting text when replaced with a short string Triggered by auto-filling a password with less than 16 characters from a password manager. --- src/video/uikit/SDL_uikitviewcontroller.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video/uikit/SDL_uikitviewcontroller.m b/src/video/uikit/SDL_uikitviewcontroller.m index 45f2d64f08..8668c4adad 100644 --- a/src/video/uikit/SDL_uikitviewcontroller.m +++ b/src/video/uikit/SDL_uikitviewcontroller.m @@ -633,7 +633,7 @@ static void SDLCALL SDL_HideHomeIndicatorHintChanged(void *userdata, const char - (BOOL)textField:(UITextField *)_textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string { if (textField.markedTextRange == nil) { - if (textField.text.length < 16) { + if ([string length] == 0 && textField.text.length < 16) { [self resetTextState]; } } From 29cff6e2645cd7d637c502af11a9e3cf8063ccdf Mon Sep 17 00:00:00 2001 From: Salman Alshamrani Date: Fri, 3 Jan 2025 20:58:03 -0500 Subject: [PATCH 50/61] Work around password integrations hiding software keyboard and preventing autofill --- src/video/uikit/SDL_uikitviewcontroller.m | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/video/uikit/SDL_uikitviewcontroller.m b/src/video/uikit/SDL_uikitviewcontroller.m index 8668c4adad..c5b27c0700 100644 --- a/src/video/uikit/SDL_uikitviewcontroller.m +++ b/src/video/uikit/SDL_uikitviewcontroller.m @@ -554,6 +554,14 @@ static void SDLCALL SDL_HideHomeIndicatorHintChanged(void *userdata, const char - (void)textFieldTextDidChange:(NSNotification *)notification { + // When opening a password manager overlay to select a password and have it auto-filled, + // text input becomes stopped as a result of the keyboard being hidden or the text field losing focus. + // As a workaround, ensure text input is activated on any changes to the text field. + bool startTextInputMomentarily = !SDL_TextInputActive(window); + + if (startTextInputMomentarily) + SDL_StartTextInput(window); + if (textField.markedTextRange == nil) { NSUInteger compareLength = SDL_min(textField.text.length, committedText.length); NSUInteger matchLength; @@ -588,6 +596,9 @@ static void SDLCALL SDL_HideHomeIndicatorHintChanged(void *userdata, const char } committedText = textField.text; } + + if (startTextInputMomentarily) + SDL_StopTextInput(window); } - (void)updateKeyboard From ec0e4e21c714be26549389fbc556946ad3508c1c Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Wed, 13 Aug 2025 16:32:02 -0400 Subject: [PATCH 51/61] docs: Mark most of SDL_filesystem.h as thread-safe. Fixes #13738. --- include/SDL3/SDL_filesystem.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/include/SDL3/SDL_filesystem.h b/include/SDL3/SDL_filesystem.h index bd5f92176b..dd9430a819 100644 --- a/include/SDL3/SDL_filesystem.h +++ b/include/SDL3/SDL_filesystem.h @@ -89,6 +89,8 @@ extern "C" { * doesn't implement this functionality, call SDL_GetError() for more * information. * + * \threadsafety It is safe to call this function from any thread. + * * \since This function is available since SDL 3.2.0. * * \sa SDL_GetPrefPath @@ -150,6 +152,8 @@ extern SDL_DECLSPEC const char * SDLCALL SDL_GetBasePath(void); * etc.). This should be freed with SDL_free() when it is no longer * needed. * + * \threadsafety It is safe to call this function from any thread. + * * \since This function is available since SDL 3.2.0. * * \sa SDL_GetBasePath @@ -222,6 +226,8 @@ typedef enum SDL_Folder * \returns either a null-terminated C string containing the full path to the * folder, or NULL if an error happened. * + * \threadsafety It is safe to call this function from any thread. + * * \since This function is available since SDL 3.2.0. */ extern SDL_DECLSPEC const char * SDLCALL SDL_GetUserFolder(SDL_Folder folder); @@ -289,6 +295,8 @@ typedef Uint32 SDL_GlobFlags; * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety It is safe to call this function from any thread. + * * \since This function is available since SDL 3.2.0. */ extern SDL_DECLSPEC bool SDLCALL SDL_CreateDirectory(const char *path); @@ -352,6 +360,8 @@ typedef SDL_EnumerationResult (SDLCALL *SDL_EnumerateDirectoryCallback)(void *us * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety It is safe to call this function from any thread. + * * \since This function is available since SDL 3.2.0. */ extern SDL_DECLSPEC bool SDLCALL SDL_EnumerateDirectory(const char *path, SDL_EnumerateDirectoryCallback callback, void *userdata); @@ -366,6 +376,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_EnumerateDirectory(const char *path, SDL_En * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety It is safe to call this function from any thread. + * * \since This function is available since SDL 3.2.0. */ extern SDL_DECLSPEC bool SDLCALL SDL_RemovePath(const char *path); @@ -389,6 +401,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_RemovePath(const char *path); * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety It is safe to call this function from any thread. + * * \since This function is available since SDL 3.2.0. */ extern SDL_DECLSPEC bool SDLCALL SDL_RenamePath(const char *oldpath, const char *newpath); @@ -429,6 +443,10 @@ extern SDL_DECLSPEC bool SDLCALL SDL_RenamePath(const char *oldpath, const char * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety It is safe to call this function from any thread, but this + * operation is not atomic, so the app might need to protect + * access to specific paths from other threads if appropriate. + * * \since This function is available since SDL 3.2.0. */ extern SDL_DECLSPEC bool SDLCALL SDL_CopyFile(const char *oldpath, const char *newpath); @@ -442,6 +460,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_CopyFile(const char *oldpath, const char *n * \returns true on success or false if the file doesn't exist, or another * failure; call SDL_GetError() for more information. * + * \threadsafety It is safe to call this function from any thread. + * * \since This function is available since SDL 3.2.0. */ extern SDL_DECLSPEC bool SDLCALL SDL_GetPathInfo(const char *path, SDL_PathInfo *info); @@ -496,6 +516,8 @@ extern SDL_DECLSPEC char ** SDLCALL SDL_GlobDirectory(const char *path, const ch * platform-dependent notation. NULL if there's a problem. This * should be freed with SDL_free() when it is no longer needed. * + * \threadsafety It is safe to call this function from any thread. + * * \since This function is available since SDL 3.2.0. */ extern SDL_DECLSPEC char * SDLCALL SDL_GetCurrentDirectory(void); From a743fb578c7adb066d1a00ad56d229e003fdbe73 Mon Sep 17 00:00:00 2001 From: ChaseKnowlden Date: Wed, 13 Aug 2025 14:17:51 -0400 Subject: [PATCH 52/61] Use PulseAudio fragsize buffer correctly Fixes broken microphone input in Sober --- src/audio/pulseaudio/SDL_pulseaudio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/audio/pulseaudio/SDL_pulseaudio.c b/src/audio/pulseaudio/SDL_pulseaudio.c index 4d618a6e2b..debcdf27d6 100644 --- a/src/audio/pulseaudio/SDL_pulseaudio.c +++ b/src/audio/pulseaudio/SDL_pulseaudio.c @@ -732,7 +732,7 @@ static bool PULSEAUDIO_OpenDevice(SDL_AudioDevice *device) if (!actual_bufattr) { result = SDL_SetError("Could not determine connected PulseAudio stream's buffer attributes"); } else { - device->buffer_size = (int) recording ? actual_bufattr->tlength : actual_bufattr->fragsize; + device->buffer_size = (int) recording ? actual_bufattr->fragsize : actual_bufattr->tlength; device->sample_frames = device->buffer_size / SDL_AUDIO_FRAMESIZE(device->spec); } } From 45feacf6088b57f4cf57fa1b61d49e71a68af1c7 Mon Sep 17 00:00:00 2001 From: Petar Popovic Date: Wed, 13 Aug 2025 22:59:17 +0200 Subject: [PATCH 53/61] emscripten tests: fix warning: uninitialized variable --- test/testautomation_rect.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/testautomation_rect.c b/test/testautomation_rect.c index fa5c52ccea..7f7e4eb6ee 100644 --- a/test/testautomation_rect.c +++ b/test/testautomation_rect.c @@ -916,7 +916,7 @@ static int SDLCALL rect_testIntersectRectEmpty(void *arg) */ static int SDLCALL rect_testIntersectRectParam(void *arg) { - SDL_Rect rectA; + SDL_Rect rectA = { 0 }; SDL_Rect rectB = { 0 }; SDL_Rect result; bool intersection; @@ -1165,7 +1165,7 @@ static int SDLCALL rect_testHasIntersectionEmpty(void *arg) */ static int SDLCALL rect_testHasIntersectionParam(void *arg) { - SDL_Rect rectA; + SDL_Rect rectA = { 0 }; SDL_Rect rectB = { 0 }; bool intersection; @@ -1726,7 +1726,7 @@ static int SDLCALL rect_testUnionRectInside(void *arg) */ static int SDLCALL rect_testUnionRectParam(void *arg) { - SDL_Rect rectA, rectB = { 0 }; + SDL_Rect rectA = { 0 }, rectB = { 0 }; SDL_Rect result; /* invalid parameter combinations */ From eeae48464e1240462b6839ab932c4b19e64be8bd Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Wed, 13 Aug 2025 19:42:28 -0700 Subject: [PATCH 54/61] Fixed crash if X11 initialization fails --- src/video/x11/SDL_x11mouse.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/video/x11/SDL_x11mouse.c b/src/video/x11/SDL_x11mouse.c index 6cfe1c0501..8b468675f6 100644 --- a/src/video/x11/SDL_x11mouse.c +++ b/src/video/x11/SDL_x11mouse.c @@ -536,8 +536,10 @@ void X11_QuitMouse(SDL_VideoDevice *_this) int j; for (j = 0; j < SDL_arraysize(sys_cursors); j++) { - X11_FreeCursor(sys_cursors[j]); - sys_cursors[j] = NULL; + if (sys_cursors[j]) { + X11_FreeCursor(sys_cursors[j]); + sys_cursors[j] = NULL; + } } for (i = data->mouse_device_info; i; i = next) { From f934b3e06685b50d427a5847f11af3246f553cf6 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Wed, 13 Aug 2025 19:42:49 -0700 Subject: [PATCH 55/61] x11: fixed creating a window when all displays are disconnected The X server maintains the desktop, but XRandR sends disconnect notifications for all displays. In this case fall back to the generic X11 desktop as a display. --- src/video/x11/SDL_x11modes.c | 188 +++++++++++++++++++++-------------- 1 file changed, 115 insertions(+), 73 deletions(-) diff --git a/src/video/x11/SDL_x11modes.c b/src/video/x11/SDL_x11modes.c index e8cd6d6193..cab71150c0 100644 --- a/src/video/x11/SDL_x11modes.c +++ b/src/video/x11/SDL_x11modes.c @@ -243,7 +243,96 @@ SDL_PixelFormat X11_GetPixelFormatFromVisualInfo(Display *display, XVisualInfo * return SDL_PIXELFORMAT_UNKNOWN; } +static SDL_DisplayID X11_AddGenericDisplay(SDL_VideoDevice *_this, bool send_event) +{ + // !!! FIXME: a lot of copy/paste from X11_InitModes_XRandR in this function. + SDL_VideoData *data = _this->internal; + Display *dpy = data->display; + const int default_screen = DefaultScreen(dpy); + Screen *screen = ScreenOfDisplay(dpy, default_screen); + int scanline_pad, n, i; + SDL_DisplayModeData *modedata; + SDL_DisplayData *displaydata; + SDL_DisplayMode mode; + XPixmapFormatValues *pixmapformats; + Uint32 pixelformat; + XVisualInfo vinfo; + SDL_VideoDisplay display; + + // note that generally even if you have a multiple physical monitors, ScreenCount(dpy) still only reports ONE screen. + + if (!get_visualinfo(dpy, default_screen, &vinfo)) { + return SDL_SetError("Failed to find an X11 visual for the primary display"); + } + + pixelformat = X11_GetPixelFormatFromVisualInfo(dpy, &vinfo); + if (SDL_ISPIXELFORMAT_INDEXED(pixelformat)) { + return SDL_SetError("Palettized video modes are no longer supported"); + } + + SDL_zero(mode); + mode.w = WidthOfScreen(screen); + mode.h = HeightOfScreen(screen); + mode.format = pixelformat; + + displaydata = (SDL_DisplayData *)SDL_calloc(1, sizeof(*displaydata)); + if (!displaydata) { + return false; + } + + modedata = (SDL_DisplayModeData *)SDL_calloc(1, sizeof(SDL_DisplayModeData)); + if (!modedata) { + SDL_free(displaydata); + return false; + } + mode.internal = modedata; + + displaydata->screen = default_screen; + displaydata->visual = vinfo.visual; + displaydata->depth = vinfo.depth; + + scanline_pad = SDL_BYTESPERPIXEL(pixelformat) * 8; + pixmapformats = X11_XListPixmapFormats(dpy, &n); + if (pixmapformats) { + for (i = 0; i < n; ++i) { + if (pixmapformats[i].depth == vinfo.depth) { + scanline_pad = pixmapformats[i].scanline_pad; + break; + } + } + X11_XFree(pixmapformats); + } + + displaydata->scanline_pad = scanline_pad; + displaydata->x = 0; + displaydata->y = 0; + displaydata->use_xrandr = false; + + SDL_zero(display); + display.name = (char *)"Generic X11 Display"; /* this is just copied and thrown away, it's safe to cast to char* here. */ + display.desktop_mode = mode; + display.internal = displaydata; + display.content_scale = X11_GetGlobalContentScale(_this); + return SDL_AddVideoDisplay(&display, send_event); +} + #ifdef SDL_VIDEO_DRIVER_X11_XRANDR + +static void X11_RemoveGenericDisplay(SDL_VideoDevice *_this) +{ + SDL_DisplayID *displays = SDL_GetDisplays(NULL); + if (displays) { + for (int i = 0; displays[i]; ++i) { + SDL_VideoDisplay *display = SDL_GetVideoDisplay(displays[i]); + const SDL_DisplayData *displaydata = display->internal; + if (!displaydata->xrandr_output) { + SDL_DelVideoDisplay(displays[i], true); + } + } + SDL_free(displays); + } +} + static bool CheckXRandR(Display *display, int *major, int *minor) { // Default the extension not available @@ -525,10 +614,17 @@ static bool X11_AddXRandRDisplay(SDL_VideoDevice *_this, Display *dpy, int scree return true; // failed to query data, skip this display } - if (SDL_AddVideoDisplay(&display, send_event) == 0) { + SDL_DisplayID displayID = SDL_AddVideoDisplay(&display, false); + if (displayID == 0) { return false; } + // We added an XRandR display, remove the generic display, if any + X11_RemoveGenericDisplay(_this); + + if (send_event) { + SDL_SendDisplayEvent(SDL_GetVideoDisplay(displayID), SDL_EVENT_DISPLAY_ADDED, 0, 0); + } return true; } @@ -592,7 +688,7 @@ static void X11_CheckDisplaysMoved(SDL_VideoDevice *_this, Display *dpy) for (int i = 0; displays[i]; ++i) { SDL_VideoDisplay *display = SDL_GetVideoDisplay(displays[i]); const SDL_DisplayData *displaydata = display->internal; - if (displaydata->screen == screen) { + if (displaydata->xrandr_output && displaydata->screen == screen) { X11_UpdateXRandRDisplay(_this, dpy, screen, displaydata->xrandr_output, res, display); } } @@ -638,8 +734,12 @@ static void X11_CheckDisplaysRemoved(SDL_VideoDevice *_this, Display *dpy) for (int i = 0; i < num_displays; ++i) { if (displays[i]) { - // This display wasn't in the XRandR list - SDL_DelVideoDisplay(displays[i], true); + SDL_VideoDisplay *display = SDL_GetVideoDisplay(displays[i]); + const SDL_DisplayData *displaydata = display->internal; + if (displaydata->xrandr_output) { + // This display wasn't in the XRandR list + SDL_DelVideoDisplay(displays[i], true); + } } } SDL_free(displays); @@ -673,7 +773,17 @@ static void X11_HandleXRandROutputChange(SDL_VideoDevice *_this, const XRROutput if (ev->connection == RR_Disconnected) { // output is going away if (display) { + // Add the generic display if we're about to remove the last XRandR display + SDL_DisplayID generic_display = 0; + if (_this->num_displays == 1) { + generic_display = X11_AddGenericDisplay(_this, false); + } + SDL_DelVideoDisplay(display->id, true); + + if (generic_display) { + SDL_SendDisplayEvent(SDL_GetVideoDisplay(generic_display), SDL_EVENT_DISPLAY_ADDED, 0, 0); + } } X11_CheckDisplaysMoved(_this, ev->display); @@ -813,75 +923,7 @@ static bool X11_InitModes_XRandR(SDL_VideoDevice *_this) enumerate the current displays and their current sizes. */ static bool X11_InitModes_StdXlib(SDL_VideoDevice *_this) { - // !!! FIXME: a lot of copy/paste from X11_InitModes_XRandR in this function. - SDL_VideoData *data = _this->internal; - Display *dpy = data->display; - const int default_screen = DefaultScreen(dpy); - Screen *screen = ScreenOfDisplay(dpy, default_screen); - int scanline_pad, n, i; - SDL_DisplayModeData *modedata; - SDL_DisplayData *displaydata; - SDL_DisplayMode mode; - XPixmapFormatValues *pixmapformats; - Uint32 pixelformat; - XVisualInfo vinfo; - SDL_VideoDisplay display; - - // note that generally even if you have a multiple physical monitors, ScreenCount(dpy) still only reports ONE screen. - - if (!get_visualinfo(dpy, default_screen, &vinfo)) { - return SDL_SetError("Failed to find an X11 visual for the primary display"); - } - - pixelformat = X11_GetPixelFormatFromVisualInfo(dpy, &vinfo); - if (SDL_ISPIXELFORMAT_INDEXED(pixelformat)) { - return SDL_SetError("Palettized video modes are no longer supported"); - } - - SDL_zero(mode); - mode.w = WidthOfScreen(screen); - mode.h = HeightOfScreen(screen); - mode.format = pixelformat; - - displaydata = (SDL_DisplayData *)SDL_calloc(1, sizeof(*displaydata)); - if (!displaydata) { - return false; - } - - modedata = (SDL_DisplayModeData *)SDL_calloc(1, sizeof(SDL_DisplayModeData)); - if (!modedata) { - SDL_free(displaydata); - return false; - } - mode.internal = modedata; - - displaydata->screen = default_screen; - displaydata->visual = vinfo.visual; - displaydata->depth = vinfo.depth; - - scanline_pad = SDL_BYTESPERPIXEL(pixelformat) * 8; - pixmapformats = X11_XListPixmapFormats(dpy, &n); - if (pixmapformats) { - for (i = 0; i < n; ++i) { - if (pixmapformats[i].depth == vinfo.depth) { - scanline_pad = pixmapformats[i].scanline_pad; - break; - } - } - X11_XFree(pixmapformats); - } - - displaydata->scanline_pad = scanline_pad; - displaydata->x = 0; - displaydata->y = 0; - displaydata->use_xrandr = false; - - SDL_zero(display); - display.name = (char *)"Generic X11 Display"; /* this is just copied and thrown away, it's safe to cast to char* here. */ - display.desktop_mode = mode; - display.internal = displaydata; - display.content_scale = X11_GetGlobalContentScale(_this); - if (SDL_AddVideoDisplay(&display, true) == 0) { + if (X11_AddGenericDisplay(_this, true) == 0) { return false; } return true; From f053be22bebfe9e6d052806abcbfd05e8e0d7621 Mon Sep 17 00:00:00 2001 From: "Joshua T. Fisher" Date: Thu, 14 Aug 2025 08:19:14 -0700 Subject: [PATCH 56/61] Improve CMake for IDE Projects (Visual Studio) (#13704) --- CMakeLists.txt | 574 ++++++++++++++++++++++++++++++++-------- cmake/sdlcommands.cmake | 17 ++ 2 files changed, 484 insertions(+), 107 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8c953ab88d..27864de8a6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1191,34 +1191,63 @@ endif() # General source files sdl_glob_sources( "${SDL3_SOURCE_DIR}/src/*.c" + "${SDL3_SOURCE_DIR}/src/*.h" "${SDL3_SOURCE_DIR}/src/atomic/*.c" + "${SDL3_SOURCE_DIR}/src/atomic/*.h" "${SDL3_SOURCE_DIR}/src/audio/*.c" + "${SDL3_SOURCE_DIR}/src/audio/*.h" "${SDL3_SOURCE_DIR}/src/camera/*.c" + "${SDL3_SOURCE_DIR}/src/camera/*.h" "${SDL3_SOURCE_DIR}/src/core/*.c" + "${SDL3_SOURCE_DIR}/src/core/*.h" "${SDL3_SOURCE_DIR}/src/cpuinfo/*.c" + "${SDL3_SOURCE_DIR}/src/cpuinfo/*.h" "${SDL3_SOURCE_DIR}/src/dynapi/*.c" + "${SDL3_SOURCE_DIR}/src/dynapi/*.h" "${SDL3_SOURCE_DIR}/src/events/*.c" + "${SDL3_SOURCE_DIR}/src/events/*.h" "${SDL3_SOURCE_DIR}/src/io/*.c" + "${SDL3_SOURCE_DIR}/src/io/*.h" "${SDL3_SOURCE_DIR}/src/io/generic/*.c" + "${SDL3_SOURCE_DIR}/src/io/generic/*.h" "${SDL3_SOURCE_DIR}/src/filesystem/*.c" + "${SDL3_SOURCE_DIR}/src/filesystem/*.h" "${SDL3_SOURCE_DIR}/src/gpu/*.c" + "${SDL3_SOURCE_DIR}/src/gpu/*.h" "${SDL3_SOURCE_DIR}/src/joystick/*.c" + "${SDL3_SOURCE_DIR}/src/joystick/*.h" "${SDL3_SOURCE_DIR}/src/haptic/*.c" + "${SDL3_SOURCE_DIR}/src/haptic/*.h" "${SDL3_SOURCE_DIR}/src/hidapi/*.c" + "${SDL3_SOURCE_DIR}/src/hidapi/*.h" "${SDL3_SOURCE_DIR}/src/locale/*.c" + "${SDL3_SOURCE_DIR}/src/locale/*.h" "${SDL3_SOURCE_DIR}/src/main/*.c" + "${SDL3_SOURCE_DIR}/src/main/*.h" "${SDL3_SOURCE_DIR}/src/misc/*.c" + "${SDL3_SOURCE_DIR}/src/misc/*.h" "${SDL3_SOURCE_DIR}/src/power/*.c" + "${SDL3_SOURCE_DIR}/src/power/*.h" "${SDL3_SOURCE_DIR}/src/render/*.c" + "${SDL3_SOURCE_DIR}/src/render/*.h" "${SDL3_SOURCE_DIR}/src/render/*/*.c" + "${SDL3_SOURCE_DIR}/src/render/*/*.h" "${SDL3_SOURCE_DIR}/src/sensor/*.c" + "${SDL3_SOURCE_DIR}/src/sensor/*.h" "${SDL3_SOURCE_DIR}/src/stdlib/*.c" + "${SDL3_SOURCE_DIR}/src/stdlib/*.h" "${SDL3_SOURCE_DIR}/src/storage/*.c" + "${SDL3_SOURCE_DIR}/src/storage/*.h" "${SDL3_SOURCE_DIR}/src/thread/*.c" + "${SDL3_SOURCE_DIR}/src/thread/*.h" "${SDL3_SOURCE_DIR}/src/time/*.c" + "${SDL3_SOURCE_DIR}/src/time/*.h" "${SDL3_SOURCE_DIR}/src/timer/*.c" + "${SDL3_SOURCE_DIR}/src/timer/*.h" "${SDL3_SOURCE_DIR}/src/video/*.c" + "${SDL3_SOURCE_DIR}/src/video/*.h" "${SDL3_SOURCE_DIR}/src/video/yuv2rgb/*.c" + "${SDL3_SOURCE_DIR}/src/video/yuv2rgb/*.h" ) # Build uclibc as a static library such that non-used symbols don't end up in the SDL3 shared library. @@ -1258,13 +1287,19 @@ if(SDL_AUDIO) # CheckDummyAudio/CheckDiskAudio - valid for all platforms if(SDL_DUMMYAUDIO) set(SDL_AUDIO_DRIVER_DUMMY 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/audio/dummy/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/audio/dummy/*.c" + "${SDL3_SOURCE_DIR}/src/audio/dummy/*.h" + ) set(HAVE_DUMMYAUDIO TRUE) set(HAVE_SDL_AUDIO TRUE) endif() if(SDL_DISKAUDIO) set(SDL_AUDIO_DRIVER_DISK 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/audio/disk/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/audio/disk/*.c" + "${SDL3_SOURCE_DIR}/src/audio/disk/*.h" + ) set(HAVE_DISKAUDIO TRUE) set(HAVE_SDL_AUDIO TRUE) endif() @@ -1274,7 +1309,10 @@ if(SDL_CAMERA) # CheckDummyCamera/CheckDiskCamera - valid for all platforms if(SDL_DUMMYCAMERA) set(SDL_CAMERA_DRIVER_DUMMY 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/camera/dummy/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/camera/dummy/*.c" + "${SDL3_SOURCE_DIR}/src/camera/dummy/*.h" + ) set(HAVE_DUMMYCAMERA TRUE) set(HAVE_SDL_CAMERA TRUE) endif() @@ -1293,7 +1331,10 @@ if(UNIX OR APPLE) CheckDLOPEN() if(HAVE_DLOPEN) set(SDL_LOADSO_DLOPEN 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/loadso/dlopen/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/loadso/dlopen/*.c" + "${SDL3_SOURCE_DIR}/src/loadso/dlopen/*.h" + ) set(HAVE_SDL_LOADSO TRUE) endif() endif() @@ -1306,14 +1347,20 @@ if(SDL_JOYSTICK) if(SDL_VIRTUAL_JOYSTICK) set(HAVE_VIRTUAL_JOYSTICK TRUE) set(SDL_JOYSTICK_VIRTUAL 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/joystick/virtual/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/joystick/virtual/*.c" + "${SDL3_SOURCE_DIR}/src/joystick/virtual/*.h" + ) endif() endif() if(SDL_VIDEO) if(SDL_DUMMYVIDEO) set(SDL_VIDEO_DRIVER_DUMMY 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/video/dummy/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/video/dummy/*.c" + "${SDL3_SOURCE_DIR}/src/video/dummy/*.h" + ) set(HAVE_DUMMYVIDEO TRUE) set(HAVE_SDL_VIDEO TRUE) endif() @@ -1323,11 +1370,17 @@ endif() if(ANDROID) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake/android") - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/core/android/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/core/android/*.c" + "${SDL3_SOURCE_DIR}/src/core/android/*.h" + ) sdl_sources("${CMAKE_ANDROID_NDK}/sources/android/cpufeatures/cpu-features.c") set_property(SOURCE "${CMAKE_ANDROID_NDK}/sources/android/cpufeatures/cpu-features.c" APPEND_STRING PROPERTY COMPILE_FLAGS " -Wno-declaration-after-statement") - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/misc/android/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/misc/android/*.c" + "${SDL3_SOURCE_DIR}/src/misc/android/*.h" + ) set(HAVE_SDL_MISC TRUE) # SDL_spinlock.c Needs to be compiled in ARM mode. @@ -1345,18 +1398,27 @@ if(ANDROID) if(SDL_AUDIO) set(SDL_AUDIO_DRIVER_OPENSLES 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/audio/openslES/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/audio/openslES/*.c" + "${SDL3_SOURCE_DIR}/src/audio/openslES/*.h" + ) sdl_link_dependency(opensles LIBS ${ANDROID_DL_LIBRARY} OpenSLES) set(SDL_AUDIO_DRIVER_AAUDIO 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/audio/aaudio/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/audio/aaudio/*.c" + "${SDL3_SOURCE_DIR}/src/audio/aaudio/*.h" + ) set(HAVE_SDL_AUDIO TRUE) endif() set(SDL_FILESYSTEM_ANDROID 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/filesystem/android/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/filesystem/android/*.c" + "${SDL3_SOURCE_DIR}/src/filesystem/android/*.h" + ) set(HAVE_SDL_FILESYSTEM TRUE) set(SDL_FSOPS_POSIX 1) # !!! FIXME: this might need something else for .apk data? @@ -1365,7 +1427,10 @@ if(ANDROID) if(SDL_HAPTIC) set(SDL_HAPTIC_ANDROID 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/haptic/android/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/haptic/android/*.c" + "${SDL3_SOURCE_DIR}/src/haptic/android/*.h" + ) set(HAVE_SDL_HAPTIC TRUE) endif() @@ -1375,46 +1440,71 @@ if(ANDROID) set(SDL_JOYSTICK_ANDROID 1) sdl_glob_sources( "${SDL3_SOURCE_DIR}/src/joystick/android/*.c" + "${SDL3_SOURCE_DIR}/src/joystick/android/*.h" ) set(HAVE_SDL_JOYSTICK TRUE) endif() set(SDL_LOADSO_DLOPEN 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/loadso/dlopen/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/loadso/dlopen/*.c" + "${SDL3_SOURCE_DIR}/src/loadso/dlopen/*.h" + ) set(HAVE_SDL_LOADSO TRUE) if(SDL_POWER) set(SDL_POWER_ANDROID 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/power/android/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/power/android/*.c" + "${SDL3_SOURCE_DIR}/src/power/android/*.h" + ) set(HAVE_SDL_POWER TRUE) endif() - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/locale/android/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/locale/android/*.c" + "${SDL3_SOURCE_DIR}/src/locale/android/*.h" + ) set(HAVE_SDL_LOCALE TRUE) set(SDL_TIME_UNIX 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/time/unix/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/time/unix/*.c" + "${SDL3_SOURCE_DIR}/src/time/unix/*.h" + ) set(HAVE_SDL_TIME TRUE) set(SDL_TIMER_UNIX 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/timer/unix/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/timer/unix/*.c" + "${SDL3_SOURCE_DIR}/src/timer/unix/*.h" + ) set(HAVE_SDL_TIMERS TRUE) if(SDL_SENSOR) set(SDL_SENSOR_ANDROID 1) set(HAVE_SDL_SENSORS TRUE) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/sensor/android/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/sensor/android/*.c" + "${SDL3_SOURCE_DIR}/src/sensor/android/*.h" + ) endif() if(SDL_CAMERA) set(SDL_CAMERA_DRIVER_ANDROID 1) set(HAVE_CAMERA TRUE) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/camera/android/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/camera/android/*.c" + "${SDL3_SOURCE_DIR}/src/camera/android/*.h" + ) endif() if(SDL_VIDEO) set(SDL_VIDEO_DRIVER_ANDROID 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/video/android/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/video/android/*.c" + "${SDL3_SOURCE_DIR}/src/video/android/*.h" + ) set(HAVE_SDL_VIDEO TRUE) # Core stuff @@ -1517,20 +1607,32 @@ elseif(EMSCRIPTEN) # project. Uncomment at will for verbose cross-compiling -I/../ path info. sdl_compile_options(PRIVATE "-Wno-warn-absolute-paths") - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/main/emscripten/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/main/emscripten/*.c" + "${SDL3_SOURCE_DIR}/src/main/emscripten/*.h" + ) set(HAVE_SDL_MAIN_CALLBACKS TRUE) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/misc/emscripten/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/misc/emscripten/*.c" + "${SDL3_SOURCE_DIR}/src/misc/emscripten/*.h" + ) set(HAVE_SDL_MISC TRUE) if(SDL_AUDIO) set(SDL_AUDIO_DRIVER_EMSCRIPTEN 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/audio/emscripten/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/audio/emscripten/*.c" + "${SDL3_SOURCE_DIR}/src/audio/emscripten/*.h" + ) set(HAVE_SDL_AUDIO TRUE) endif() set(SDL_FILESYSTEM_EMSCRIPTEN 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/filesystem/emscripten/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/filesystem/emscripten/*.c" + "${SDL3_SOURCE_DIR}/src/filesystem/emscripten/*.h" + ) set(HAVE_SDL_FILESYSTEM TRUE) set(SDL_FSOPS_POSIX 1) @@ -1540,30 +1642,48 @@ elseif(EMSCRIPTEN) if(SDL_CAMERA) set(SDL_CAMERA_DRIVER_EMSCRIPTEN 1) set(HAVE_CAMERA TRUE) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/camera/emscripten/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/camera/emscripten/*.c" + "${SDL3_SOURCE_DIR}/src/camera/emscripten/*.h" + ) endif() if(SDL_JOYSTICK) set(SDL_JOYSTICK_EMSCRIPTEN 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/joystick/emscripten/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/joystick/emscripten/*.c" + "${SDL3_SOURCE_DIR}/src/joystick/emscripten/*.h" + ) set(HAVE_SDL_JOYSTICK TRUE) endif() if(SDL_POWER) set(SDL_POWER_EMSCRIPTEN 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/power/emscripten/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/power/emscripten/*.c" + "${SDL3_SOURCE_DIR}/src/power/emscripten/*.h" + ) set(HAVE_SDL_POWER TRUE) endif() - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/locale/emscripten/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/locale/emscripten/*.c" + "${SDL3_SOURCE_DIR}/src/locale/emscripten/*.h" + ) set(HAVE_SDL_LOCALE TRUE) set(SDL_TIME_UNIX 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/time/unix/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/time/unix/*.c" + "${SDL3_SOURCE_DIR}/src/time/unix/*.h" + ) set(HAVE_SDL_TIME TRUE) set(SDL_TIMER_UNIX 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/timer/unix/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/timer/unix/*.c" + "${SDL3_SOURCE_DIR}/src/timer/unix/*.h" + ) set(HAVE_SDL_TIMERS TRUE) if(SDL_CLOCK_GETTIME) @@ -1572,7 +1692,10 @@ elseif(EMSCRIPTEN) if(SDL_VIDEO) set(SDL_VIDEO_DRIVER_EMSCRIPTEN 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/video/emscripten/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/video/emscripten/*.c" + "${SDL3_SOURCE_DIR}/src/video/emscripten/*.h" + ) set(HAVE_SDL_VIDEO TRUE) #enable gles @@ -1590,11 +1713,17 @@ elseif(UNIX AND NOT APPLE AND NOT RISCOS AND NOT HAIKU) if(SDL_AUDIO) if(NETBSD) set(SDL_AUDIO_DRIVER_NETBSD 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/audio/netbsd/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/audio/netbsd/*.c" + "${SDL3_SOURCE_DIR}/src/audio/netbsd/*.h" + ) set(HAVE_SDL_AUDIO TRUE) elseif(QNX) set(SDL_AUDIO_DRIVER_QNX 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/audio/qnx/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/audio/qnx/*.c" + "${SDL3_SOURCE_DIR}/src/audio/qnx/*.h" + ) sdl_link_dependency(asound LIBS asound) set(HAVE_SDL_AUDIO TRUE) endif() @@ -1624,12 +1753,18 @@ elseif(UNIX AND NOT APPLE AND NOT RISCOS AND NOT HAIKU) CheckVulkan() CheckQNXScreen() - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/tray/unix/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/tray/unix/*.c" + "${SDL3_SOURCE_DIR}/src/tray/unix/*.h" + ) set(HAVE_SDL_TRAY TRUE) endif() if(UNIX) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/core/unix/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/core/unix/*.c" + "${SDL3_SOURCE_DIR}/src/core/unix/*.h" + ) check_c_source_compiles(" #include @@ -1678,7 +1813,10 @@ elseif(UNIX AND NOT APPLE AND NOT RISCOS AND NOT HAIKU) if(SDL_CAMERA AND HAVE_LINUX_VIDEODEV2_H) set(SDL_CAMERA_DRIVER_V4L2 1) set(HAVE_CAMERA TRUE) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/camera/v4l2/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/camera/v4l2/*.c" + "${SDL3_SOURCE_DIR}/src/camera/v4l2/*.h" + ) endif() if(HAVE_LINUX_INPUT_H) @@ -1687,7 +1825,10 @@ elseif(UNIX AND NOT APPLE AND NOT RISCOS AND NOT HAIKU) if(SDL_HAPTIC AND HAVE_LINUX_INPUT_H) set(SDL_HAPTIC_LINUX 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/haptic/linux/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/haptic/linux/*.c" + "${SDL3_SOURCE_DIR}/src/haptic/linux/*.h" + ) set(HAVE_SDL_HAPTIC TRUE) endif() @@ -1763,25 +1904,40 @@ elseif(UNIX AND NOT APPLE AND NOT RISCOS AND NOT HAIKU) if(HAVE_DBUS_DBUS_H) sdl_sources( "${SDL3_SOURCE_DIR}/src/core/linux/SDL_dbus.c" + "${SDL3_SOURCE_DIR}/src/core/linux/SDL_dbus.h" "${SDL3_SOURCE_DIR}/src/core/linux/SDL_system_theme.c" + "${SDL3_SOURCE_DIR}/src/core/linux/SDL_system_theme.h" "${SDL3_SOURCE_DIR}/src/core/linux/SDL_progressbar.c" + "${SDL3_SOURCE_DIR}/src/core/linux/SDL_progressbar.h" ) endif() if(SDL_USE_IME) - sdl_sources("${SDL3_SOURCE_DIR}/src/core/linux/SDL_ime.c") + sdl_sources( + "${SDL3_SOURCE_DIR}/src/core/linux/SDL_ime.c" + "${SDL3_SOURCE_DIR}/src/core/linux/SDL_ime.h" + ) endif() if(HAVE_IBUS_IBUS_H) - sdl_sources("${SDL3_SOURCE_DIR}/src/core/linux/SDL_ibus.c") + sdl_sources( + "${SDL3_SOURCE_DIR}/src/core/linux/SDL_ibus.c" + "${SDL3_SOURCE_DIR}/src/core/linux/SDL_ibus.h" + ) endif() if(HAVE_FCITX) - sdl_sources("${SDL3_SOURCE_DIR}/src/core/linux/SDL_fcitx.c") + sdl_sources( + "${SDL3_SOURCE_DIR}/src/core/linux/SDL_fcitx.c" + "${SDL3_SOURCE_DIR}/src/core/linux/SDL_fcitx.h" + ) endif() if(HAVE_LIBUDEV_H) - sdl_sources("${SDL3_SOURCE_DIR}/src/core/linux/SDL_udev.c") + sdl_sources( + "${SDL3_SOURCE_DIR}/src/core/linux/SDL_udev.c" + "${SDL3_SOURCE_DIR}/src/core/linux/SDL_udev.h" + ) endif() if(HAVE_LINUX_INPUT_H) @@ -1792,13 +1948,17 @@ elseif(UNIX AND NOT APPLE AND NOT RISCOS AND NOT HAIKU) endif() if(HAVE_INPUT_KBIO) - sdl_sources("${SDL3_SOURCE_DIR}/src/core/freebsd/SDL_evdev_kbd_freebsd.c") + sdl_sources( + "${SDL3_SOURCE_DIR}/src/core/freebsd/SDL_evdev_kbd_freebsd.c" + "${SDL3_SOURCE_DIR}/src/core/freebsd/SDL_evdev_kbd_default_keyaccmap.h" + ) endif() if(HAVE_INPUT_WSCONS) sdl_sources( "${SDL3_SOURCE_DIR}/src/core/openbsd/SDL_wscons_kbd.c" "${SDL3_SOURCE_DIR}/src/core/openbsd/SDL_wscons_mouse.c" + "${SDL3_SOURCE_DIR}/src/core/openbsd/SDL_wscons.h" ) endif() @@ -1819,6 +1979,7 @@ elseif(UNIX AND NOT APPLE AND NOT RISCOS AND NOT HAIKU) # Always compiled for Linux, unconditionally: sdl_sources( "${SDL3_SOURCE_DIR}/src/core/linux/SDL_evdev_capabilities.c" + "${SDL3_SOURCE_DIR}/src/core/linux/SDL_evdev_capabilities.h" "${SDL3_SOURCE_DIR}/src/core/linux/SDL_threadprio.c" ) @@ -1837,6 +1998,7 @@ elseif(UNIX AND NOT APPLE AND NOT RISCOS AND NOT HAIKU) set(SDL_JOYSTICK_LINUX 1) sdl_glob_sources( "${SDL3_SOURCE_DIR}/src/joystick/linux/*.c" + "${SDL3_SOURCE_DIR}/src/joystick/linux/*.h" ) set(HAVE_SDL_JOYSTICK TRUE) endif() @@ -1879,7 +2041,10 @@ elseif(UNIX AND NOT APPLE AND NOT RISCOS AND NOT HAIKU) sdl_glob_sources("${SDL3_SOURCE_DIR}/src/storage/generic/*.c") if(LINUX) set(SDL_STORAGE_STEAM 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/storage/steam/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/storage/steam/*.c" + "${SDL3_SOURCE_DIR}/src/storage/steam/*.h" + ) endif() set(HAVE_SDL_STORAGE 1) @@ -1927,8 +2092,11 @@ elseif(WINDOWS) #include int main(int argc, char **argv) { return 0; }" HAVE_WIN32_CC) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/core/windows/*.c") - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/core/windows/*.cpp") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/core/windows/*.c" + "${SDL3_SOURCE_DIR}/src/core/windows/*.cpp" + "${SDL3_SOURCE_DIR}/src/core/windows/*.h" + ) sdl_glob_sources("${SDL3_SOURCE_DIR}/src/main/windows/*.c") sdl_glob_sources("${SDL3_SOURCE_DIR}/src/io/windows/*.c") @@ -2023,22 +2191,31 @@ elseif(WINDOWS) if(SDL_AUDIO) if(HAVE_DSOUND_H) set(SDL_AUDIO_DRIVER_DSOUND 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/audio/directsound/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/audio/directsound/*.c" + "${SDL3_SOURCE_DIR}/src/audio/directsound/*.h" + ) set(HAVE_SDL_AUDIO TRUE) endif() if(SDL_WASAPI AND HAVE_AUDIOCLIENT_H AND HAVE_MMDEVICEAPI_H) set(SDL_AUDIO_DRIVER_WASAPI 1) set(HAVE_WASAPI TRUE) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/audio/wasapi/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/audio/wasapi/*.c" + "${SDL3_SOURCE_DIR}/src/audio/wasapi/*.h" + ) set(HAVE_SDL_AUDIO TRUE) endif() endif() if(SDL_VIDEO) set(SDL_VIDEO_DRIVER_WINDOWS 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/video/windows/*.c") - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/video/windows/*.cpp") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/video/windows/*.c" + "${SDL3_SOURCE_DIR}/src/video/windows/*.cpp" + "${SDL3_SOURCE_DIR}/src/video/windows/*.h" + ) CheckOpenVR() @@ -2062,7 +2239,9 @@ elseif(WINDOWS) set(SDL_THREAD_WINDOWS 1) sdl_sources( "${SDL3_SOURCE_DIR}/src/thread/generic/SDL_syscond.c" + "${SDL3_SOURCE_DIR}/src/thread/generic/SDL_syscond_c.h" "${SDL3_SOURCE_DIR}/src/thread/generic/SDL_sysrwlock.c" + "${SDL3_SOURCE_DIR}/src/thread/generic/SDL_sysrwlock_c.h" "${SDL3_SOURCE_DIR}/src/thread/windows/SDL_syscond_cv.c" "${SDL3_SOURCE_DIR}/src/thread/windows/SDL_sysmutex.c" "${SDL3_SOURCE_DIR}/src/thread/windows/SDL_sysrwlock_srw.c" @@ -2076,7 +2255,10 @@ elseif(WINDOWS) if(SDL_SENSOR AND HAVE_SENSORSAPI_H) set(SDL_SENSOR_WINDOWS 1) set(HAVE_SDL_SENSORS TRUE) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/sensor/windows/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/sensor/windows/*.c" + "${SDL3_SOURCE_DIR}/src/sensor/windows/*.h" + ) endif() if(SDL_POWER) @@ -2098,7 +2280,10 @@ elseif(WINDOWS) set(SDL_STORAGE_GENERIC 1) sdl_glob_sources("${SDL3_SOURCE_DIR}/src/storage/generic/*.c") set(SDL_STORAGE_STEAM 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/storage/steam/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/storage/steam/*.c" + "${SDL3_SOURCE_DIR}/src/storage/steam/*.h" + ) set(HAVE_SDL_STORAGE 1) # Libraries for Win32 native and MinGW @@ -2116,7 +2301,10 @@ elseif(WINDOWS) sdl_glob_sources("${SDL3_SOURCE_DIR}/src/loadso/windows/*.c") set(HAVE_SDL_LOADSO TRUE) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/core/windows/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/core/windows/*.c" + "${SDL3_SOURCE_DIR}/src/core/windows/*.h" + ) if(SDL_VIDEO) if(SDL_OPENGL) @@ -2151,7 +2339,10 @@ elseif(WINDOWS) endif() if(SDL_JOYSTICK) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/joystick/windows/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/joystick/windows/*.c" + "${SDL3_SOURCE_DIR}/src/joystick/windows/*.h" + ) set(SDL_JOYSTICK_RAWINPUT 1) if(HAVE_DINPUT_H) @@ -2173,7 +2364,10 @@ elseif(WINDOWS) if(SDL_HAPTIC) if(HAVE_DINPUT_H) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/haptic/windows/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/haptic/windows/*.c" + "${SDL3_SOURCE_DIR}/src/haptic/windows/*.h" + ) set(SDL_HAPTIC_DINPUT 1) set(HAVE_SDL_HAPTIC TRUE) endif() @@ -2235,7 +2429,10 @@ elseif(APPLE) if(SDL_AUDIO) set(SDL_AUDIO_DRIVER_COREAUDIO 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/audio/coreaudio/*.m") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/audio/coreaudio/*.m" + "${SDL3_SOURCE_DIR}/src/audio/coreaudio/*.h" + ) set(HAVE_SDL_AUDIO TRUE) set(SDL_FRAMEWORK_COREAUDIO 1) set(SDL_FRAMEWORK_AUDIOTOOLBOX 1) @@ -2247,7 +2444,10 @@ elseif(APPLE) endif() if(SDL_JOYSTICK) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/joystick/apple/*.m") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/joystick/apple/*.m" + "${SDL3_SOURCE_DIR}/src/joystick/apple/*.h" + ) if(IOS OR TVOS OR VISIONOS OR WATCHOS) set(SDL_JOYSTICK_MFI 1) if(IOS OR VISIONOS OR WATCHOS) @@ -2256,7 +2456,10 @@ elseif(APPLE) set(SDL_FRAMEWORK_GAMECONTROLLER 1) set(SDL_FRAMEWORK_COREHAPTICS 1) else() - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/joystick/darwin/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/joystick/darwin/*.c" + "${SDL3_SOURCE_DIR}/src/joystick/darwin/*.h" + ) set_property(SOURCE ${MFI_JOYSTICK_SOURCES} APPEND_STRING PROPERTY COMPILE_FLAGS " -fobjc-weak") check_objc_source_compiles(" #include @@ -2296,7 +2499,10 @@ elseif(APPLE) sdl_glob_sources("${SDL3_SOURCE_DIR}/src/haptic/dummy/*.c") set(SDL_HAPTIC_DUMMY 1) else() - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/haptic/darwin/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/haptic/darwin/*.c" + "${SDL3_SOURCE_DIR}/src/haptic/darwin/*.h" + ) set(SDL_HAPTIC_IOKIT 1) set(SDL_FRAMEWORK_IOKIT 1) set(SDL_FRAMEWORK_FF 1) @@ -2306,7 +2512,10 @@ elseif(APPLE) if(SDL_POWER) if (IOS OR TVOS OR VISIONOS OR WATCHOS) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/power/uikit/*.m") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/power/uikit/*.m" + "${SDL3_SOURCE_DIR}/src/power/uikit/*.h" + ) set(SDL_POWER_UIKIT 1) else() sdl_glob_sources("${SDL3_SOURCE_DIR}/src/power/macos/*.c") @@ -2336,7 +2545,10 @@ elseif(APPLE) sdl_glob_sources("${SDL3_SOURCE_DIR}/src/storage/generic/*.c") if(MACOS) set(SDL_STORAGE_STEAM 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/storage/steam/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/storage/steam/*.c" + "${SDL3_SOURCE_DIR}/src/storage/steam/*.h" + ) endif() set(HAVE_SDL_STORAGE 1) @@ -2348,7 +2560,10 @@ elseif(APPLE) if(IOS OR VISIONOS OR WATCHOS) set(SDL_SENSOR_COREMOTION 1) set(HAVE_SDL_SENSORS TRUE) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/sensor/coremotion/*.m") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/sensor/coremotion/*.m" + "${SDL3_SOURCE_DIR}/src/sensor/coremotion/*.h" + ) endif() endif() @@ -2361,7 +2576,10 @@ elseif(APPLE) set(SDL_FRAMEWORK_UIKIT 1) set(SDL_IPHONE_KEYBOARD 1) set(SDL_IPHONE_LAUNCHSCREEN 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/video/uikit/*.m") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/video/uikit/*.m" + "${SDL3_SOURCE_DIR}/src/video/uikit/*.h" + ) set(HAVE_SDL_VIDEO TRUE) else() CheckCOCOA() @@ -2411,13 +2629,19 @@ elseif(APPLE) set(HAVE_METAL TRUE) endif() if(SDL_RENDER_METAL) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/render/metal/*.m") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/render/metal/*.m" + "${SDL3_SOURCE_DIR}/src/render/metal/*.h" + ) set(SDL_VIDEO_RENDER_METAL 1) set(HAVE_RENDER_METAL TRUE) endif() if (SDL_GPU) set(SDL_GPU_METAL 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/gpu/metal/*.m") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/gpu/metal/*.m" + "${SDL3_SOURCE_DIR}/src/gpu/metal/*.h" + ) endif() endif() endif() @@ -2511,7 +2735,10 @@ elseif(HAIKU) enable_language(CXX) if(SDL_AUDIO) set(SDL_AUDIO_DRIVER_HAIKU 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/audio/haiku/*.cc") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/audio/haiku/*.cc" + "${SDL3_SOURCE_DIR}/src/audio/haiku/*.h" + ) set(HAVE_SDL_AUDIO TRUE) endif() @@ -2526,7 +2753,10 @@ elseif(HAIKU) if(SDL_VIDEO) set(SDL_VIDEO_DRIVER_HAIKU 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/video/haiku/*.cc") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/video/haiku/*.cc" + "${SDL3_SOURCE_DIR}/src/video/haiku/*.h" + ) set(HAVE_SDL_VIDEO TRUE) if(SDL_OPENGL) @@ -2564,7 +2794,10 @@ elseif(HAIKU) sdl_glob_sources("${SDL3_SOURCE_DIR}/src/locale/haiku/*.cc") set(HAVE_SDL_LOCALE TRUE) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/core/haiku/*.cc") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/core/haiku/*.cc" + "${SDL3_SOURCE_DIR}/src/core/haiku/*.h" + ) CheckPTHREAD() sdl_link_dependency(base LIBS root be media game device textencoding tracker) @@ -2575,7 +2808,10 @@ elseif(RISCOS) if(SDL_VIDEO) set(SDL_VIDEO_DRIVER_RISCOS 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/video/riscos/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/video/riscos/*.c" + "${SDL3_SOURCE_DIR}/src/video/riscos/*.h" + ) set(HAVE_SDL_VIDEO TRUE) endif() @@ -2615,12 +2851,17 @@ elseif(VITA) set_property(SOURCE "${SDL3_SOURCE_DIR}/src/atomic/SDL_spinlock.c" APPEND_STRING PROPERTY COMPILE_FLAGS " -marm") endif() - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/misc/vita/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/misc/vita/*.c" + ) set(HAVE_SDL_MISC TRUE) if(SDL_AUDIO) set(SDL_AUDIO_DRIVER_VITA 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/audio/vita/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/audio/vita/*.c" + "${SDL3_SOURCE_DIR}/src/audio/vita/*.h" + ) set(HAVE_SDL_AUDIO TRUE) endif() @@ -2647,10 +2888,14 @@ elseif(VITA) set(SDL_THREAD_VITA 1) sdl_sources( "${SDL3_SOURCE_DIR}/src/thread/vita/SDL_sysmutex.c" + "${SDL3_SOURCE_DIR}/src/thread/vita/SDL_sysmutex_c.h" "${SDL3_SOURCE_DIR}/src/thread/vita/SDL_syssem.c" "${SDL3_SOURCE_DIR}/src/thread/vita/SDL_systhread.c" + "${SDL3_SOURCE_DIR}/src/thread/vita/SDL_systhread_c.h" "${SDL3_SOURCE_DIR}/src/thread/generic/SDL_syscond.c" + "${SDL3_SOURCE_DIR}/src/thread/generic/SDL_syscond_c.h" "${SDL3_SOURCE_DIR}/src/thread/generic/SDL_sysrwlock.c" + "${SDL3_SOURCE_DIR}/src/thread/generic/SDL_sysrwlock_c.h" "${SDL3_SOURCE_DIR}/src/thread/generic/SDL_systls.c" ) set(HAVE_SDL_THREADS TRUE) @@ -2669,7 +2914,10 @@ elseif(VITA) if(SDL_SENSOR) set(SDL_SENSOR_VITA 1) set(HAVE_SDL_SENSORS TRUE) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/sensor/vita/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/sensor/vita/*.c" + "${SDL3_SOURCE_DIR}/src/sensor/vita/*.h" + ) endif() if(SDL_CAMERA) @@ -2680,7 +2928,10 @@ elseif(VITA) if(SDL_VIDEO) set(SDL_VIDEO_DRIVER_VITA 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/video/vita/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/video/vita/*.c" + "${SDL3_SOURCE_DIR}/src/video/vita/*.h" + ) set(HAVE_SDL_VIDEO TRUE) if(VIDEO_VITA_PIB) @@ -2769,7 +3020,10 @@ elseif(PSP) if(SDL_AUDIO) set(SDL_AUDIO_DRIVER_PSP 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/audio/psp/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/audio/psp/*.c" + "${SDL3_SOURCE_DIR}/src/audio/psp/*.h" + ) set(HAVE_SDL_AUDIO TRUE) endif() @@ -2796,9 +3050,12 @@ elseif(PSP) set(SDL_THREAD_PSP 1) sdl_glob_sources( "${SDL3_SOURCE_DIR}/src/thread/generic/SDL_syscond.c" + "${SDL3_SOURCE_DIR}/src/thread/generic/SDL_syscond_c.h" "${SDL3_SOURCE_DIR}/src/thread/generic/SDL_systls.c" "${SDL3_SOURCE_DIR}/src/thread/generic/SDL_sysrwlock.c" + "${SDL3_SOURCE_DIR}/src/thread/generic/SDL_sysrwlock_c.h" "${SDL3_SOURCE_DIR}/src/thread/psp/*.c" + "${SDL3_SOURCE_DIR}/src/thread/psp/*.h" ) set(HAVE_SDL_THREADS TRUE) @@ -2816,7 +3073,10 @@ elseif(PSP) if(SDL_VIDEO) set(SDL_VIDEO_DRIVER_PSP 1) set(SDL_VIDEO_RENDER_PSP 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/video/psp/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/video/psp/*.c" + "${SDL3_SOURCE_DIR}/src/video/psp/*.h" + ) set(SDL_VIDEO_OPENGL 1) set(HAVE_SDL_VIDEO TRUE) endif() @@ -2840,7 +3100,10 @@ elseif(PS2) if(SDL_AUDIO) set(SDL_AUDIO_DRIVER_PS2 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/audio/ps2/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/audio/ps2/*.c" + "${SDL3_SOURCE_DIR}/src/audio/ps2/*.h" + ) set(HAVE_SDL_AUDIO TRUE) endif() @@ -2861,10 +3124,13 @@ elseif(PS2) set(SDL_THREAD_PS2 1) sdl_glob_sources( "${SDL3_SOURCE_DIR}/src/thread/generic/SDL_syscond.c" + "${SDL3_SOURCE_DIR}/src/thread/generic/SDL_syscond_c.h" "${SDL3_SOURCE_DIR}/src/thread/generic/SDL_sysmutex.c" "${SDL3_SOURCE_DIR}/src/thread/generic/SDL_sysrwlock.c" + "${SDL3_SOURCE_DIR}/src/thread/generic/SDL_sysrwlock_c.h" "${SDL3_SOURCE_DIR}/src/thread/generic/SDL_systls.c" "${SDL3_SOURCE_DIR}/src/thread/ps2/*.c" + "${SDL3_SOURCE_DIR}/src/thread/ps2/*.h" ) set(HAVE_SDL_THREADS TRUE) @@ -2899,7 +3165,10 @@ elseif(N3DS) if(SDL_AUDIO) set(SDL_AUDIO_DRIVER_N3DS 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/audio/n3ds/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/audio/n3ds/*.c" + "${SDL3_SOURCE_DIR}/src/audio/n3ds/*.h" + ) set(HAVE_SDL_AUDIO TRUE) endif() @@ -2922,11 +3191,16 @@ elseif(N3DS) endif() set(SDL_THREAD_N3DS 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/thread/n3ds/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/thread/n3ds/*.c" + "${SDL3_SOURCE_DIR}/src/thread/n3ds/*.h" + ) sdl_sources( "${SDL3_SOURCE_DIR}/src/thread/generic/SDL_syscond.c" + "${SDL3_SOURCE_DIR}/src/thread/generic/SDL_syscond_c.h" "${SDL3_SOURCE_DIR}/src/thread/generic/SDL_systls.c" "${SDL3_SOURCE_DIR}/src/thread/generic/SDL_sysrwlock.c" + "${SDL3_SOURCE_DIR}/src/thread/generic/SDL_sysrwlock_c.h" ) set(HAVE_SDL_THREADS TRUE) @@ -2950,29 +3224,45 @@ elseif(N3DS) if(SDL_VIDEO) set(SDL_VIDEO_DRIVER_N3DS 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/video/n3ds/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/video/n3ds/*.c" + "${SDL3_SOURCE_DIR}/src/video/n3ds/*.h" + ) set(HAVE_SDL_VIDEO TRUE) endif() sdl_glob_sources("${SDL3_SOURCE_DIR}/src/locale/n3ds/*.c") set(HAVE_SDL_LOCALE TRUE) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/io/n3ds/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/io/n3ds/*.c" + "${SDL3_SOURCE_DIR}/src/io/n3ds/*.h" + ) elseif(NGAGE) enable_language(CXX) set(SDL_MAIN_USE_CALLBACKS 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/main/ngage/*.c") - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/main/ngage/*.cpp") - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/core/ngage/*.cpp") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/main/ngage/*.c" + "${SDL3_SOURCE_DIR}/src/main/ngage/*.cpp" + "${SDL3_SOURCE_DIR}/src/main/ngage/*.hpp" + ) + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/core/ngage/*.cpp" + "${SDL3_SOURCE_DIR}/src/core/ngage/*.h" + ) set(HAVE_SDL_MAIN_CALLBACKS TRUE) if(SDL_AUDIO) set(SDL_AUDIO_DRIVER_NGAGE 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/audio/ngage/*.c") - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/audio/ngage/*.cpp") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/audio/ngage/*.c" + "${SDL3_SOURCE_DIR}/src/audio/ngage/*.cpp" + "${SDL3_SOURCE_DIR}/src/audio/ngage/*.h" + "${SDL3_SOURCE_DIR}/src/audio/ngage/*.hpp" + ) set(HAVE_SDL_AUDIO TRUE) endif() @@ -2986,13 +3276,20 @@ elseif(NGAGE) if(SDL_RENDER) set(SDL_VIDEO_RENDER_NGAGE 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/render/ngage/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/render/ngage/*.c" + "${SDL3_SOURCE_DIR}/src/render/ngage/*.h" + "${SDL3_SOURCE_DIR}/src/render/ngage/*.hpp" + ) endif() sdl_glob_sources("${SDL3_SOURCE_DIR}/src/time/ngage/*.cpp") set(SDL_TIME_NGAGE 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/render/ngage/*.cpp") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/render/ngage/*.cpp" + "${SDL3_SOURCE_DIR}/src/render/ngage/*.h" + ) sdl_glob_sources("${SDL3_SOURCE_DIR}/src/time/unix/*.c") set(SDL_TIMER_NGAGE 1) @@ -3001,7 +3298,10 @@ elseif(NGAGE) set(SDL_FSOPS_POSIX 1) set(SDL_VIDEO_DRIVER_NGAGE 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/video/ngage/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/video/ngage/*.c" + "${SDL3_SOURCE_DIR}/src/video/ngage/*.h" + ) set(HAVE_SDL_TIMERS TRUE) set_option(SDL_LEAN_AND_MEAN "Enable lean and mean" ON) @@ -3044,7 +3344,9 @@ if (SDL_DIALOG) elseif(UNIX AND NOT APPLE AND NOT RISCOS AND NOT HAIKU) sdl_sources(${SDL3_SOURCE_DIR}/src/dialog/unix/SDL_unixdialog.c) sdl_sources(${SDL3_SOURCE_DIR}/src/dialog/unix/SDL_portaldialog.c) + sdl_sources(${SDL3_SOURCE_DIR}/src/dialog/unix/SDL_portaldialog.h) sdl_sources(${SDL3_SOURCE_DIR}/src/dialog/unix/SDL_zenitydialog.c) + sdl_sources(${SDL3_SOURCE_DIR}/src/dialog/unix/SDL_zenitydialog.h) set(HAVE_SDL_DIALOG TRUE) elseif(HAIKU) sdl_sources(${SDL3_SOURCE_DIR}/src/dialog/haiku/SDL_haikudialog.cc) @@ -3060,7 +3362,10 @@ endif() sdl_sources("${SDL3_SOURCE_DIR}/src/process/SDL_process.c") if(WINDOWS) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/process/windows/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/process/windows/*.c" + "${SDL3_SOURCE_DIR}/src/process/windows/*.h" + ) set(SDL_PROCESS_WINDOWS 1) set(HAVE_SDL_PROCESS TRUE) else() @@ -3101,7 +3406,10 @@ int main(void) } " HAVE_POSIX_SPAWN) if(HAVE_POSIX_SPAWN) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/process/posix/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/process/posix/*.c" + "${SDL3_SOURCE_DIR}/src/process/posix/*.h" + ) set(SDL_PROCESS_POSIX 1) set(HAVE_SDL_PROCESS TRUE) endif() @@ -3112,7 +3420,10 @@ endif() if(SDL_VIDEO) if(SDL_OFFSCREEN) set(SDL_VIDEO_DRIVER_OFFSCREEN 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/video/offscreen/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/video/offscreen/*.c" + "${SDL3_SOURCE_DIR}/src/video/offscreen/*.h" + ) set(HAVE_OFFSCREEN TRUE) set(HAVE_SDL_VIDEO TRUE) endif() @@ -3122,17 +3433,26 @@ sdl_glob_sources(${SDL3_SOURCE_DIR}/src/tray/*.c) if(SDL_GPU) if(HAVE_D3D11_H) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/gpu/d3d11/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/gpu/d3d11/*.c" + "${SDL3_SOURCE_DIR}/src/gpu/d3d11/*.h" + ) set(SDL_GPU_D3D11 1) set(HAVE_SDL_GPU TRUE) endif() if(WINDOWS) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/gpu/d3d12/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/gpu/d3d12/*.c" + "${SDL3_SOURCE_DIR}/src/gpu/d3d12/*.h" + ) set(SDL_GPU_D3D12 1) set(HAVE_SDL_GPU TRUE) endif() if(SDL_VIDEO_VULKAN) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/gpu/vulkan/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/gpu/vulkan/*.c" + "${SDL3_SOURCE_DIR}/src/gpu/vulkan/*.h" + ) set(SDL_GPU_VULKAN 1) set(HAVE_SDL_GPU TRUE) endif() @@ -3152,35 +3472,59 @@ endif() # src/X/*.c does not get included. if(NOT HAVE_SDL_AUDIO) set(SDL_AUDIO_DRIVER_DUMMY 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/audio/dummy/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/audio/dummy/*.c" + "${SDL3_SOURCE_DIR}/src/audio/dummy/*.h" + ) endif() if(NOT HAVE_SDL_VIDEO) set(SDL_VIDEO_DRIVER_DUMMY 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/video/dummy/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/video/dummy/*.c" + "${SDL3_SOURCE_DIR}/src/video/dummy/*.h" + ) endif() if(NOT HAVE_SDL_JOYSTICK) set(SDL_JOYSTICK_DUMMY 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/joystick/dummy/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/joystick/dummy/*.c" + "${SDL3_SOURCE_DIR}/src/joystick/dummy/*.h" + ) endif() if(NOT HAVE_SDL_HAPTIC) set(SDL_HAPTIC_DUMMY 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/haptic/dummy/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/haptic/dummy/*.c" + "${SDL3_SOURCE_DIR}/src/haptic/dummy/*.h" + ) endif() if(NOT HAVE_SDL_SENSORS) set(SDL_SENSOR_DUMMY 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/sensor/dummy/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/sensor/dummy/*.c" + "${SDL3_SOURCE_DIR}/src/sensor/dummy/*.h" + ) endif() if(NOT HAVE_SDL_LOADSO) set(SDL_LOADSO_DUMMY 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/loadso/dummy/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/loadso/dummy/*.c" + "${SDL3_SOURCE_DIR}/src/loadso/dummy/*.h" + ) endif() if(NOT HAVE_SDL_FILESYSTEM) set(SDL_FILESYSTEM_DUMMY 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/filesystem/dummy/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/filesystem/dummy/*.c" + "${SDL3_SOURCE_DIR}/src/filesystem/dummy/*.h" + ) endif() if(NOT HAVE_SDL_STORAGE) set(SDL_STORAGE_GENERIC 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/storage/generic/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/storage/generic/*.c" + "${SDL3_SOURCE_DIR}/src/storage/generic/*.h" + ) endif() if(NOT HAVE_SDL_FSOPS) set(SDL_FSOPS_DUMMY 1) @@ -3188,11 +3532,17 @@ if(NOT HAVE_SDL_FSOPS) endif() if(NOT HAVE_SDL_LOCALE) set(SDL_LOCALE_DUMMY 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/locale/dummy/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/locale/dummy/*.c" + "${SDL3_SOURCE_DIR}/src/locale/dummy/*.h" + ) endif() if(NOT HAVE_SDL_MISC) set(SDL_MISC_DUMMY 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/misc/dummy/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/misc/dummy/*.c" + "${SDL3_SOURCE_DIR}/src/misc/dummy/*.h" + ) endif() if(NOT HAVE_SDL_DIALOG) set(SDL_DIALOG_DUMMY 1) @@ -3208,7 +3558,10 @@ if(NOT HAVE_SDL_TRAY) endif() if(NOT HAVE_CAMERA) set(SDL_CAMERA_DRIVER_DUMMY 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/camera/dummy/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/camera/dummy/*.c" + "${SDL3_SOURCE_DIR}/src/camera/dummy/*.h" + ) endif() # We always need to have threads and timers around @@ -3216,7 +3569,10 @@ if(NOT HAVE_SDL_THREADS) # The Emscripten and N-Gage platform has been carefully vetted to work without threads if(EMSCRIPTEN OR NGAGE) set(SDL_THREADS_DISABLED 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/thread/generic/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/thread/generic/*.c" + "${SDL3_SOURCE_DIR}/src/thread/generic/*.h" + ) else() message(FATAL_ERROR "Threads are needed by many SDL subsystems and may not be disabled") endif() @@ -3227,7 +3583,10 @@ endif() # Most platforms use this. if(NOT HAVE_SDL_MAIN_CALLBACKS) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/main/generic/*.c") + sdl_glob_sources( + "${SDL3_SOURCE_DIR}/src/main/generic/*.c" + "${SDL3_SOURCE_DIR}/src/main/generic/*.h" + ) endif() # config variables may contain generator expression, so we need to generate SDL_build_config.h in 2 steps: @@ -3270,9 +3629,10 @@ list(APPEND SDL3_INCLUDE_FILES "${SDL3_BINARY_DIR}/include-revision/SDL3/SDL_rev if(SDL_FRAMEWORK) # With Apple frameworks, headers in the PUBLIC_HEADER property also need to be added as sources list(APPEND SDL3_INCLUDE_FILES ${SDL3_TEST_INCLUDE_FILES}) - sdl_sources(${SDL3_INCLUDE_FILES}) endif() +sdl_sources(${SDL3_INCLUDE_FILES}) + if((CMAKE_STATIC_LIBRARY_PREFIX STREQUAL "" AND CMAKE_STATIC_LIBRARY_SUFFIX STREQUAL ".lib") OR SDL_FRAMEWORK) # - Avoid conflict between the dll import library and the static library # - Create SDL3-static Apple Framework diff --git a/cmake/sdlcommands.cmake b/cmake/sdlcommands.cmake index d658eb3f8e..1349326313 100644 --- a/cmake/sdlcommands.cmake +++ b/cmake/sdlcommands.cmake @@ -1,6 +1,21 @@ add_library(SDL3-collector INTERFACE) add_library(SDL3_test-collector INTERFACE) +function(sdl_source_group prefix_directory) + set(prefixed_list) + file(TO_CMAKE_PATH ${prefix_directory} normalized_prefix_path) + foreach(file in ${ARGN}) + file(TO_CMAKE_PATH ${file} normalized_path) + string(FIND "${normalized_path}" ${normalized_prefix_path} position) + if("${position}" EQUAL 0) + list(APPEND prefixed_list ${file}) + endif() + endforeach() + if(prefixed_list) + source_group(TREE ${prefix_directory} FILES ${prefixed_list}) + endif() +endfunction() + # Use sdl_glob_sources to add glob sources to SDL3-shared, to SDL3-static, or to both. function(sdl_glob_sources) cmake_parse_arguments(ARGS "" "" "SHARED;STATIC" ${ARGN}) @@ -13,6 +28,7 @@ function(sdl_glob_sources) if(TARGET SDL3-static) target_sources(SDL3-static PRIVATE ${static_sources} ${both_sources}) endif() + sdl_source_group(${PROJECT_SOURCE_DIR} ${shared_sources} ${shared_sources} ${both_sources}) set_property(TARGET SDL3-collector APPEND PROPERTY INTERFACE_SOURCES ${shared_sources} ${static_sources} ${both_sources}) endfunction() @@ -25,6 +41,7 @@ function(sdl_sources) if(TARGET SDL3-static) target_sources(SDL3-static PRIVATE ${ARGS_STATIC} ${ARGS_UNPARSED_ARGUMENTS}) endif() + sdl_source_group(${PROJECT_SOURCE_DIR} ${ARGS_SHARED} ${ARGS_STATIC} ${ARGS_UNPARSED_ARGUMENTS}) set_property(TARGET SDL3-collector APPEND PROPERTY INTERFACE_SOURCES ${ARGS_SHARED} ${ARGS_STATIC} ${ARGS_UNPARSED_ARGUMENTS}) endfunction() From e11110400a0928e811ba02e128c3ca1567aca710 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Thu, 14 Aug 2025 13:41:40 -0700 Subject: [PATCH 57/61] Fixed crash when reinitializing video on X11 The keyboard keymap was left pointing at a freed keymap after X11_QuitKeyboard() --- src/video/x11/SDL_x11keyboard.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/video/x11/SDL_x11keyboard.c b/src/video/x11/SDL_x11keyboard.c index 247d8e0a84..d26087a12f 100644 --- a/src/video/x11/SDL_x11keyboard.c +++ b/src/video/x11/SDL_x11keyboard.c @@ -515,6 +515,7 @@ void X11_QuitKeyboard(SDL_VideoDevice *_this) #ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLIB if (data->keyboard.xkb_enabled) { + SDL_SetKeymap(NULL, false); for (int i = 0; i < XkbNumKbdGroups; ++i) { SDL_DestroyKeymap(data->keyboard.xkb.keymaps[i]); data->keyboard.xkb.keymaps[i] = NULL; From 20df88a85b9cefc4e44ee4500e3a8ea7707ad4db Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Thu, 14 Aug 2025 14:44:49 -0700 Subject: [PATCH 58/61] Fixed spacing --- src/events/SDL_keyboard.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/events/SDL_keyboard.c b/src/events/SDL_keyboard.c index 6023c88608..bb43206024 100644 --- a/src/events/SDL_keyboard.c +++ b/src/events/SDL_keyboard.c @@ -266,7 +266,7 @@ void SDL_SetKeymap(SDL_Keymap *keymap, bool send_event) !SDL_isdigit(SDL_GetKeymapKeycode(keymap, (SDL_Scancode)i, SDL_KMOD_SHIFT))) { keymap->french_numbers = false; break; - } + } } // Detect non-Latin keymap From 2d855e12d2401777d867fa5f14bf8e390a55b66a Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Thu, 14 Aug 2025 14:48:58 -0700 Subject: [PATCH 59/61] Fixed style --- src/joystick/hidapi/SDL_hidapi_lg4ff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/joystick/hidapi/SDL_hidapi_lg4ff.c b/src/joystick/hidapi/SDL_hidapi_lg4ff.c index 901911985f..683b2d0c6c 100644 --- a/src/joystick/hidapi/SDL_hidapi_lg4ff.c +++ b/src/joystick/hidapi/SDL_hidapi_lg4ff.c @@ -277,7 +277,7 @@ static bool HIDAPI_DriverLg4ff_IsSupportedDevice( return true; } // a supported native mode is found, send mode change command, then still state that we support the device - if (device != NULL && SDL_HIDAPI_DriverLg4ff_GetEnvInt("SDL_HIDAPI_LG4FF_NO_MODE_SWITCH", 0, 1, 0) == 0) { + if (device && SDL_HIDAPI_DriverLg4ff_GetEnvInt("SDL_HIDAPI_LG4FF_NO_MODE_SWITCH", 0, 1, 0) == 0) { HIDAPI_DriverLg4ff_SwitchMode(device, real_id); } return true; From fe16c620d8722296f8956a7d27d657be65678e38 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Thu, 14 Aug 2025 14:49:04 -0700 Subject: [PATCH 60/61] Fix crash when enumerating Steam Controllers Closes https://github.com/libsdl-org/SDL/pull/13746 --- src/joystick/hidapi/SDL_hidapi_steam.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/joystick/hidapi/SDL_hidapi_steam.c b/src/joystick/hidapi/SDL_hidapi_steam.c index b48d35393b..6b366515d3 100644 --- a/src/joystick/hidapi/SDL_hidapi_steam.c +++ b/src/joystick/hidapi/SDL_hidapi_steam.c @@ -1038,6 +1038,11 @@ static bool HIDAPI_DriverSteam_IsSupportedDevice(SDL_HIDAPI_Device *device, cons return false; } + if (!device) { + // Might be supported by this driver, enumerate and find out + return true; + } + if (device->is_bluetooth) { return true; } From 5f77da3a5025b92500c32d0dc7962d22fc79a8c7 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Thu, 14 Aug 2025 19:30:54 -0700 Subject: [PATCH 61/61] x11: use raw values for relative mouse motion Fixes https://github.com/libsdl-org/SDL/issues/13743 --- src/video/x11/SDL_x11xinput2.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/video/x11/SDL_x11xinput2.c b/src/video/x11/SDL_x11xinput2.c index 5da1bb791a..11afba68ef 100644 --- a/src/video/x11/SDL_x11xinput2.c +++ b/src/video/x11/SDL_x11xinput2.c @@ -84,15 +84,14 @@ static void parse_relative_valuators(SDL_XInput2DeviceInfo *devinfo, const XIRaw for (int j = 0; j < 2; ++j) { if (devinfo->number[j] == i) { - const double current_val = rawev->valuators.values[values_i]; + double current_val = rawev->raw_values[values_i]; if (devinfo->relative[j]) { processed_coords[j] = current_val; } else { - processed_coords[j] = devinfo->prev_coords[j] - current_val; // convert absolute to relative + processed_coords[j] = (current_val - devinfo->prev_coords[j]); // convert absolute to relative + devinfo->prev_coords[j] = current_val; } - - devinfo->prev_coords[j] = current_val; ++found; break;