diff --git a/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java b/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java index 27ecad87dfabb..c1c4c661f3621 100644 --- a/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java +++ b/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java @@ -61,7 +61,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh private static final String TAG = "SDL"; private static final int SDL_MAJOR_VERSION = 2; private static final int SDL_MINOR_VERSION = 32; - private static final int SDL_MICRO_VERSION = 0; + private static final int SDL_MICRO_VERSION = 2; /* // Display InputType.SOURCE/CLASS of events and devices // diff --git a/build.zig b/build.zig index fc2c323c839af..d54bb428e7aa6 100644 --- a/build.zig +++ b/build.zig @@ -159,7 +159,7 @@ pub fn build(b: *std.Build) void { .style = .{ .cmake = b.path("include/SDL_revision.h.cmake") }, .include_path = "SDL_revision.h", }, .{ - .SDL_REVISION = "SDL-2.32.0", + .SDL_REVISION = "SDL-2.32.2", .SDL_VENDOR_INFO = "allyourcodebase.com", }); lib.addConfigHeader(revision_header); diff --git a/build.zig.zon b/build.zig.zon index f2412e10fd2fc..1dc065ac5e5b3 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -1,6 +1,6 @@ .{ .name = "SDL", - .version = "2.32.0", + .version = "2.32.2", .minimum_zig_version = "0.13.0", .dependencies = .{}, .paths = .{ diff --git a/include/SDL_hints.h b/include/SDL_hints.h index 4665107fd2b28..6713d01fea204 100644 --- a/include/SDL_hints.h +++ b/include/SDL_hints.h @@ -1423,6 +1423,27 @@ extern "C" { */ #define SDL_HINT_JOYSTICK_DEVICE "SDL_JOYSTICK_DEVICE" + +/** + * A variable containing a list of devices and their desired number of haptic + * (force feedback) enabled axis. + * + * The format of the string is a comma separated list of USB VID/PID pairs in + * hexadecimal form plus the number of desired axes, e.g. + * + * `0xAAAA/0xBBBB/1,0xCCCC/0xDDDD/3` + * + * This hint supports a "wildcard" device that will set the number of haptic + * axes on all initialized haptic devices which were not defined explicitly in + * this hint. + * + * `0xFFFF/0xFFFF/1` + * + * This hint should be set before a controller is opened. The number of haptic + * axes won't exceed the number of real axes found on the device. + */ +#define SDL_HINT_JOYSTICK_HAPTIC_AXES "SDL_JOYSTICK_HAPTIC_AXES" + /** * A variable controlling whether joysticks on Linux will always treat 'hat' * axis inputs (ABS_HAT0X - ABS_HAT3Y) as 8-way digital hats without checking diff --git a/include/SDL_keyboard.h b/include/SDL_keyboard.h index 8c4efdd078658..62ac9f2288ac3 100644 --- a/include/SDL_keyboard.h +++ b/include/SDL_keyboard.h @@ -255,7 +255,7 @@ extern DECLSPEC SDL_Keycode SDLCALL SDL_GetKeyFromName(const char *name); * On some platforms using this function activates the screen keyboard. * * On desktop platforms, SDL_StartTextInput() is implicitly called on SDL - * window creation which will cause events SDL_TextInputEvent and + * video subsystem initialization which will cause SDL_TextInputEvent and * SDL_TextEditingEvent to begin emitting. * * \since This function is available since SDL 2.0.0. diff --git a/include/SDL_version.h b/include/SDL_version.h index 32d1133beea13..0465020776924 100644 --- a/include/SDL_version.h +++ b/include/SDL_version.h @@ -58,7 +58,7 @@ typedef struct SDL_version */ #define SDL_MAJOR_VERSION 2 #define SDL_MINOR_VERSION 32 -#define SDL_PATCHLEVEL 0 +#define SDL_PATCHLEVEL 2 /** * Macro to determine SDL version program was compiled against. diff --git a/src/audio/emscripten/SDL_emscriptenaudio.c b/src/audio/emscripten/SDL_emscriptenaudio.c index 3600fb5c587ed..304cabd0bf623 100644 --- a/src/audio/emscripten/SDL_emscriptenaudio.c +++ b/src/audio/emscripten/SDL_emscriptenaudio.c @@ -310,7 +310,7 @@ static int EMSCRIPTENAUDIO_OpenDevice(_THIS, const char *devname) if ((SDL2 === undefined) || (SDL2.capture === undefined)) { return; } audioProcessingEvent.outputBuffer.getChannelData(0).fill(0.0); SDL2.capture.currentCaptureBuffer = audioProcessingEvent.inputBuffer; - dynCall('vi', $2, [$3]); + dynCall('vp', $2, [$3]); }; SDL2.capture.mediaStreamNode.connect(SDL2.capture.scriptProcessorNode); SDL2.capture.scriptProcessorNode.connect(SDL2.audioContext.destination); @@ -326,7 +326,7 @@ static int EMSCRIPTENAUDIO_OpenDevice(_THIS, const char *devname) SDL2.capture.silenceBuffer.getChannelData(0).fill(0.0); var silence_callback = function() { SDL2.capture.currentCaptureBuffer = SDL2.capture.silenceBuffer; - dynCall('vi', $2, [$3]); + dynCall('vp', $2, [$3]); }; SDL2.capture.silenceTimer = setInterval(silence_callback, ($1 / SDL2.audioContext.sampleRate) * 1000); @@ -351,7 +351,7 @@ static int EMSCRIPTENAUDIO_OpenDevice(_THIS, const char *devname) SDL2.audio.silenceBuffer = undefined; } SDL2.audio.currentOutputBuffer = e['outputBuffer']; - dynCall('vi', $2, [$3]); + dynCall('vp', $2, [$3]); }; SDL2.audio.scriptProcessorNode['connect'](SDL2.audioContext['destination']); @@ -369,7 +369,7 @@ static int EMSCRIPTENAUDIO_OpenDevice(_THIS, const char *devname) // the buffer that gets filled here just gets ignored, so the app can make progress // and/or avoid flooding audio queues until we can actually play audio. SDL2.audio.currentOutputBuffer = SDL2.audio.silenceBuffer; - dynCall('vi', $2, [$3]); + dynCall('vp', $2, [$3]); SDL2.audio.currentOutputBuffer = undefined; }; diff --git a/src/audio/pulseaudio/SDL_pulseaudio.c b/src/audio/pulseaudio/SDL_pulseaudio.c index 2fb2f31c68b94..49230b7d46a4a 100644 --- a/src/audio/pulseaudio/SDL_pulseaudio.c +++ b/src/audio/pulseaudio/SDL_pulseaudio.c @@ -497,7 +497,7 @@ static void PULSEAUDIO_FlushCapture(_THIS) { struct SDL_PrivateAudioData *h = this->hidden; const void *data = NULL; - size_t nbytes = 0; + size_t nbytes = 0, buflen = 0; PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop); @@ -507,19 +507,19 @@ static void PULSEAUDIO_FlushCapture(_THIS) h->capturelen = 0; } - while (SDL_AtomicGet(&this->enabled) && (PULSEAUDIO_pa_stream_readable_size(h->stream) > 0)) { + buflen = PULSEAUDIO_pa_stream_readable_size(h->stream); + while (SDL_AtomicGet(&this->enabled) && (buflen > 0)) { PULSEAUDIO_pa_threaded_mainloop_wait(pulseaudio_threaded_mainloop); if ((PULSEAUDIO_pa_context_get_state(pulseaudio_context) != PA_CONTEXT_READY) || (PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY)) { /*printf("PULSEAUDIO DEVICE FAILURE IN FLUSHCAPTURE!\n");*/ SDL_OpenedAudioDeviceDisconnected(this); break; } - - if (PULSEAUDIO_pa_stream_readable_size(h->stream) > 0) { - /* a new fragment is available! Just dump it. */ - PULSEAUDIO_pa_stream_peek(h->stream, &data, &nbytes); - PULSEAUDIO_pa_stream_drop(h->stream); /* drop this fragment. */ - } + /* a fragment of audio present before FlushCapture was call is + available! Just drop it. */ + PULSEAUDIO_pa_stream_peek(h->stream, &data, &nbytes); + PULSEAUDIO_pa_stream_drop(h->stream); + buflen -= nbytes; } PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop); diff --git a/src/dynapi/SDL_dynapi.c b/src/dynapi/SDL_dynapi.c index 48f50954aeefc..04adfd2ce77f4 100644 --- a/src/dynapi/SDL_dynapi.c +++ b/src/dynapi/SDL_dynapi.c @@ -440,19 +440,24 @@ extern SDL_NORETURN void SDL_ExitProcess(int exitcode); static void SDL_InitDynamicAPILocked(void) { + SDL_DYNAPI_ENTRYFN entry = NULL; /* funcs from here by default. */ + SDL_bool use_internal = SDL_TRUE; + /* this can't use SDL_getenv_REAL, because it might allocate memory before the app can set their allocator */ #if defined(WIN32) || defined(_WIN32) || defined(__CYGWIN__) /* We've always used LoadLibraryA for this, so this has never worked with Unicode paths on Windows. Sorry. */ char envbuf[512]; /* overflows will just report as environment variable being unset, but LoadLibraryA has a MAX_PATH of 260 anyhow, apparently. */ const DWORD rc = GetEnvironmentVariableA(SDL_DYNAMIC_API_ENVVAR, envbuf, (DWORD) sizeof (envbuf)); char *libname = ((rc != 0) && (rc < sizeof (envbuf))) ? envbuf : NULL; +#elif defined(__OS2__) + char * libname; + if (DosScanEnv(SDL_DYNAMIC_API_ENVVAR, &libname) != NO_ERROR) { + libname = NULL; + } #else char *libname = getenv(SDL_DYNAMIC_API_ENVVAR); #endif - SDL_DYNAPI_ENTRYFN entry = NULL; /* funcs from here by default. */ - SDL_bool use_internal = SDL_TRUE; - if (libname) { while (*libname && !entry) { char *ptr = libname; diff --git a/src/haptic/SDL_haptic.c b/src/haptic/SDL_haptic.c index 349eb632a0fd8..86510d5085204 100644 --- a/src/haptic/SDL_haptic.c +++ b/src/haptic/SDL_haptic.c @@ -23,6 +23,85 @@ #include "SDL_syshaptic.h" #include "SDL_haptic_c.h" #include "../joystick/SDL_joystick_c.h" /* For SDL_PrivateJoystickValid */ +#include "SDL_hints.h" +#include "../SDL_hints_c.h" + +typedef struct SDL_Haptic_VIDPID_Naxes { + Uint16 vid; + Uint16 pid; + Uint16 naxes; +} SDL_Haptic_VIDPID_Naxes; + +static void SDL_HapticLoadAxesList(SDL_Haptic_VIDPID_Naxes **entries, int *num_entries) +{ + SDL_Haptic_VIDPID_Naxes entry; + const char *spot; + int length = 0; + + spot = SDL_GetHint(SDL_HINT_JOYSTICK_HAPTIC_AXES); + if (!spot) + return; + + while (SDL_sscanf(spot, "0x%hx/0x%hx/%hu%n", &entry.vid, &entry.pid, &entry.naxes, &length) == 3) { + SDL_assert(length > 0); + spot += length; + length = 0; + + if ((*num_entries % 8) == 0) { + int new_max = *num_entries + 8; + SDL_Haptic_VIDPID_Naxes *new_entries = + (SDL_Haptic_VIDPID_Naxes *)SDL_realloc(*entries, new_max * sizeof(**entries)); + + // Out of memory, go with what we have already + if (!new_entries) + break; + + *entries = new_entries; + } + (*entries)[(*num_entries)++] = entry; + + if (spot[0] == ',') + spot++; + } +} + +// /* Return -1 if not found */ +static int SDL_HapticNaxesListIndex(struct SDL_Haptic_VIDPID_Naxes *entries, int num_entries, Uint16 vid, Uint16 pid) +{ + int i; + if (!entries) + return -1; + + for (i = 0; i < num_entries; ++i) { + if (entries[i].vid == vid && entries[i].pid == pid) + return i; + } + + return -1; +} + +// Check if device needs a custom number of naxes +static int SDL_HapticGetNaxes(Uint16 vid, Uint16 pid) +{ + int num_entries = 0, index = 0, naxes = -1; + SDL_Haptic_VIDPID_Naxes *naxes_list = NULL; + + SDL_HapticLoadAxesList(&naxes_list, &num_entries); + if (!num_entries || !naxes_list) + return -1; + + // Perform "wildcard" pass + index = SDL_HapticNaxesListIndex(naxes_list, num_entries, 0xffff, 0xffff); + if (index >= 0) + naxes = naxes_list[index].naxes; + + index = SDL_HapticNaxesListIndex(naxes_list, num_entries, vid, pid); + if (index >= 0) + naxes = naxes_list[index].naxes; + + SDL_free(naxes_list); + return naxes; +} /* Global for SDL_windowshaptic.c */ #if defined(SDL_HAPTIC_DINPUT) || defined(SDL_HAPTIC_XINPUT) @@ -258,6 +337,8 @@ SDL_Haptic *SDL_HapticOpenFromJoystick(SDL_Joystick *joystick) { SDL_Haptic *haptic; SDL_Haptic *hapticlist; + int naxes, general_axes; + Uint16 vid, pid; /* Make sure there is room. */ if (SDL_NumHaptics() <= 0) { @@ -314,6 +395,18 @@ SDL_Haptic *SDL_HapticOpenFromJoystick(SDL_Joystick *joystick) } SDL_UnlockJoysticks(); + vid = SDL_JoystickGetVendor(joystick); + pid = SDL_JoystickGetProduct(joystick); + general_axes = SDL_JoystickNumAxes(joystick); + + naxes = SDL_HapticGetNaxes(vid, pid); + if (naxes > 0) + haptic->naxes = naxes; + + // Limit to the actual number of axes found on the device + if (general_axes >= 0 && naxes > general_axes) + haptic->naxes = general_axes; + /* Add haptic to list */ ++haptic->ref_count; /* Link the haptic in the list */ diff --git a/src/hidapi/configure.ac b/src/hidapi/configure.ac index c6747f906ac48..c3d3dbc1b1dbb 100644 --- a/src/hidapi/configure.ac +++ b/src/hidapi/configure.ac @@ -79,7 +79,7 @@ case $host in backend="mac" os="darwin" threads="pthreads" - LIBS="${LIBS} -framework IOKit -framework CoreFoundation" + LIBS="${LIBS} -framework IOKit -framework CoreFoundation -framework AppKit" ;; *-freebsd*) AC_MSG_RESULT([ (FreeBSD back-end)]) diff --git a/src/hidapi/mac/Makefile-manual b/src/hidapi/mac/Makefile-manual index 5399b5a70fc62..16052d143fc58 100644 --- a/src/hidapi/mac/Makefile-manual +++ b/src/hidapi/mac/Makefile-manual @@ -14,7 +14,7 @@ COBJS=hid.o CPPOBJS=../hidtest/hidtest.o OBJS=$(COBJS) $(CPPOBJS) CFLAGS+=-I../hidapi -Wall -g -c -LIBS=-framework IOKit -framework CoreFoundation +LIBS=-framework IOKit -framework CoreFoundation -framework AppKit hidtest: $(OBJS) diff --git a/src/hidapi/mac/hid.c b/src/hidapi/mac/hid.c index 0dbe4227d21c3..b45767333740b 100644 --- a/src/hidapi/mac/hid.c +++ b/src/hidapi/mac/hid.c @@ -37,6 +37,9 @@ #define VALVE_USB_VID 0x28DE +/* As defined in AppKit.h, but we don't need the entire AppKit for a single constant. */ +extern const double NSAppKitVersionNumber; + /* Barrier implementation because Mac OSX doesn't have pthread_barrier. It also doesn't have clock_gettime(). So much for POSIX and SUSv2. This implementation came from Brent Priddy and was posted on @@ -131,6 +134,7 @@ struct hid_device_list_node }; static IOHIDManagerRef hid_mgr = 0x0; +static int is_macos_10_10_or_greater = 0; static struct hid_device_list_node *device_list = 0x0; static hid_device *new_hid_device(void) @@ -485,6 +489,7 @@ static int init_hid_manager(void) int HID_API_EXPORT hid_init(void) { if (!hid_mgr) { + is_macos_10_10_or_greater = (NSAppKitVersionNumber >= 1343); /* NSAppKitVersionNumber10_10 */ return init_hid_manager(); } @@ -1138,8 +1143,10 @@ void HID_API_EXPORT hid_close(hid_device *dev) if (!dev) return; - /* Disconnect the report callback before close. */ - if (!dev->disconnected) { + /* Disconnect the report callback before close. + See comment below. + */ + if (is_macos_10_10_or_greater || !dev->disconnected) { IOHIDDeviceRegisterInputReportCallback( dev->device_handle, dev->input_report_buf, dev->max_input_report_len, NULL, dev); @@ -1162,8 +1169,14 @@ void HID_API_EXPORT hid_close(hid_device *dev) /* Close the OS handle to the device, but only if it's not been unplugged. If it's been unplugged, then calling - IOHIDDeviceClose() will crash. */ - if (!dev->disconnected) { + IOHIDDeviceClose() will crash. + + UPD: The crash part was true in/until some version of macOS. + Starting with macOS 10.15, there is an opposite effect in some environments: + crash happenes if IOHIDDeviceClose() is not called. + Not leaking a resource in all tested environments. + */ + if (is_macos_10_10_or_greater || !dev->disconnected) { IOHIDDeviceClose(dev->device_handle, kIOHIDOptionsTypeNone); } diff --git a/src/main/windows/version.rc b/src/main/windows/version.rc index 42ba606fe4d72..28a6a0669114b 100644 --- a/src/main/windows/version.rc +++ b/src/main/windows/version.rc @@ -9,8 +9,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // VS_VERSION_INFO VERSIONINFO - FILEVERSION 2,32,0,0 - PRODUCTVERSION 2,32,0,0 + FILEVERSION 2,32,2,0 + PRODUCTVERSION 2,32,2,0 FILEFLAGSMASK 0x3fL FILEFLAGS 0x0L FILEOS 0x40004L @@ -23,12 +23,12 @@ BEGIN BEGIN VALUE "CompanyName", "\0" VALUE "FileDescription", "SDL\0" - VALUE "FileVersion", "2, 32, 0, 0\0" + VALUE "FileVersion", "2, 32, 2, 0\0" VALUE "InternalName", "SDL\0" VALUE "LegalCopyright", "Copyright (C) 2025 Sam Lantinga\0" VALUE "OriginalFilename", "SDL2.dll\0" VALUE "ProductName", "Simple DirectMedia Layer\0" - VALUE "ProductVersion", "2, 32, 0, 0\0" + VALUE "ProductVersion", "2, 32, 2, 0\0" END END BLOCK "VarFileInfo" diff --git a/src/stdlib/SDL_string.c b/src/stdlib/SDL_string.c index 9af6977fe6b28..2ce22186e3c56 100644 --- a/src/stdlib/SDL_string.c +++ b/src/stdlib/SDL_string.c @@ -1113,6 +1113,7 @@ static SDL_bool CharacterMatchesSet(char c, const char *set, size_t set_len) /* NOLINTNEXTLINE(readability-non-const-parameter) */ int SDL_vsscanf(const char *text, const char *fmt, va_list ap) { + const char *start = text; int retval = 0; if (!text || !*text) { @@ -1383,6 +1384,36 @@ int SDL_vsscanf(const char *text, const char *fmt, va_list ap) } done = SDL_TRUE; break; + case 'n': + switch (inttype) { + case DO_SHORT: + { + short *valuep = va_arg(ap, short *); + *valuep = (short)(text - start); + } break; + case DO_INT: + { + int *valuep = va_arg(ap, int *); + *valuep = (int)(text - start); + } break; + case DO_LONG: + { + long *valuep = va_arg(ap, long *); + *valuep = (long)(text - start); + } break; + case DO_LONGLONG: + { + long long *valuep = va_arg(ap, long long *); + *valuep = (long long)(text - start); + } break; + case DO_SIZE_T: + { + size_t *valuep = va_arg(ap, size_t *); + *valuep = (size_t)(text - start); + } break; + } + done = SDL_TRUE; + break; case '[': { const char *set = fmt + 1; diff --git a/src/thread/windows/SDL_systhread.c b/src/thread/windows/SDL_systhread.c index fbeabd5a778ca..37653f96318be 100644 --- a/src/thread/windows/SDL_systhread.c +++ b/src/thread/windows/SDL_systhread.c @@ -159,7 +159,7 @@ void SDL_SYS_SetupThread(const char *name) inf.dwFlags = 0; /* The debugger catches this, renames the thread, continues on. */ - RaiseException(0x406D1388, 0, sizeof(inf) / sizeof(ULONG), (const ULONG_PTR *)&inf); + RaiseException(0x406D1388, 0, sizeof(inf) / sizeof(ULONG_PTR), (const ULONG_PTR *)&inf); } } }