From 899d0b5dce7f94e0b9d472bdf0d3263a2cd1054c Mon Sep 17 00:00:00 2001 From: Tyler Wilding Date: Sat, 23 Aug 2025 20:13:35 -0400 Subject: [PATCH] deps: update to SDL 3.2.20 (#4007) --- .../SDL/.github/workflows/create-test-plan.py | 11 +- third-party/SDL/.github/workflows/generic.yml | 6 +- third-party/SDL/.github/workflows/release.yml | 2 +- third-party/SDL/Android.mk | 4 + third-party/SDL/CMakeLists.txt | 21 +- .../SDL/Xcode/SDL/Info-Framework.plist | 4 +- .../Xcode/SDL/SDL.xcodeproj/project.pbxproj | 8 +- .../SDL/Xcode/SDL/pkg-support/SDL.info | 2 +- .../SDL/android-project/app/build.gradle | 2 +- .../android-project/app/jni/Application.mk | 5 +- .../app/jni/src/CMakeLists.txt | 5 + .../main/java/org/libsdl/app/SDLActivity.java | 2 +- .../SDL/build-scripts/androidbuildlibs.sh | 7 + third-party/SDL/cmake/sdlchecks.cmake | 16 +- third-party/SDL/docs/README-macos.md | 4 +- third-party/SDL/include/SDL3/SDL.h | 2 +- third-party/SDL/include/SDL3/SDL_assert.h | 2 +- third-party/SDL/include/SDL3/SDL_audio.h | 7 +- third-party/SDL/include/SDL3/SDL_clipboard.h | 28 +-- third-party/SDL/include/SDL3/SDL_endian.h | 2 +- third-party/SDL/include/SDL3/SDL_gpu.h | 2 +- third-party/SDL/include/SDL3/SDL_scancode.h | 2 +- third-party/SDL/include/SDL3/SDL_version.h | 2 +- third-party/SDL/include/SDL3/SDL_video.h | 13 ++ third-party/SDL/include/SDL3/SDL_vulkan.h | 6 +- .../build_config/SDL_build_config_windows.h | 11 +- third-party/SDL/src/SDL.c | 6 +- third-party/SDL/src/audio/SDL_audio.c | 38 +++- third-party/SDL/src/audio/SDL_audiotypecvt.c | 4 + third-party/SDL/src/audio/SDL_wave.c | 4 +- .../SDL/src/audio/alsa/SDL_alsa_audio.c | 2 +- .../src/audio/directsound/SDL_directsound.c | 2 +- third-party/SDL/src/audio/psp/SDL_pspaudio.c | 2 +- third-party/SDL/src/audio/wasapi/SDL_wasapi.c | 2 +- .../src/camera/android/SDL_camera_android.c | 2 +- third-party/SDL/src/core/gdk/SDL_gdk.cpp | 2 +- .../SDL/src/core/windows/SDL_immdevice.c | 22 +- .../SDL/src/core/windows/SDL_immdevice.h | 2 +- .../SDL/src/core/windows/SDL_windows.c | 72 +++++++ third-party/SDL/src/core/windows/version.rc | 8 +- .../SDL/src/dialog/cocoa/SDL_cocoadialog.m | 7 + .../SDL/src/events/scancodes_xfree86.h | 8 +- third-party/SDL/src/gpu/SDL_gpu.c | 9 +- third-party/SDL/src/gpu/d3d12/SDL_gpu_d3d12.c | 11 +- third-party/SDL/src/gpu/metal/SDL_gpu_metal.m | 1 + .../SDL/src/gpu/vulkan/SDL_gpu_vulkan.c | 50 +++-- .../SDL/src/haptic/windows/SDL_dinputhaptic.c | 4 - third-party/SDL/src/hidapi/libusb/hid.c | 28 ++- third-party/SDL/src/hidapi/linux/hid.c | 2 +- third-party/SDL/src/hidapi/windows/hid.c | 1 + third-party/SDL/src/joystick/SDL_gamepad.c | 9 +- third-party/SDL/src/joystick/SDL_gamepad_db.h | 6 +- third-party/SDL/src/joystick/SDL_joystick.c | 6 +- .../SDL/src/joystick/controller_list.h | 2 +- .../src/joystick/darwin/SDL_iokitjoystick.c | 35 ++- .../src/joystick/darwin/SDL_iokitjoystick_c.h | 1 + .../src/joystick/hidapi/SDL_hidapi_gamecube.c | 106 +++++---- .../SDL/src/joystick/hidapi/SDL_hidapi_ps4.c | 4 +- .../SDL/src/joystick/hidapi/SDL_hidapi_ps5.c | 23 +- .../src/joystick/hidapi/SDL_hidapi_switch.c | 116 ++++++---- third-party/SDL/src/joystick/usb_ids.h | 1 + .../src/joystick/windows/SDL_dinputjoystick.c | 4 - third-party/SDL/src/render/SDL_render.c | 8 +- .../src/render/opengles2/SDL_render_gles2.c | 16 ++ .../SDL/src/render/vulkan/SDL_render_vulkan.c | 4 +- third-party/SDL/src/stdlib/SDL_getenv.c | 7 +- third-party/SDL/src/stdlib/SDL_malloc.c | 7 + .../SDL/src/thread/psp/SDL_systhread.c | 10 +- third-party/SDL/src/video/SDL_clipboard.c | 14 +- third-party/SDL/src/video/SDL_surface.c | 2 +- third-party/SDL/src/video/SDL_sysvideo.h | 6 + third-party/SDL/src/video/SDL_video.c | 54 ++++- .../SDL/src/video/cocoa/SDL_cocoaevents.m | 17 +- .../SDL/src/video/cocoa/SDL_cocoawindow.h | 2 +- .../SDL/src/video/cocoa/SDL_cocoawindow.m | 101 +++++---- .../SDL/src/video/wayland/SDL_waylandevents.c | 2 +- .../SDL/src/video/wayland/SDL_waylandvideo.c | 1 + .../SDL/src/video/wayland/SDL_waylandwindow.c | 131 +++++------ .../SDL/src/video/wayland/SDL_waylandwindow.h | 5 +- .../SDL/src/video/windows/SDL_windowsevents.c | 6 +- .../src/video/windows/SDL_windowsgameinput.c | 6 + .../src/video/windows/SDL_windowsrawinput.c | 2 + .../SDL/src/video/windows/SDL_windowswindow.c | 203 ++++++------------ .../SDL/src/video/windows/SDL_windowswindow.h | 1 - third-party/SDL/src/video/x11/SDL_x11events.c | 26 +-- third-party/SDL/src/video/x11/SDL_x11events.h | 1 + .../SDL/src/video/x11/SDL_x11messagebox.c | 3 + third-party/SDL/src/video/x11/SDL_x11window.c | 127 +++++++---- third-party/SDL/src/video/x11/SDL_x11window.h | 1 - .../SDL/src/video/x11/SDL_x11xinput2.c | 16 +- third-party/SDL/test/CMakeLists.txt | 2 +- third-party/SDL/test/testautomation_surface.c | 44 ++++ third-party/SDL/test/testffmpeg_vulkan.c | 17 ++ third-party/SDL/test/testmodal.c | 2 +- third-party/SDL/test/testpopup.c | 48 ++++- third-party/SDL/test/testtray.c | 10 +- vendor.yaml | 2 +- 97 files changed, 1092 insertions(+), 590 deletions(-) diff --git a/third-party/SDL/.github/workflows/create-test-plan.py b/third-party/SDL/.github/workflows/create-test-plan.py index a137889c81..5e2ab02800 100644 --- a/third-party/SDL/.github/workflows/create-test-plan.py +++ b/third-party/SDL/.github/workflows/create-test-plan.py @@ -174,6 +174,7 @@ class JobDetails: brew_packages: list[str] = dataclasses.field(default_factory=list) cmake_toolchain_file: str = "" cmake_arguments: list[str] = dataclasses.field(default_factory=list) + cmake_generator: str = "Ninja" cmake_build_arguments: list[str] = dataclasses.field(default_factory=list) clang_tidy: bool = True cppflags: list[str] = dataclasses.field(default_factory=list) @@ -222,6 +223,7 @@ class JobDetails: check_sources: bool = False setup_python: bool = False pypi_packages: list[str] = dataclasses.field(default_factory=list) + binutils_strings: str = "strings" def to_workflow(self, enable_artifacts: bool) -> dict[str, str|bool]: data = { @@ -255,6 +257,7 @@ class JobDetails: "cflags": my_shlex_join(self.cppflags + self.cflags), "cxxflags": my_shlex_join(self.cppflags + self.cxxflags), "ldflags": my_shlex_join(self.ldflags), + "cmake-generator": self.cmake_generator, "cmake-toolchain-file": self.cmake_toolchain_file, "clang-tidy": self.clang_tidy, "cmake-arguments": my_shlex_join(self.cmake_arguments), @@ -289,6 +292,7 @@ class JobDetails: "check-sources": self.check_sources, "setup-python": self.setup_python, "pypi-packages": my_shlex_join(self.pypi_packages), + "binutils-strings": self.binutils_strings, } return {k: v for k, v in data.items() if v != ""} @@ -675,13 +679,16 @@ def spec_to_job(spec: JobSpec, key: str, trackmem_symbol_names: bool) -> JobDeta job.shared_lib = SharedLibType.SO_0 job.static_lib = StaticLibType.A case SdlPlatform.N3ds: - job.ccache = True + job.cmake_generator = "Unix Makefiles" + job.cmake_build_arguments.append("-j$(nproc)") + job.ccache = False job.shared = False - job.apt_packages = ["ccache", "ninja-build", "binutils"] + job.apt_packages = [] job.clang_tidy = False job.run_tests = False job.cc_from_cmake = True job.cmake_toolchain_file = "${DEVKITPRO}/cmake/3DS.cmake" + job.binutils_strings = "/opt/devkitpro/devkitARM/bin/arm-none-eabi-strings" job.static_lib = StaticLibType.A case SdlPlatform.Msys2: job.ccache = True diff --git a/third-party/SDL/.github/workflows/generic.yml b/third-party/SDL/.github/workflows/generic.yml index 9776431e3d..c2846bad5a 100644 --- a/third-party/SDL/.github/workflows/generic.yml +++ b/third-party/SDL/.github/workflows/generic.yml @@ -201,7 +201,7 @@ jobs: #shell: ${{ matrix.platform.shell }} run: | ${{ matrix.platform.source-cmd }} - ${{ matrix.platform.cmake-config-emulator }} cmake -S . -B build -GNinja \ + ${{ matrix.platform.cmake-config-emulator }} cmake -S . -B build -G "${{ matrix.platform.cmake-generator }}" \ -Wdeprecated -Wdev -Werror \ ${{ matrix.platform.cmake-toolchain-file != '' && format('-DCMAKE_TOOLCHAIN_FILE={0}', matrix.platform.cmake-toolchain-file) || '' }} \ -DSDL_WERROR=${{ matrix.platform.werror }} \ @@ -232,9 +232,9 @@ jobs: run: | echo "This should show us the SDL_REVISION" echo "Shared library:" - ${{ (matrix.platform.shared-lib && format('strings build/{0} | grep "Github Workflow"', matrix.platform.shared-lib)) || 'echo ""' }} + ${{ (matrix.platform.shared-lib && format('{0} build/{1} | grep "Github Workflow"', matrix.platform.binutils-strings, matrix.platform.shared-lib)) || 'echo ""' }} echo "Static library:" - ${{ (matrix.platform.static-lib && format('strings build/{0} | grep "Github Workflow"', matrix.platform.static-lib)) || 'echo ""' }} + ${{ (matrix.platform.static-lib && format('{0} build/{1} | grep "Github Workflow"', matrix.platform.binutils-strings, matrix.platform.static-lib)) || 'echo ""' }} - name: 'Run build-time tests (CMake)' id: tests if: ${{ !matrix.platform.no-cmake && matrix.platform.run-tests }} diff --git a/third-party/SDL/.github/workflows/release.yml b/third-party/SDL/.github/workflows/release.yml index 4fbd583784..01eaee96ca 100644 --- a/third-party/SDL/.github/workflows/release.yml +++ b/third-party/SDL/.github/workflows/release.yml @@ -256,7 +256,7 @@ jobs: msvc: needs: [src] - runs-on: windows-2019 + runs-on: windows-2025 outputs: VC-x86: ${{ steps.releaser.outputs.VC-x86 }} VC-x64: ${{ steps.releaser.outputs.VC-x64 }} diff --git a/third-party/SDL/Android.mk b/third-party/SDL/Android.mk index b5fe8955f9..413967c40d 100644 --- a/third-party/SDL/Android.mk +++ b/third-party/SDL/Android.mk @@ -107,6 +107,10 @@ 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/third-party/SDL/CMakeLists.txt b/third-party/SDL/CMakeLists.txt index 1c120a5900..58739574ac 100644 --- a/third-party/SDL/CMakeLists.txt +++ b/third-party/SDL/CMakeLists.txt @@ -5,7 +5,7 @@ if(NOT DEFINED CMAKE_BUILD_TYPE) endif() # See docs/release_checklist.md -project(SDL3 LANGUAGES C VERSION "3.2.16") +project(SDL3 LANGUAGES C VERSION "3.2.20") if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) set(SDL3_MAINPROJECT ON) @@ -188,9 +188,12 @@ if(MSVC) # 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_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) @@ -634,6 +637,11 @@ if(MSVC) # Mark SDL3.dll as compatible with Control-flow Enforcement Technology (CET) sdl_shared_link_options("-CETCOMPAT") endif() + + # for VS >= 17.14 targeting ARM64: inline the Interlocked funcs + if(MSVC_VERSION GREATER 1943 AND SDL_CPU_ARM64 AND NOT SDL_LIBC) + sdl_compile_options(PRIVATE "/forceInterlockedFunctions-") + endif() endif() if(CMAKE_C_COMPILER_ID STREQUAL "MSVC") @@ -1336,9 +1344,7 @@ if(ANDROID) set(HAVE_SDL_HAPTIC TRUE) endif() - if(SDL_HIDAPI) - CheckHIDAPI() - endif() + CheckHIDAPI() if(SDL_JOYSTICK) set(SDL_JOYSTICK_ANDROID 1) @@ -1481,6 +1487,11 @@ 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/third-party/SDL/Xcode/SDL/Info-Framework.plist b/third-party/SDL/Xcode/SDL/Info-Framework.plist index 2f0338d685..5e724fca87 100644 --- a/third-party/SDL/Xcode/SDL/Info-Framework.plist +++ b/third-party/SDL/Xcode/SDL/Info-Framework.plist @@ -19,10 +19,10 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 3.2.16 + 3.2.20 CFBundleSignature SDLX CFBundleVersion - 3.2.16 + 3.2.20 diff --git a/third-party/SDL/Xcode/SDL/SDL.xcodeproj/project.pbxproj b/third-party/SDL/Xcode/SDL/SDL.xcodeproj/project.pbxproj index 5a5d205290..ae8a61ba46 100644 --- a/third-party/SDL/Xcode/SDL/SDL.xcodeproj/project.pbxproj +++ b/third-party/SDL/Xcode/SDL/SDL.xcodeproj/project.pbxproj @@ -3086,7 +3086,7 @@ CLANG_ENABLE_OBJC_ARC = YES; DEPLOYMENT_POSTPROCESSING = YES; DYLIB_COMPATIBILITY_VERSION = 201.0.0; - DYLIB_CURRENT_VERSION = 201.16.0; + DYLIB_CURRENT_VERSION = 201.20.0; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_ALTIVEC_EXTENSIONS = YES; @@ -3121,7 +3121,7 @@ "@loader_path/Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.13; - MARKETING_VERSION = 3.2.16; + MARKETING_VERSION = 3.2.20; OTHER_LDFLAGS = "$(CONFIG_FRAMEWORK_LDFLAGS)"; PRODUCT_BUNDLE_IDENTIFIER = org.libsdl.SDL3; PRODUCT_NAME = SDL3; @@ -3150,7 +3150,7 @@ CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; DYLIB_COMPATIBILITY_VERSION = 201.0.0; - DYLIB_CURRENT_VERSION = 201.16.0; + DYLIB_CURRENT_VERSION = 201.20.0; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; @@ -3182,7 +3182,7 @@ "@loader_path/Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.13; - MARKETING_VERSION = 3.2.16; + MARKETING_VERSION = 3.2.20; ONLY_ACTIVE_ARCH = YES; OTHER_LDFLAGS = "$(CONFIG_FRAMEWORK_LDFLAGS)"; PRODUCT_BUNDLE_IDENTIFIER = org.libsdl.SDL3; diff --git a/third-party/SDL/Xcode/SDL/pkg-support/SDL.info b/third-party/SDL/Xcode/SDL/pkg-support/SDL.info index 6f42a55475..475a7ccba5 100644 --- a/third-party/SDL/Xcode/SDL/pkg-support/SDL.info +++ b/third-party/SDL/Xcode/SDL/pkg-support/SDL.info @@ -1,4 +1,4 @@ -Title SDL 3.2.16 +Title SDL 3.2.20 Version 1 Description SDL Library for macOS (http://www.libsdl.org) DefaultLocation /Library/Frameworks diff --git a/third-party/SDL/android-project/app/build.gradle b/third-party/SDL/android-project/app/build.gradle index 989ef34c71..f44cf26732 100644 --- a/third-party/SDL/android-project/app/build.gradle +++ b/third-party/SDL/android-project/app/build.gradle @@ -19,7 +19,7 @@ android { abiFilters 'arm64-v8a' } cmake { - arguments "-DANDROID_PLATFORM=android-21", "-DANDROID_STL=c++_static" + arguments "-DANDROID_PLATFORM=android-21", "-DANDROID_STL=c++_static", "-DAPP_SUPPORT_FLEXIBLE_PAGE_SIZES=true" // abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' abiFilters 'arm64-v8a' } diff --git a/third-party/SDL/android-project/app/jni/Application.mk b/third-party/SDL/android-project/app/jni/Application.mk index 023bc20dfc..80b73fd6bc 100644 --- a/third-party/SDL/android-project/app/jni/Application.mk +++ b/third-party/SDL/android-project/app/jni/Application.mk @@ -7,4 +7,7 @@ APP_ABI := armeabi-v7a arm64-v8a x86 x86_64 # Min runtime API level -APP_PLATFORM=android-16 +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/third-party/SDL/android-project/app/jni/src/CMakeLists.txt b/third-party/SDL/android-project/app/jni/src/CMakeLists.txt index 41a82f2af0..df0a4d0f34 100644 --- a/third-party/SDL/android-project/app/jni/src/CMakeLists.txt +++ b/third-party/SDL/android-project/app/jni/src/CMakeLists.txt @@ -26,4 +26,9 @@ 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/third-party/SDL/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java b/third-party/SDL/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java index 361688d68e..0aa1ef34c8 100644 --- a/third-party/SDL/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java +++ b/third-party/SDL/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 = 3; private static final int SDL_MINOR_VERSION = 2; - private static final int SDL_MICRO_VERSION = 16; + private static final int SDL_MICRO_VERSION = 20; /* // Display InputType.SOURCE/CLASS of events and devices // diff --git a/third-party/SDL/build-scripts/androidbuildlibs.sh b/third-party/SDL/build-scripts/androidbuildlibs.sh index a903f36eea..1004a98703 100644 --- a/third-party/SDL/build-scripts/androidbuildlibs.sh +++ b/third-party/SDL/build-scripts/androidbuildlibs.sh @@ -30,6 +30,7 @@ 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 @@ -42,6 +43,8 @@ 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 @@ -67,6 +70,9 @@ 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 \ @@ -75,4 +81,5 @@ ndk-build \ APP_ABI="$abi" \ APP_PLATFORM="$platform" \ APP_MODULES="SDL3" \ + APP_SUPPORT_FLEXIBLE_PAGE_SIZES="$flexpage" \ $ndk_args diff --git a/third-party/SDL/cmake/sdlchecks.cmake b/third-party/SDL/cmake/sdlchecks.cmake index a444688a27..d321e7108d 100644 --- a/third-party/SDL/cmake/sdlchecks.cmake +++ b/third-party/SDL/cmake/sdlchecks.cmake @@ -1077,6 +1077,14 @@ endmacro() # Check for HIDAPI support macro(CheckHIDAPI) + if(ANDROID) + enable_language(CXX) + sdl_sources("${SDL3_SOURCE_DIR}/src/hidapi/android/hid.cpp") + endif() + if(IOS OR TVOS) + sdl_sources("${SDL3_SOURCE_DIR}/src/hidapi/ios/hid.m") + set(SDL_FRAMEWORK_COREBLUETOOTH 1) + endif() if(SDL_HIDAPI) set(HAVE_HIDAPI ON) if(SDL_HIDAPI_LIBUSB) @@ -1109,14 +1117,6 @@ macro(CheckHIDAPI) endif() if(HAVE_HIDAPI) - if(ANDROID) - enable_language(CXX) - sdl_sources("${SDL3_SOURCE_DIR}/src/hidapi/android/hid.cpp") - endif() - if(IOS OR TVOS) - sdl_sources("${SDL3_SOURCE_DIR}/src/hidapi/ios/hid.m") - set(SDL_FRAMEWORK_COREBLUETOOTH 1) - endif() set(HAVE_SDL_HIDAPI TRUE) if(SDL_JOYSTICK AND SDL_HIDAPI_JOYSTICK) diff --git a/third-party/SDL/docs/README-macos.md b/third-party/SDL/docs/README-macos.md index fb0639b66e..e5c75c1c81 100644 --- a/third-party/SDL/docs/README-macos.md +++ b/third-party/SDL/docs/README-macos.md @@ -49,7 +49,7 @@ NSApplicationDelegate implementation: ```objc - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender { - if (SDL_GetEventState(SDL_EVENT_QUIT) == SDL_ENABLE) { + if (SDL_EventEnabled(SDL_EVENT_QUIT)) { SDL_Event event; SDL_zero(event); event.type = SDL_EVENT_QUIT; @@ -61,7 +61,7 @@ NSApplicationDelegate implementation: - (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename { - if (SDL_GetEventState(SDL_EVENT_DROP_FILE) == SDL_ENABLE) { + if (SDL_EventEnabled(SDL_EVENT_DROP_FILE)) { SDL_Event event; SDL_zero(event); event.type = SDL_EVENT_DROP_FILE; diff --git a/third-party/SDL/include/SDL3/SDL.h b/third-party/SDL/include/SDL3/SDL.h index ed1b32483a..587b541a45 100644 --- a/third-party/SDL/include/SDL3/SDL.h +++ b/third-party/SDL/include/SDL3/SDL.h @@ -20,7 +20,7 @@ */ /** - * Main include header for the SDL library, version 3.2.16 + * Main include header for the SDL library, version 3.2.20 * * It is almost always best to include just this one header instead of * picking out individual headers included here. There are exceptions to diff --git a/third-party/SDL/include/SDL3/SDL_assert.h b/third-party/SDL/include/SDL3/SDL_assert.h index 6c90acc029..053af13b43 100644 --- a/third-party/SDL/include/SDL3/SDL_assert.h +++ b/third-party/SDL/include/SDL3/SDL_assert.h @@ -362,7 +362,7 @@ extern SDL_DECLSPEC SDL_AssertState SDLCALL SDL_ReportAssertion(SDL_AssertData * #define SDL_enabled_assert(condition) \ do { \ while ( !(condition) ) { \ - static struct SDL_AssertData sdl_assert_data = { 0, 0, #condition, 0, 0, 0, 0 }; \ + static struct SDL_AssertData sdl_assert_data = { false, 0, #condition, NULL, 0, NULL, NULL }; \ const SDL_AssertState sdl_assert_state = SDL_ReportAssertion(&sdl_assert_data, SDL_FUNCTION, SDL_FILE, SDL_LINE); \ if (sdl_assert_state == SDL_ASSERTION_RETRY) { \ continue; /* go again. */ \ diff --git a/third-party/SDL/include/SDL3/SDL_audio.h b/third-party/SDL/include/SDL3/SDL_audio.h index c6acf885f4..51af40e955 100644 --- a/third-party/SDL/include/SDL3/SDL_audio.h +++ b/third-party/SDL/include/SDL3/SDL_audio.h @@ -942,7 +942,10 @@ extern SDL_DECLSPEC void SDLCALL SDL_CloseAudioDevice(SDL_AudioDeviceID devid); * Binding a stream to a device will set its output format for playback * devices, and its input format for recording devices, so they match the * device's settings. The caller is welcome to change the other end of the - * stream's format at any time with SDL_SetAudioStreamFormat(). + * stream's format at any time with SDL_SetAudioStreamFormat(). If the other + * end of the stream's format has never been set (the audio stream was created + * with a NULL audio spec), this function will set it to match the device + * end's format. * * \param devid an audio device to bind a stream to. * \param streams an array of audio streams to bind. @@ -1021,7 +1024,7 @@ extern SDL_DECLSPEC void SDLCALL SDL_UnbindAudioStream(SDL_AudioStream *stream); /** * Query an audio stream for its currently-bound device. * - * This reports the audio device that an audio stream is currently bound to. + * This reports the logical audio device that an audio stream is currently bound to. * * If not bound, or invalid, this returns zero, which is not a valid device * ID. diff --git a/third-party/SDL/include/SDL3/SDL_clipboard.h b/third-party/SDL/include/SDL3/SDL_clipboard.h index 0d3cbb499b..3ca56a48af 100644 --- a/third-party/SDL/include/SDL3/SDL_clipboard.h +++ b/third-party/SDL/include/SDL3/SDL_clipboard.h @@ -106,7 +106,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetClipboardText(const char *text); /** * Get UTF-8 text from the clipboard. * - * This functions returns an empty string if there was not enough memory left + * This function returns an empty string if there is not enough memory left * for a copy of the clipboard's content. * * \returns the clipboard text on success or an empty string on failure; call @@ -155,7 +155,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetPrimarySelectionText(const char *text); /** * Get UTF-8 text from the primary selection. * - * This functions returns an empty string if there was not enough memory left + * This function returns an empty string if there is not enough memory left * for a copy of the primary selection's content. * * \returns the primary selection text on success or an empty string on @@ -194,15 +194,15 @@ extern SDL_DECLSPEC bool SDLCALL SDL_HasPrimarySelectionText(void); * clipboard is cleared or new data is set. The clipboard is automatically * cleared in SDL_Quit(). * - * \param userdata a pointer to provided user data. + * \param userdata a pointer to the provided user data. * \param mime_type the requested mime-type. * \param size a pointer filled in with the length of the returned data. * \returns a pointer to the data for the provided mime-type. Returning NULL - * or setting length to 0 will cause no data to be sent to the + * or setting the length to 0 will cause no data to be sent to the * "receiver". It is up to the receiver to handle this. Essentially * returning no data is more or less undefined behavior and may cause * breakage in receiving applications. The returned data will not be - * freed so it needs to be retained and dealt with internally. + * freed, so it needs to be retained and dealt with internally. * * \since This function is available since SDL 3.2.0. * @@ -211,10 +211,10 @@ extern SDL_DECLSPEC bool SDLCALL SDL_HasPrimarySelectionText(void); typedef const void *(SDLCALL *SDL_ClipboardDataCallback)(void *userdata, const char *mime_type, size_t *size); /** - * Callback function that will be called when the clipboard is cleared, or new + * Callback function that will be called when the clipboard is cleared, or when new * data is set. * - * \param userdata a pointer to provided user data. + * \param userdata a pointer to the provided user data. * * \since This function is available since SDL 3.2.0. * @@ -231,7 +231,7 @@ typedef void (SDLCALL *SDL_ClipboardCleanupCallback)(void *userdata); * respond with the data for the requested mime-type. * * The size of text data does not include any terminator, and the text does - * not need to be null terminated (e.g. you can directly copy a portion of a + * not need to be null-terminated (e.g., you can directly copy a portion of a * document). * * \param callback a function pointer to the function that provides the @@ -239,7 +239,7 @@ typedef void (SDLCALL *SDL_ClipboardCleanupCallback)(void *userdata); * \param cleanup a function pointer to the function that cleans up the * clipboard data. * \param userdata an opaque pointer that will be forwarded to the callbacks. - * \param mime_types a list of mime-types that are being offered. + * \param mime_types a list of mime-types that are being offered. SDL copies the given list. * \param num_mime_types the number of mime-types in the mime_types list. * \returns true on success or false on failure; call SDL_GetError() for more * information. @@ -269,10 +269,10 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetClipboardData(SDL_ClipboardDataCallback extern SDL_DECLSPEC bool SDLCALL SDL_ClearClipboardData(void); /** - * Get the data from clipboard for a given mime type. + * Get the data from the clipboard for a given mime type. * * The size of text data does not include the terminator, but the text is - * guaranteed to be null terminated. + * guaranteed to be null-terminated. * * \param mime_type the mime type to read from the clipboard. * \param size a pointer filled in with the length of the returned data. @@ -292,8 +292,8 @@ extern SDL_DECLSPEC void * SDLCALL SDL_GetClipboardData(const char *mime_type, s /** * Query whether there is data in the clipboard for the provided mime type. * - * \param mime_type the mime type to check for data for. - * \returns true if there exists data in clipboard for the provided mime type, + * \param mime_type the mime type to check for data. + * \returns true if data exists in the clipboard for the provided mime type, * false if it does not. * * \threadsafety This function should only be called on the main thread. @@ -310,7 +310,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_HasClipboardData(const char *mime_type); * * \param num_mime_types a pointer filled with the number of mime types, may * be NULL. - * \returns a null terminated array of strings with mime types, or NULL on + * \returns a null-terminated array of strings with mime types, or NULL on * failure; call SDL_GetError() for more information. This should be * freed with SDL_free() when it is no longer needed. * diff --git a/third-party/SDL/include/SDL3/SDL_endian.h b/third-party/SDL/include/SDL3/SDL_endian.h index a34e9d4b09..2a9b8a34d8 100644 --- a/third-party/SDL/include/SDL3/SDL_endian.h +++ b/third-party/SDL/include/SDL3/SDL_endian.h @@ -486,7 +486,7 @@ SDL_FORCE_INLINE Uint32 SDL_Swap32(Uint32 x) { return x_but_byteswapped; } * * \since This function is available since SDL 3.2.0. */ -SDL_FORCE_INLINE Uint32 SDL_Swap64(Uint64 x) { return x_but_byteswapped; } +SDL_FORCE_INLINE Uint64 SDL_Swap64(Uint64 x) { return x_but_byteswapped; } /** * Swap a 16-bit value from littleendian to native byte order. diff --git a/third-party/SDL/include/SDL3/SDL_gpu.h b/third-party/SDL/include/SDL3/SDL_gpu.h index b616619099..4a5e32f17b 100644 --- a/third-party/SDL/include/SDL3/SDL_gpu.h +++ b/third-party/SDL/include/SDL3/SDL_gpu.h @@ -1549,7 +1549,7 @@ typedef struct SDL_GPUSamplerCreateInfo typedef struct SDL_GPUVertexBufferDescription { Uint32 slot; /**< The binding slot of the vertex buffer. */ - Uint32 pitch; /**< The byte pitch between consecutive elements of the vertex buffer. */ + Uint32 pitch; /**< The size of a single element + the offset between elements. */ SDL_GPUVertexInputRate input_rate; /**< Whether attribute addressing is a function of the vertex index or instance index. */ Uint32 instance_step_rate; /**< Reserved for future use. Must be set to 0. */ } SDL_GPUVertexBufferDescription; diff --git a/third-party/SDL/include/SDL3/SDL_scancode.h b/third-party/SDL/include/SDL3/SDL_scancode.h index 9650a6cfe3..6e9be47e6a 100644 --- a/third-party/SDL/include/SDL3/SDL_scancode.h +++ b/third-party/SDL/include/SDL3/SDL_scancode.h @@ -208,7 +208,7 @@ typedef enum SDL_Scancode SDL_SCANCODE_NONUSBACKSLASH = 100, /**< This is the additional key that ISO * keyboards have over ANSI ones, - * located between left shift and Y. + * located between left shift and Z. * Produces GRAVE ACCENT and TILDE in a * US or UK Mac layout, REVERSE SOLIDUS * (backslash) and VERTICAL LINE in a diff --git a/third-party/SDL/include/SDL3/SDL_version.h b/third-party/SDL/include/SDL3/SDL_version.h index 435b3f95fe..7443d52987 100644 --- a/third-party/SDL/include/SDL3/SDL_version.h +++ b/third-party/SDL/include/SDL3/SDL_version.h @@ -62,7 +62,7 @@ extern "C" { * * \since This macro is available since SDL 3.2.0. */ -#define SDL_MICRO_VERSION 16 +#define SDL_MICRO_VERSION 20 /** * This macro turns the version numbers into a numeric value. diff --git a/third-party/SDL/include/SDL3/SDL_video.h b/third-party/SDL/include/SDL3/SDL_video.h index 877b9adde5..3b3676d14e 100644 --- a/third-party/SDL/include/SDL3/SDL_video.h +++ b/third-party/SDL/include/SDL3/SDL_video.h @@ -1167,6 +1167,15 @@ extern SDL_DECLSPEC SDL_Window * SDLCALL SDL_CreateWindow(const char *title, int * Popup windows implicitly do not have a border/decorations and do not appear * on the taskbar/dock or in lists of windows such as alt-tab menus. * + * By default, popup window positions will automatically be constrained to keep + * the entire window within display bounds. This can be overridden with the + * `SDL_PROP_WINDOW_CREATE_CONSTRAIN_POPUP_BOOLEAN` property. + * + * By default, popup menus will automatically grab keyboard focus from the parent + * when shown. This behavior can be overridden by setting the `SDL_WINDOW_NOT_FOCUSABLE` + * flag, setting the `SDL_PROP_WINDOW_CREATE_FOCUSABLE_BOOLEAN` property to false, or + * toggling it after creation via the `SDL_SetWindowFocusable()` function. + * * If a parent window is hidden or destroyed, any child popup windows will be * recursively hidden or destroyed as well. Child popup windows not explicitly * hidden will be restored when the parent is shown. @@ -1207,6 +1216,9 @@ extern SDL_DECLSPEC SDL_Window * SDLCALL SDL_CreatePopupWindow(SDL_Window *paren * be always on top * - `SDL_PROP_WINDOW_CREATE_BORDERLESS_BOOLEAN`: true if the window has no * window decoration + * - `SDL_PROP_WINDOW_CREATE_CONSTRAIN_POPUP_BOOLEAN`: true if the "tooltip" and + * "menu" window types should be automatically constrained to be entirely within + * display bounds (default), false if no constraints on the position are desired. * - `SDL_PROP_WINDOW_CREATE_EXTERNAL_GRAPHICS_CONTEXT_BOOLEAN`: true if the * window will be used with an externally managed graphics context. * - `SDL_PROP_WINDOW_CREATE_FOCUSABLE_BOOLEAN`: true if the window should @@ -1321,6 +1333,7 @@ extern SDL_DECLSPEC SDL_Window * SDLCALL SDL_CreateWindowWithProperties(SDL_Prop #define SDL_PROP_WINDOW_CREATE_ALWAYS_ON_TOP_BOOLEAN "SDL.window.create.always_on_top" #define SDL_PROP_WINDOW_CREATE_BORDERLESS_BOOLEAN "SDL.window.create.borderless" +#define SDL_PROP_WINDOW_CREATE_CONSTRAIN_POPUP_BOOLEAN "SDL.window.create.constrain_popup" #define SDL_PROP_WINDOW_CREATE_FOCUSABLE_BOOLEAN "SDL.window.create.focusable" #define SDL_PROP_WINDOW_CREATE_EXTERNAL_GRAPHICS_CONTEXT_BOOLEAN "SDL.window.create.external_graphics_context" #define SDL_PROP_WINDOW_CREATE_FLAGS_NUMBER "SDL.window.create.flags" diff --git a/third-party/SDL/include/SDL3/SDL_vulkan.h b/third-party/SDL/include/SDL3/SDL_vulkan.h index 710afbe631..e91e148428 100644 --- a/third-party/SDL/include/SDL3/SDL_vulkan.h +++ b/third-party/SDL/include/SDL3/SDL_vulkan.h @@ -51,14 +51,14 @@ extern "C" { #endif -/* Avoid including vulkan.h, don't define VkInstance if it's already included */ -#ifdef VULKAN_H_ +/* Avoid including vulkan_core.h, don't define VkInstance if it's already included */ +#ifdef VULKAN_CORE_H_ #define NO_SDL_VULKAN_TYPEDEFS #endif #ifndef NO_SDL_VULKAN_TYPEDEFS #define VK_DEFINE_HANDLE(object) typedef struct object##_T* object; -#if defined(__LP64__) || defined(_WIN64) || defined(__x86_64__) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__) +#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__) || (defined(__riscv) && __riscv_xlen == 64) #define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef struct object##_T *object; #else #define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef uint64_t object; diff --git a/third-party/SDL/include/build_config/SDL_build_config_windows.h b/third-party/SDL/include/build_config/SDL_build_config_windows.h index 4427517641..98079cb178 100644 --- a/third-party/SDL/include/build_config/SDL_build_config_windows.h +++ b/third-party/SDL/include/build_config/SDL_build_config_windows.h @@ -111,6 +111,9 @@ typedef unsigned int uintptr_t; # define SDL_DISABLE_AVX 1 #endif +#define HAVE_STDARG_H 1 +#define HAVE_STDDEF_H 1 + /* This can be disabled to avoid C runtime dependencies and manifest requirements */ #ifndef HAVE_LIBC #define HAVE_LIBC 1 @@ -122,8 +125,6 @@ typedef unsigned int uintptr_t; #define HAVE_LIMITS_H 1 #define HAVE_MATH_H 1 #define HAVE_SIGNAL_H 1 -#define HAVE_STDARG_H 1 -#define HAVE_STDDEF_H 1 #define HAVE_STDIO_H 1 #define HAVE_STDLIB_H 1 #define HAVE_STRING_H 1 @@ -153,7 +154,6 @@ typedef unsigned int uintptr_t; #define HAVE_STRCMP 1 #define HAVE_STRNCMP 1 #define HAVE_STRPBRK 1 -#define HAVE_VSSCANF 1 #define HAVE_VSNPRINTF 1 #define HAVE_ACOS 1 #define HAVE_ASIN 1 @@ -211,10 +211,7 @@ typedef unsigned int uintptr_t; #if _MSC_VER >= 1400 #define HAVE__FSEEKI64 1 #endif -#endif /* _MSC_VER */ -#else -#define HAVE_STDARG_H 1 -#define HAVE_STDDEF_H 1 +#endif /* _MSC_VER */ #endif /* Enable various audio drivers */ diff --git a/third-party/SDL/src/SDL.c b/third-party/SDL/src/SDL.c index 502f6617a4..46a74aafdb 100644 --- a/third-party/SDL/src/SDL.c +++ b/third-party/SDL/src/SDL.c @@ -65,7 +65,7 @@ // Initialization/Cleanup routines #include "timer/SDL_timer_c.h" -#ifdef SDL_VIDEO_DRIVER_WINDOWS +#ifdef SDL_PLATFORM_WINDOWS extern bool SDL_HelperWindowCreate(void); extern void SDL_HelperWindowDestroy(void); #endif @@ -317,7 +317,7 @@ bool SDL_InitSubSystem(SDL_InitFlags flags) SDL_DBus_Init(); #endif -#ifdef SDL_VIDEO_DRIVER_WINDOWS +#ifdef SDL_PLATFORM_WINDOWS if (flags & (SDL_INIT_HAPTIC | SDL_INIT_JOYSTICK)) { if (!SDL_HelperWindowCreate()) { goto quit_and_error; @@ -653,7 +653,7 @@ void SDL_Quit(void) SDL_bInMainQuit = true; // Quit all subsystems -#ifdef SDL_VIDEO_DRIVER_WINDOWS +#ifdef SDL_PLATFORM_WINDOWS SDL_HelperWindowDestroy(); #endif SDL_QuitSubSystem(SDL_INIT_EVERYTHING); diff --git a/third-party/SDL/src/audio/SDL_audio.c b/third-party/SDL/src/audio/SDL_audio.c index fbe6e22a39..5e3e1fb8f0 100644 --- a/third-party/SDL/src/audio/SDL_audio.c +++ b/third-party/SDL/src/audio/SDL_audio.c @@ -1153,7 +1153,10 @@ bool SDL_PlaybackAudioThreadIterate(SDL_AudioDevice *device) // We should have updated this elsewhere if the format changed! SDL_assert(SDL_AudioSpecsEqual(&stream->dst_spec, &device->spec, NULL, NULL)); + SDL_assert(stream->src_spec.format != SDL_AUDIO_UNKNOWN); + const int br = SDL_GetAtomicInt(&logdev->paused) ? 0 : SDL_GetAudioStreamDataAdjustGain(stream, device_buffer, buffer_size, logdev->gain); + if (br < 0) { // Probably OOM. Kill the audio device; the whole thing is likely dying soon anyhow. failed = true; SDL_memset(device_buffer, device->silence_value, buffer_size); // just supply silence to the device before we die. @@ -1195,6 +1198,8 @@ bool SDL_PlaybackAudioThreadIterate(SDL_AudioDevice *device) // We should have updated this elsewhere if the format changed! SDL_assert(SDL_AudioSpecsEqual(&stream->dst_spec, &outspec, NULL, NULL)); + SDL_assert(stream->src_spec.format != SDL_AUDIO_UNKNOWN); + /* this will hold a lock on `stream` while getting. We don't explicitly lock the streams for iterating here because the binding linked list can only change while the device lock is held. (we _do_ lock the stream during binding/unbinding to make sure that two threads can't try to bind @@ -1330,6 +1335,7 @@ bool SDL_RecordingAudioThreadIterate(SDL_AudioDevice *device) SDL_assert(stream->src_spec.format == ((logdev->postmix || (logdev->gain != 1.0f)) ? SDL_AUDIO_F32 : device->spec.format)); SDL_assert(stream->src_spec.channels == device->spec.channels); SDL_assert(stream->src_spec.freq == device->spec.freq); + SDL_assert(stream->dst_spec.format != SDL_AUDIO_UNKNOWN); void *final_buf = output_buffer; @@ -1391,6 +1397,7 @@ static int SDLCALL RecordingAudioThread(void *devicep) // thread entry point typedef struct CountAudioDevicesData { int devs_seen; + int devs_skipped; const int num_devices; SDL_AudioDeviceID *result; const bool recording; @@ -1406,7 +1413,13 @@ static bool SDLCALL CountAudioDevices(void *userdata, const SDL_HashTable *table const bool isphysical = !!(devid & (1<<1)); if (isphysical && (devid_recording == data->recording)) { SDL_assert(data->devs_seen < data->num_devices); - data->result[data->devs_seen++] = devid; + SDL_AudioDevice *device = (SDL_AudioDevice *) value; // this is normally risky, but we hold the device_hash_lock here. + const bool zombie = SDL_GetAtomicInt(&device->zombie) != 0; + if (zombie) { + data->devs_skipped++; + } else { + data->result[data->devs_seen++] = devid; + } } return true; // keep iterating. } @@ -1422,10 +1435,11 @@ static SDL_AudioDeviceID *GetAudioDevices(int *count, bool recording) num_devices = SDL_GetAtomicInt(recording ? ¤t_audio.recording_device_count : ¤t_audio.playback_device_count); result = (SDL_AudioDeviceID *) SDL_malloc((num_devices + 1) * sizeof (SDL_AudioDeviceID)); if (result) { - CountAudioDevicesData data = { 0, num_devices, result, recording }; + CountAudioDevicesData data = { 0, 0, num_devices, result, recording }; SDL_IterateHashTable(current_audio.device_hash, CountAudioDevices, &data); - SDL_assert(data.devs_seen == num_devices); - result[data.devs_seen] = 0; // null-terminated. + SDL_assert((data.devs_seen + data.devs_skipped) == num_devices); + num_devices = data.devs_seen; // might be less if we skipped any. + result[num_devices] = 0; // null-terminated. } } SDL_UnlockRWLock(current_audio.device_hash_lock); @@ -1567,7 +1581,9 @@ int *SDL_GetAudioDeviceChannelMap(SDL_AudioDeviceID devid, int *count) SDL_AudioDevice *device = ObtainPhysicalAudioDeviceDefaultAllowed(devid); if (device) { channels = device->spec.channels; - result = SDL_ChannelMapDup(device->chmap, channels); + if (channels > 0 && device->chmap) { + result = SDL_ChannelMapDup(device->chmap, channels); + } } ReleaseAudioDevice(device); @@ -1965,10 +1981,6 @@ bool SDL_BindAudioStreams(SDL_AudioDeviceID devid, SDL_AudioStream * const *stre } else if (logdev->simplified) { result = SDL_SetError("Cannot change stream bindings on device opened with SDL_OpenAudioDeviceStream"); } else { - - // !!! FIXME: We'll set the device's side's format below, but maybe we should refuse to bind a stream if the app's side doesn't have a format set yet. - // !!! FIXME: Actually, why do we allow there to be an invalid format, again? - // make sure start of list is sane. SDL_assert(!logdev->bound_streams || (logdev->bound_streams->prev_binding == NULL)); @@ -2003,9 +2015,17 @@ bool SDL_BindAudioStreams(SDL_AudioDeviceID devid, SDL_AudioStream * const *stre if (result) { // Now that everything is verified, chain everything together. + const bool recording = device->recording; for (int i = 0; i < num_streams; i++) { SDL_AudioStream *stream = streams[i]; if (stream) { // shouldn't be NULL, but just in case... + // if the stream never had its non-device-end format set, just set it to the device end's format. + if (recording && (stream->dst_spec.format == SDL_AUDIO_UNKNOWN)) { + SDL_copyp(&stream->dst_spec, &device->spec); + } else if (!recording && (stream->src_spec.format == SDL_AUDIO_UNKNOWN)) { + SDL_copyp(&stream->src_spec, &device->spec); + } + stream->bound_device = logdev; stream->prev_binding = NULL; stream->next_binding = logdev->bound_streams; diff --git a/third-party/SDL/src/audio/SDL_audiotypecvt.c b/third-party/SDL/src/audio/SDL_audiotypecvt.c index a27575f239..b3a7bbca2a 100644 --- a/third-party/SDL/src/audio/SDL_audiotypecvt.c +++ b/third-party/SDL/src/audio/SDL_audiotypecvt.c @@ -537,8 +537,10 @@ static void SDL_TARGETING("ssse3") SDL_Convert_Swap32_SSSE3(Uint32* dst, const U // behavior. However, the compiler support for this pragma is bad. #if defined(__clang__) #if __clang_major__ >= 12 +#if defined(__aarch64__) #pragma STDC FENV_ACCESS ON #endif +#endif #elif defined(_MSC_VER) #pragma fenv_access (on) #elif defined(__GNUC__) @@ -814,8 +816,10 @@ static void SDL_Convert_Swap32_NEON(Uint32* dst, const Uint32* src, int num_samp #if defined(__clang__) #if __clang_major__ >= 12 +#if defined(__aarch64__) #pragma STDC FENV_ACCESS DEFAULT #endif +#endif #elif defined(_MSC_VER) #pragma fenv_access (off) #elif defined(__GNUC__) diff --git a/third-party/SDL/src/audio/SDL_wave.c b/third-party/SDL/src/audio/SDL_wave.c index 1d53e79d16..826b652476 100644 --- a/third-party/SDL/src/audio/SDL_wave.c +++ b/third-party/SDL/src/audio/SDL_wave.c @@ -2114,8 +2114,8 @@ bool SDL_LoadWAV_IO(SDL_IOStream *src, bool closeio, SDL_AudioSpec *spec, Uint8 result = WaveLoad(src, &file, spec, audio_buf, audio_len); if (!result) { SDL_free(*audio_buf); - audio_buf = NULL; - audio_len = 0; + *audio_buf = NULL; + *audio_len = 0; } // Cleanup diff --git a/third-party/SDL/src/audio/alsa/SDL_alsa_audio.c b/third-party/SDL/src/audio/alsa/SDL_alsa_audio.c index badbc43ec8..cb8c907c0f 100644 --- a/third-party/SDL/src/audio/alsa/SDL_alsa_audio.c +++ b/third-party/SDL/src/audio/alsa/SDL_alsa_audio.c @@ -1156,7 +1156,7 @@ static bool ALSA_OpenDevice(SDL_AudioDevice *device) #if SDL_ALSA_DEBUG snd_pcm_uframes_t bufsize; ALSA_snd_pcm_hw_params_get_buffer_size(cfg_ctx.hwparams, &bufsize); - SDL_LogError(SDL_LOG_CATEGORY_AUDIO, + SDL_LogDebug(SDL_LOG_CATEGORY_AUDIO, "ALSA: period size = %ld, periods = %u, buffer size = %lu", cfg_ctx.persize, cfg_ctx.periods, bufsize); #endif diff --git a/third-party/SDL/src/audio/directsound/SDL_directsound.c b/third-party/SDL/src/audio/directsound/SDL_directsound.c index 7b5cb11ebd..ab4dd0cc53 100644 --- a/third-party/SDL/src/audio/directsound/SDL_directsound.c +++ b/third-party/SDL/src/audio/directsound/SDL_directsound.c @@ -206,7 +206,7 @@ static void DSOUND_DetectDevices(SDL_AudioDevice **default_playback, SDL_AudioDe { #ifdef HAVE_MMDEVICEAPI_H if (SupportsIMMDevice) { - SDL_IMMDevice_EnumerateEndpoints(default_playback, default_recording); + SDL_IMMDevice_EnumerateEndpoints(default_playback, default_recording, SDL_AUDIO_UNKNOWN); } else #endif { diff --git a/third-party/SDL/src/audio/psp/SDL_pspaudio.c b/third-party/SDL/src/audio/psp/SDL_pspaudio.c index 18546e88fc..1b095e97f2 100644 --- a/third-party/SDL/src/audio/psp/SDL_pspaudio.c +++ b/third-party/SDL/src/audio/psp/SDL_pspaudio.c @@ -114,7 +114,7 @@ static bool PSPAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, in } else { rc = sceAudioOutputPannedBlocking(device->hidden->channel, PSP_AUDIO_VOLUME_MAX, PSP_AUDIO_VOLUME_MAX, (void *) buffer); } - return (rc == 0); + return (rc >= 0); } static bool PSPAUDIO_WaitDevice(SDL_AudioDevice *device) diff --git a/third-party/SDL/src/audio/wasapi/SDL_wasapi.c b/third-party/SDL/src/audio/wasapi/SDL_wasapi.c index db0974b0ea..2ab2dc3323 100644 --- a/third-party/SDL/src/audio/wasapi/SDL_wasapi.c +++ b/third-party/SDL/src/audio/wasapi/SDL_wasapi.c @@ -337,7 +337,7 @@ typedef struct static bool mgmtthrtask_DetectDevices(void *userdata) { mgmtthrtask_DetectDevicesData *data = (mgmtthrtask_DetectDevicesData *)userdata; - SDL_IMMDevice_EnumerateEndpoints(data->default_playback, data->default_recording); + SDL_IMMDevice_EnumerateEndpoints(data->default_playback, data->default_recording, SDL_AUDIO_F32); return true; } diff --git a/third-party/SDL/src/camera/android/SDL_camera_android.c b/third-party/SDL/src/camera/android/SDL_camera_android.c index 54b539a126..97c7c4d91f 100644 --- a/third-party/SDL/src/camera/android/SDL_camera_android.c +++ b/third-party/SDL/src/camera/android/SDL_camera_android.c @@ -814,7 +814,7 @@ static void ANDROIDCAMERA_Deinitialize(void) static bool ANDROIDCAMERA_Init(SDL_CameraDriverImpl *impl) { // !!! FIXME: slide this off into a subroutine - // system libraries are in android-24 and later; we currently target android-16 and later, so check if they exist at runtime. + // system libraries are in android-24 and later; we currently target android-21 and later, so check if they exist at runtime. void *libcamera2 = dlopen("libcamera2ndk.so", RTLD_NOW | RTLD_LOCAL); if (!libcamera2) { SDL_Log("CAMERA: libcamera2ndk.so can't be loaded: %s", dlerror()); diff --git a/third-party/SDL/src/core/gdk/SDL_gdk.cpp b/third-party/SDL/src/core/gdk/SDL_gdk.cpp index 738daa5b78..4e792ef270 100644 --- a/third-party/SDL/src/core/gdk/SDL_gdk.cpp +++ b/third-party/SDL/src/core/gdk/SDL_gdk.cpp @@ -105,7 +105,7 @@ bool GDK_RegisterChangeNotifications(void) SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "[GDK] in RegisterAppConstrainedChangeNotification handler"); SDL_VideoDevice *_this = SDL_GetVideoDevice(); if (_this) { - if (constrained) { + if (constrained && !((_this->windows) && _this->windows->text_input_active)) { SDL_SetKeyboardFocus(NULL); } else { SDL_SetKeyboardFocus(_this->windows); diff --git a/third-party/SDL/src/core/windows/SDL_immdevice.c b/third-party/SDL/src/core/windows/SDL_immdevice.c index 802a412e15..cc6945b1bc 100644 --- a/third-party/SDL/src/core/windows/SDL_immdevice.c +++ b/third-party/SDL/src/core/windows/SDL_immdevice.c @@ -120,7 +120,7 @@ void SDL_IMMDevice_FreeDeviceHandle(SDL_AudioDevice *device) } } -static SDL_AudioDevice *SDL_IMMDevice_Add(const bool recording, const char *devname, WAVEFORMATEXTENSIBLE *fmt, LPCWSTR devid, GUID *dsoundguid) +static SDL_AudioDevice *SDL_IMMDevice_Add(const bool recording, const char *devname, WAVEFORMATEXTENSIBLE *fmt, LPCWSTR devid, GUID *dsoundguid, SDL_AudioFormat force_format) { /* You can have multiple endpoints on a device that are mutually exclusive ("Speakers" vs "Line Out" or whatever). In a perfect world, things that are unplugged won't be in this collection. The only gotcha is probably for @@ -162,7 +162,7 @@ static SDL_AudioDevice *SDL_IMMDevice_Add(const bool recording, const char *devn SDL_zero(spec); spec.channels = (Uint8)fmt->Format.nChannels; spec.freq = fmt->Format.nSamplesPerSec; - spec.format = SDL_WaveFormatExToSDLFormat((WAVEFORMATEX *)fmt); + spec.format = (force_format != SDL_AUDIO_UNKNOWN) ? force_format : SDL_WaveFormatExToSDLFormat((WAVEFORMATEX *)fmt); device = SDL_AddAudioDevice(recording, devname, &spec, handle); if (!device) { @@ -183,6 +183,7 @@ typedef struct SDLMMNotificationClient { const IMMNotificationClientVtbl *lpVtbl; SDL_AtomicInt refcount; + SDL_AudioFormat force_format; } SDLMMNotificationClient; static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_QueryInterface(IMMNotificationClient *client, REFIID iid, void **ppv) @@ -241,6 +242,7 @@ static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDeviceRemoved(IMMNoti static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDeviceStateChanged(IMMNotificationClient *iclient, LPCWSTR pwstrDeviceId, DWORD dwNewState) { + SDLMMNotificationClient *client = (SDLMMNotificationClient *)iclient; IMMDevice *device = NULL; if (SUCCEEDED(IMMDeviceEnumerator_GetDevice(enumerator, pwstrDeviceId, &device))) { @@ -255,7 +257,7 @@ static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDeviceStateChanged(IM GUID dsoundguid; GetMMDeviceInfo(device, &utf8dev, &fmt, &dsoundguid); if (utf8dev) { - SDL_IMMDevice_Add(recording, utf8dev, &fmt, pwstrDeviceId, &dsoundguid); + SDL_IMMDevice_Add(recording, utf8dev, &fmt, pwstrDeviceId, &dsoundguid, client->force_format); SDL_free(utf8dev); } } else { @@ -286,7 +288,7 @@ static const IMMNotificationClientVtbl notification_client_vtbl = { SDLMMNotificationClient_OnPropertyValueChanged }; -static SDLMMNotificationClient notification_client = { ¬ification_client_vtbl, { 1 } }; +static SDLMMNotificationClient notification_client = { ¬ification_client_vtbl, { 1 }, SDL_AUDIO_UNKNOWN }; bool SDL_IMMDevice_Init(const SDL_IMMDevice_callbacks *callbacks) { @@ -363,7 +365,7 @@ bool SDL_IMMDevice_Get(SDL_AudioDevice *device, IMMDevice **immdevice, bool reco return true; } -static void EnumerateEndpointsForFlow(const bool recording, SDL_AudioDevice **default_device) +static void EnumerateEndpointsForFlow(const bool recording, SDL_AudioDevice **default_device, SDL_AudioFormat force_format) { /* Note that WASAPI separates "adapter devices" from "audio endpoint devices" ...one adapter device ("SoundBlaster Pro") might have multiple endpoint devices ("Speakers", "Line-Out"). */ @@ -405,7 +407,7 @@ static void EnumerateEndpointsForFlow(const bool recording, SDL_AudioDevice **de SDL_zero(dsoundguid); GetMMDeviceInfo(immdevice, &devname, &fmt, &dsoundguid); if (devname) { - SDL_AudioDevice *sdldevice = SDL_IMMDevice_Add(recording, devname, &fmt, devid, &dsoundguid); + SDL_AudioDevice *sdldevice = SDL_IMMDevice_Add(recording, devname, &fmt, devid, &dsoundguid, force_format); if (default_device && default_devid && SDL_wcscmp(default_devid, devid) == 0) { *default_device = sdldevice; } @@ -422,10 +424,12 @@ static void EnumerateEndpointsForFlow(const bool recording, SDL_AudioDevice **de IMMDeviceCollection_Release(collection); } -void SDL_IMMDevice_EnumerateEndpoints(SDL_AudioDevice **default_playback, SDL_AudioDevice **default_recording) +void SDL_IMMDevice_EnumerateEndpoints(SDL_AudioDevice **default_playback, SDL_AudioDevice **default_recording, SDL_AudioFormat force_format) { - EnumerateEndpointsForFlow(false, default_playback); - EnumerateEndpointsForFlow(true, default_recording); + EnumerateEndpointsForFlow(false, default_playback, force_format); + EnumerateEndpointsForFlow(true, default_recording, force_format); + + notification_client.force_format = force_format; // if this fails, we just won't get hotplug events. Carry on anyhow. IMMDeviceEnumerator_RegisterEndpointNotificationCallback(enumerator, (IMMNotificationClient *)¬ification_client); diff --git a/third-party/SDL/src/core/windows/SDL_immdevice.h b/third-party/SDL/src/core/windows/SDL_immdevice.h index 66fdf13b81..0582bc0ded 100644 --- a/third-party/SDL/src/core/windows/SDL_immdevice.h +++ b/third-party/SDL/src/core/windows/SDL_immdevice.h @@ -37,7 +37,7 @@ typedef struct SDL_IMMDevice_callbacks bool SDL_IMMDevice_Init(const SDL_IMMDevice_callbacks *callbacks); void SDL_IMMDevice_Quit(void); bool SDL_IMMDevice_Get(struct SDL_AudioDevice *device, IMMDevice **immdevice, bool recording); -void SDL_IMMDevice_EnumerateEndpoints(struct SDL_AudioDevice **default_playback, struct SDL_AudioDevice **default_recording); +void SDL_IMMDevice_EnumerateEndpoints(struct SDL_AudioDevice **default_playback, struct SDL_AudioDevice **default_recording, SDL_AudioFormat force_format); LPGUID SDL_IMMDevice_GetDirectSoundGUID(struct SDL_AudioDevice *device); LPCWSTR SDL_IMMDevice_GetDevID(struct SDL_AudioDevice *device); void SDL_IMMDevice_FreeDeviceHandle(struct SDL_AudioDevice *device); diff --git a/third-party/SDL/src/core/windows/SDL_windows.c b/third-party/SDL/src/core/windows/SDL_windows.c index 3259e787d4..163635adcc 100644 --- a/third-party/SDL/src/core/windows/SDL_windows.c +++ b/third-party/SDL/src/core/windows/SDL_windows.c @@ -53,6 +53,78 @@ typedef enum RO_INIT_TYPE #define WC_ERR_INVALID_CHARS 0x00000080 #endif +// Fake window to help with DirectInput events. +HWND SDL_HelperWindow = NULL; +static const TCHAR *SDL_HelperWindowClassName = TEXT("SDLHelperWindowInputCatcher"); +static const TCHAR *SDL_HelperWindowName = TEXT("SDLHelperWindowInputMsgWindow"); +static ATOM SDL_HelperWindowClass = 0; + +/* + * Creates a HelperWindow used for DirectInput. + */ +bool SDL_HelperWindowCreate(void) +{ + HINSTANCE hInstance = GetModuleHandle(NULL); + WNDCLASS wce; + + // Make sure window isn't created twice. + if (SDL_HelperWindow != NULL) { + return true; + } + + // Create the class. + SDL_zero(wce); + wce.lpfnWndProc = DefWindowProc; + wce.lpszClassName = SDL_HelperWindowClassName; + wce.hInstance = hInstance; + + // Register the class. + SDL_HelperWindowClass = RegisterClass(&wce); + if (SDL_HelperWindowClass == 0 && GetLastError() != ERROR_CLASS_ALREADY_EXISTS) { + return WIN_SetError("Unable to create Helper Window Class"); + } + + // Create the window. + SDL_HelperWindow = CreateWindowEx(0, SDL_HelperWindowClassName, + SDL_HelperWindowName, + WS_OVERLAPPED, CW_USEDEFAULT, + CW_USEDEFAULT, CW_USEDEFAULT, + CW_USEDEFAULT, HWND_MESSAGE, NULL, + hInstance, NULL); + if (!SDL_HelperWindow) { + UnregisterClass(SDL_HelperWindowClassName, hInstance); + return WIN_SetError("Unable to create Helper Window"); + } + + return true; +} + +/* + * Destroys the HelperWindow previously created with SDL_HelperWindowCreate. + */ +void SDL_HelperWindowDestroy(void) +{ + HINSTANCE hInstance = GetModuleHandle(NULL); + + // Destroy the window. + if (SDL_HelperWindow != NULL) { + if (DestroyWindow(SDL_HelperWindow) == 0) { + WIN_SetError("Unable to destroy Helper Window"); + return; + } + SDL_HelperWindow = NULL; + } + + // Unregister the class. + if (SDL_HelperWindowClass != 0) { + if ((UnregisterClass(SDL_HelperWindowClassName, hInstance)) == 0) { + WIN_SetError("Unable to destroy Helper Window Class"); + return; + } + SDL_HelperWindowClass = 0; + } +} + // Sets an error message based on an HRESULT bool WIN_SetErrorFromHRESULT(const char *prefix, HRESULT hr) { diff --git a/third-party/SDL/src/core/windows/version.rc b/third-party/SDL/src/core/windows/version.rc index 3492918f5e..568e83292e 100644 --- a/third-party/SDL/src/core/windows/version.rc +++ b/third-party/SDL/src/core/windows/version.rc @@ -9,8 +9,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // VS_VERSION_INFO VERSIONINFO - FILEVERSION 3,2,16,0 - PRODUCTVERSION 3,2,16,0 + FILEVERSION 3,2,20,0 + PRODUCTVERSION 3,2,20,0 FILEFLAGSMASK 0x3fL FILEFLAGS 0x0L FILEOS 0x40004L @@ -23,12 +23,12 @@ BEGIN BEGIN VALUE "CompanyName", "\0" VALUE "FileDescription", "SDL\0" - VALUE "FileVersion", "3, 2, 16, 0\0" + VALUE "FileVersion", "3, 2, 20, 0\0" VALUE "InternalName", "SDL\0" VALUE "LegalCopyright", "Copyright (C) 2025 Sam Lantinga\0" VALUE "OriginalFilename", "SDL3.dll\0" VALUE "ProductName", "Simple DirectMedia Layer\0" - VALUE "ProductVersion", "3, 2, 16, 0\0" + VALUE "ProductVersion", "3, 2, 20, 0\0" END END BLOCK "VarFileInfo" diff --git a/third-party/SDL/src/dialog/cocoa/SDL_cocoadialog.m b/third-party/SDL/src/dialog/cocoa/SDL_cocoadialog.m index 64b2fdf978..bc806f52a8 100644 --- a/third-party/SDL/src/dialog/cocoa/SDL_cocoadialog.m +++ b/third-party/SDL/src/dialog/cocoa/SDL_cocoadialog.m @@ -27,6 +27,8 @@ #import #import +extern void Cocoa_SetWindowHasModalDialog(SDL_Window *window, bool has_modal); + static void AddFileExtensionType(NSMutableArray *types, const char *pattern_ptr) { if (!*pattern_ptr) { @@ -163,6 +165,9 @@ void SDL_SYS_ShowFileDialogWithProperties(SDL_FileDialogType type, SDL_DialogFil if (window) { w = (__bridge NSWindow *)SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_COCOA_WINDOW_POINTER, NULL); + if (w) { + Cocoa_SetWindowHasModalDialog(window, true); + } } if (w) { @@ -186,6 +191,7 @@ void SDL_SYS_ShowFileDialogWithProperties(SDL_FileDialogType type, SDL_DialogFil callback(userdata, files, -1); } + Cocoa_SetWindowHasModalDialog(window, false); ReactivateAfterDialog(); }]; } else { @@ -206,6 +212,7 @@ void SDL_SYS_ShowFileDialogWithProperties(SDL_FileDialogType type, SDL_DialogFil const char *files[1] = { NULL }; callback(userdata, files, -1); } + ReactivateAfterDialog(); } } diff --git a/third-party/SDL/src/events/scancodes_xfree86.h b/third-party/SDL/src/events/scancodes_xfree86.h index 5e51bb17c4..81d625e8ec 100644 --- a/third-party/SDL/src/events/scancodes_xfree86.h +++ b/third-party/SDL/src/events/scancodes_xfree86.h @@ -371,10 +371,10 @@ static const SDL_Scancode xfree86_scancode_table2[] = { /* 188, 0x0bc */ SDL_SCANCODE_F18, // XF86Launch9 /* 189, 0x0bd */ SDL_SCANCODE_F19, // NoSymbol /* 190, 0x0be */ SDL_SCANCODE_F20, // XF86AudioMicMute - /* 191, 0x0bf */ SDL_SCANCODE_UNKNOWN, // XF86TouchpadToggle - /* 192, 0x0c0 */ SDL_SCANCODE_UNKNOWN, // XF86TouchpadOn - /* 193, 0x0c1 */ SDL_SCANCODE_UNKNOWN, // XF86TouchpadOff - /* 194, 0x0c2 */ SDL_SCANCODE_UNKNOWN, // NoSymbol + /* 191, 0x0bf */ SDL_SCANCODE_F21, // XF86TouchpadToggle + /* 192, 0x0c0 */ SDL_SCANCODE_F22, // XF86TouchpadOn + /* 193, 0x0c1 */ SDL_SCANCODE_F23, // XF86TouchpadOff + /* 194, 0x0c2 */ SDL_SCANCODE_F24, // NoSymbol /* 195, 0x0c3 */ SDL_SCANCODE_MODE, // Mode_switch /* 196, 0x0c4 */ SDL_SCANCODE_UNKNOWN, // NoSymbol /* 197, 0x0c5 */ SDL_SCANCODE_UNKNOWN, // NoSymbol diff --git a/third-party/SDL/src/gpu/SDL_gpu.c b/third-party/SDL/src/gpu/SDL_gpu.c index e1ad66c00b..aea7ed5c97 100644 --- a/third-party/SDL/src/gpu/SDL_gpu.c +++ b/third-party/SDL/src/gpu/SDL_gpu.c @@ -1573,6 +1573,7 @@ SDL_GPUCommandBuffer *SDL_AcquireGPUCommandBuffer( commandBufferHeader->copy_pass.in_progress = false; commandBufferHeader->swapchain_texture_acquired = false; commandBufferHeader->submitted = false; + commandBufferHeader->ignore_render_pass_texture_validation = false; SDL_zeroa(commandBufferHeader->render_pass.vertex_sampler_bound); SDL_zeroa(commandBufferHeader->render_pass.vertex_storage_texture_bound); SDL_zeroa(commandBufferHeader->render_pass.vertex_storage_buffer_bound); @@ -2247,14 +2248,14 @@ void SDL_DrawGPUIndexedPrimitivesIndirect( void SDL_EndGPURenderPass( SDL_GPURenderPass *render_pass) { - CommandBufferCommonHeader *commandBufferCommonHeader; - commandBufferCommonHeader = (CommandBufferCommonHeader *)RENDERPASS_COMMAND_BUFFER; - if (render_pass == NULL) { SDL_InvalidParamError("render_pass"); return; } + CommandBufferCommonHeader *commandBufferCommonHeader; + commandBufferCommonHeader = (CommandBufferCommonHeader *)RENDERPASS_COMMAND_BUFFER; + if (RENDERPASS_DEVICE->debug_mode) { CHECK_RENDERPASS } @@ -2543,7 +2544,7 @@ void SDL_EndGPUComputePass( if (COMPUTEPASS_DEVICE->debug_mode) { commandBufferCommonHeader = (CommandBufferCommonHeader *)COMPUTEPASS_COMMAND_BUFFER; commandBufferCommonHeader->compute_pass.in_progress = false; - commandBufferCommonHeader->compute_pass.compute_pipeline = false; + commandBufferCommonHeader->compute_pass.compute_pipeline = NULL; SDL_zeroa(commandBufferCommonHeader->compute_pass.sampler_bound); SDL_zeroa(commandBufferCommonHeader->compute_pass.read_only_storage_texture_bound); SDL_zeroa(commandBufferCommonHeader->compute_pass.read_only_storage_buffer_bound); diff --git a/third-party/SDL/src/gpu/d3d12/SDL_gpu_d3d12.c b/third-party/SDL/src/gpu/d3d12/SDL_gpu_d3d12.c index a92215910c..1ad359b72a 100644 --- a/third-party/SDL/src/gpu/d3d12/SDL_gpu_d3d12.c +++ b/third-party/SDL/src/gpu/d3d12/SDL_gpu_d3d12.c @@ -1194,7 +1194,6 @@ struct D3D12UniformBuffer D3D12Buffer *buffer; Uint32 writeOffset; Uint32 drawOffset; - Uint32 currentBlockSize; }; // Forward function declarations @@ -1441,6 +1440,8 @@ static void D3D12_INTERNAL_ReleaseTextureContainer( container->textures[i]); } + SDL_DestroyProperties(container->header.info.props); + // Containers are just client handles, so we can destroy immediately if (container->debugName) { SDL_free(container->debugName); @@ -4496,7 +4497,6 @@ static D3D12UniformBuffer *D3D12_INTERNAL_AcquireUniformBufferFromPool( SDL_UnlockMutex(renderer->acquireUniformBufferLock); - uniformBuffer->currentBlockSize = 0; uniformBuffer->drawOffset = 0; uniformBuffer->writeOffset = 0; @@ -4535,6 +4535,7 @@ static void D3D12_INTERNAL_PushUniformData( Uint32 length) { D3D12UniformBuffer *uniformBuffer; + Uint32 blockSize; if (shaderStage == SDL_GPU_SHADERSTAGE_VERTEX) { if (commandBuffer->vertexUniformBuffers[slotIndex] == NULL) { @@ -4559,13 +4560,13 @@ static void D3D12_INTERNAL_PushUniformData( return; } - uniformBuffer->currentBlockSize = + blockSize = D3D12_INTERNAL_Align( length, 256); // If there is no more room, acquire a new uniform buffer - if (uniformBuffer->writeOffset + uniformBuffer->currentBlockSize >= UNIFORM_BUFFER_SIZE) { + if (uniformBuffer->writeOffset + blockSize >= UNIFORM_BUFFER_SIZE) { ID3D12Resource_Unmap( uniformBuffer->buffer->handle, 0, @@ -4595,7 +4596,7 @@ static void D3D12_INTERNAL_PushUniformData( data, length); - uniformBuffer->writeOffset += uniformBuffer->currentBlockSize; + uniformBuffer->writeOffset += blockSize; if (shaderStage == SDL_GPU_SHADERSTAGE_VERTEX) { commandBuffer->needVertexUniformBufferBind[slotIndex] = true; diff --git a/third-party/SDL/src/gpu/metal/SDL_gpu_metal.m b/third-party/SDL/src/gpu/metal/SDL_gpu_metal.m index 7c2aefebb2..591bd052af 100644 --- a/third-party/SDL/src/gpu/metal/SDL_gpu_metal.m +++ b/third-party/SDL/src/gpu/metal/SDL_gpu_metal.m @@ -888,6 +888,7 @@ static void METAL_INTERNAL_DestroyTextureContainer( container->textures[i]->handle = nil; SDL_free(container->textures[i]); } + SDL_DestroyProperties(container->header.info.props); if (container->debugName != NULL) { SDL_free(container->debugName); } diff --git a/third-party/SDL/src/gpu/vulkan/SDL_gpu_vulkan.c b/third-party/SDL/src/gpu/vulkan/SDL_gpu_vulkan.c index 4467ef801b..66f486fa6d 100644 --- a/third-party/SDL/src/gpu/vulkan/SDL_gpu_vulkan.c +++ b/third-party/SDL/src/gpu/vulkan/SDL_gpu_vulkan.c @@ -1108,6 +1108,7 @@ typedef struct VulkanCommandBuffer VulkanFenceHandle *inFlightFence; bool autoReleaseFence; + bool swapchainRequested; bool isDefrag; // Whether this CB was created for defragging } VulkanCommandBuffer; @@ -1277,13 +1278,20 @@ static inline const char *VkErrorMessages(VkResult code) #undef ERR_TO_STR } -#define SET_ERROR_AND_RETURN(fmt, msg, ret) \ +#define SET_ERROR(fmt, msg) \ do { \ if (renderer->debugMode) { \ SDL_LogError(SDL_LOG_CATEGORY_GPU, fmt, msg); \ } \ SDL_SetError((fmt), (msg)); \ - return ret; \ + } while (0) + +#define SET_STRING_ERROR(msg) SET_ERROR("%s", msg) + +#define SET_ERROR_AND_RETURN(fmt, msg, ret) \ + do { \ + SET_ERROR(fmt, msg); \ + return ret; \ } while (0) #define SET_STRING_ERROR_AND_RETURN(msg, ret) SET_ERROR_AND_RETURN("%s", msg, ret) @@ -6951,6 +6959,8 @@ static void VULKAN_ReleaseTexture( VULKAN_INTERNAL_ReleaseTexture(renderer, vulkanTextureContainer->textures[i]); } + SDL_DestroyProperties(vulkanTextureContainer->header.info.props); + // Containers are just client handles, so we can destroy immediately if (vulkanTextureContainer->debugName != NULL) { SDL_free(vulkanTextureContainer->debugName); @@ -8592,7 +8602,7 @@ static void VULKAN_INTERNAL_BindComputeDescriptorSets( dynamicOffsetCount, dynamicOffsets); - commandBuffer->needNewVertexUniformOffsets = false; + commandBuffer->needNewComputeUniformOffsets = false; } static void VULKAN_DispatchCompute( @@ -9925,6 +9935,14 @@ static bool VULKAN_INTERNAL_AcquireSwapchainTexture( SET_STRING_ERROR_AND_RETURN("Cannot acquire a swapchain texture from an unclaimed window!", false); } + // The command buffer is flagged for cleanup when the swapchain is requested as a cleanup timing mechanism + vulkanCommandBuffer->swapchainRequested = true; + + if (window->flags & SDL_WINDOW_HIDDEN) { + // Edge case, texture is filled in with NULL but not an error + return true; + } + // If window data marked as needing swapchain recreate, try to recreate if (windowData->needsSwapchainRecreate) { Uint32 recreateSwapchainResult = VULKAN_INTERNAL_RecreateSwapchain(renderer, windowData); @@ -9942,13 +9960,6 @@ static bool VULKAN_INTERNAL_AcquireSwapchainTexture( } } - if (swapchainTextureWidth) { - *swapchainTextureWidth = windowData->width; - } - if (swapchainTextureHeight) { - *swapchainTextureHeight = windowData->height; - } - if (windowData->inFlightFences[windowData->frameCounter] != NULL) { if (block) { // If we are blocking, just wait for the fence! @@ -10000,6 +10011,14 @@ static bool VULKAN_INTERNAL_AcquireSwapchainTexture( } } + if (swapchainTextureWidth) { + *swapchainTextureWidth = windowData->width; + } + + if (swapchainTextureHeight) { + *swapchainTextureHeight = windowData->height; + } + swapchainTextureContainer = &windowData->textureContainers[swapchainImageIndex]; // We need a special execution dependency with pWaitDstStageMask or image transition can start before acquire finishes @@ -10377,6 +10396,7 @@ static void VULKAN_INTERNAL_CleanCommandBuffer( commandBuffer->presentDataCount = 0; commandBuffer->waitSemaphoreCount = 0; commandBuffer->signalSemaphoreCount = 0; + commandBuffer->swapchainRequested = false; // Reset defrag state @@ -10533,7 +10553,7 @@ static bool VULKAN_Submit( VulkanTextureSubresource *swapchainTextureSubresource; VulkanMemorySubAllocator *allocator; bool performCleanups = - (renderer->claimedWindowCount > 0 && vulkanCommandBuffer->presentDataCount > 0) || + (renderer->claimedWindowCount > 0 && vulkanCommandBuffer->swapchainRequested) || renderer->claimedWindowCount == 0; SDL_LockMutex(renderer->submitLock); @@ -11686,7 +11706,7 @@ static SDL_GPUDevice *VULKAN_CreateDevice(bool debugMode, bool preferLowPower, S renderer = (VulkanRenderer *)SDL_calloc(1, sizeof(*renderer)); if (!renderer) { SDL_Vulkan_UnloadLibrary(); - return false; + return NULL; } renderer->debugMode = debugMode; @@ -11694,9 +11714,10 @@ static SDL_GPUDevice *VULKAN_CreateDevice(bool debugMode, bool preferLowPower, S renderer->allowedFramesInFlight = 2; if (!VULKAN_INTERNAL_PrepareVulkan(renderer)) { + SET_STRING_ERROR("Failed to initialize Vulkan!"); SDL_free(renderer); SDL_Vulkan_UnloadLibrary(); - SET_STRING_ERROR_AND_RETURN("Failed to initialize Vulkan!", NULL); + return NULL; } SDL_LogInfo(SDL_LOG_CATEGORY_GPU, "SDL_GPU Driver: Vulkan"); @@ -11722,9 +11743,10 @@ static SDL_GPUDevice *VULKAN_CreateDevice(bool debugMode, bool preferLowPower, S if (!VULKAN_INTERNAL_CreateLogicalDevice( renderer)) { + SET_STRING_ERROR("Failed to create logical device!"); SDL_free(renderer); SDL_Vulkan_UnloadLibrary(); - SET_STRING_ERROR_AND_RETURN("Failed to create logical device!", NULL); + return NULL; } // FIXME: just move this into this function diff --git a/third-party/SDL/src/haptic/windows/SDL_dinputhaptic.c b/third-party/SDL/src/haptic/windows/SDL_dinputhaptic.c index 255aac015d..79c4b3501f 100644 --- a/third-party/SDL/src/haptic/windows/SDL_dinputhaptic.c +++ b/third-party/SDL/src/haptic/windows/SDL_dinputhaptic.c @@ -31,11 +31,7 @@ /* * External stuff. */ -#ifdef SDL_VIDEO_DRIVER_WINDOWS extern HWND SDL_HelperWindow; -#else -static const HWND SDL_HelperWindow = NULL; -#endif /* * Internal stuff. diff --git a/third-party/SDL/src/hidapi/libusb/hid.c b/third-party/SDL/src/hidapi/libusb/hid.c index c911f7e715..21fa1c0b0f 100644 --- a/third-party/SDL/src/hidapi/libusb/hid.c +++ b/third-party/SDL/src/hidapi/libusb/hid.c @@ -71,6 +71,11 @@ extern "C" { #define DETACH_KERNEL_DRIVER #endif +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:5287) /* operands are different enum types */ +#endif + /* Uncomment to enable the retrieval of Usage and Usage Page in hid_enumerate(). Warning, on platforms different from FreeBSD this is very invasive as it requires the detach @@ -918,6 +923,22 @@ static int should_enumerate_interface(unsigned short vendor_id, const struct lib return 0; } +static int libusb_blacklist(unsigned short vendor_id, unsigned short product_id) +{ + size_t i; + static const struct { unsigned short vid; unsigned short pid; } known_bad[] = { + { 0x1532, 0x0227 } /* Razer Huntsman Gaming keyboard - long delay asking for device details */ + }; + + for (i = 0; i < (sizeof(known_bad)/sizeof(known_bad[0])); i++) { + if ((vendor_id == known_bad[i].vid) && (product_id == known_bad[i].pid || known_bad[i].pid == 0x0000)) { + return 1; + } + } + + return 0; +} + struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id) { libusb_device **devs; @@ -948,7 +969,8 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short dev_pid = desc.idProduct; if ((vendor_id != 0x0 && vendor_id != dev_vid) || - (product_id != 0x0 && product_id != dev_pid)) { + (product_id != 0x0 && product_id != dev_pid) || + libusb_blacklist(dev_vid, dev_pid)) { continue; } @@ -2140,6 +2162,10 @@ uint16_t get_usb_code_for_current_locale(void) return 0x0; } +#if defined(_MSC_VER) +#pragma warning (pop) +#endif + #ifdef __cplusplus } #endif diff --git a/third-party/SDL/src/hidapi/linux/hid.c b/third-party/SDL/src/hidapi/linux/hid.c index dbbb327785..fa303d7ae5 100644 --- a/third-party/SDL/src/hidapi/linux/hid.c +++ b/third-party/SDL/src/hidapi/linux/hid.c @@ -134,7 +134,6 @@ static wchar_t *utf8_to_wchar_t(const char *utf8) * Use register_error_str(NULL) to free the error message completely. */ static void register_error_str(wchar_t **error_str, const char *msg) { - free(*error_str); #ifdef HIDAPI_USING_SDL_RUNTIME /* Thread-safe error handling */ if (msg) { @@ -143,6 +142,7 @@ static void register_error_str(wchar_t **error_str, const char *msg) SDL_ClearError(); } #else + free(*error_str); *error_str = utf8_to_wchar_t(msg); #endif } diff --git a/third-party/SDL/src/hidapi/windows/hid.c b/third-party/SDL/src/hidapi/windows/hid.c index 3376ba96e5..87aa639e24 100644 --- a/third-party/SDL/src/hidapi/windows/hid.c +++ b/third-party/SDL/src/hidapi/windows/hid.c @@ -949,6 +949,7 @@ static int hid_blacklist(unsigned short vendor_id, unsigned short product_id) { 0x0D8C, 0x0014 }, /* Sharkoon Skiller SGH2 headset - causes deadlock asking for device details */ { 0x1532, 0x0109 }, /* Razer Lycosa Gaming keyboard - causes deadlock asking for device details */ { 0x1532, 0x010B }, /* Razer Arctosa Gaming keyboard - causes deadlock asking for device details */ + { 0x1532, 0x0227 }, /* Razer Huntsman Gaming keyboard - long delay asking for device details */ { 0x1B1C, 0x1B3D }, /* Corsair Gaming keyboard - causes deadlock asking for device details */ { 0x1CCF, 0x0000 } /* All Konami Amusement Devices - causes deadlock asking for device details */ }; diff --git a/third-party/SDL/src/joystick/SDL_gamepad.c b/third-party/SDL/src/joystick/SDL_gamepad.c index 385e890cf9..5bb2808d89 100644 --- a/third-party/SDL/src/joystick/SDL_gamepad.c +++ b/third-party/SDL/src/joystick/SDL_gamepad.c @@ -311,7 +311,7 @@ void SDL_PrivateGamepadAdded(SDL_JoystickID instance_id) { SDL_Event event; - if (!SDL_gamepads_initialized) { + if (!SDL_gamepads_initialized || SDL_IsJoystickBeingAdded()) { return; } @@ -913,7 +913,7 @@ static GamepadMapping_t *SDL_PrivateMatchGamepadMappingForGUID(SDL_GUID guid, bo // An exact match, including CRC return mapping; } else if (crc && exact_match_crc) { - return NULL; + continue; } if (!best_match) { @@ -1786,6 +1786,11 @@ static GamepadMapping_t *SDL_PrivateGenerateAutomaticGamepadMapping(const char * char name_string[128]; char mapping[1024]; + // Remove the CRC from the GUID + // We already know that this GUID doesn't have a mapping without the CRC, and we want newly + // added mappings without a CRC to override this mapping. + SDL_SetJoystickGUIDCRC(&guid, 0); + // Remove any commas in the name SDL_strlcpy(name_string, name, sizeof(name_string)); { diff --git a/third-party/SDL/src/joystick/SDL_gamepad_db.h b/third-party/SDL/src/joystick/SDL_gamepad_db.h index a0f8ea8e89..1222cea857 100644 --- a/third-party/SDL/src/joystick/SDL_gamepad_db.h +++ b/third-party/SDL/src/joystick/SDL_gamepad_db.h @@ -215,7 +215,7 @@ static const char *s_GamepadMappings[] = { "03000000362800000100000000000000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:b13,rightx:a3,righty:a4,x:b1,y:b2,", "03000000782300000a10000000000000,Onlive Wireless Controller,a:b15,b:b14,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b11,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b13,y:b12,", "030000006b14000001a1000000000000,Orange Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,", - "0300000009120000072f000000000000,OrangeFox86 DreamPicoPort,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:-a2,leftx:a0,lefty:a1,rightx:a3,righty:a4,righttrigger:-a5,start:b11,x:b3,y:b4,", + "0300000009120000072f000000000000,OrangeFox86 DreamPicoPort,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:-a2,leftx:a0,lefty:a1,righttrigger:-a5,rightx:a3,righty:a4,start:b11,x:b3,y:b4,", "03000000120c0000f60e000000000000,P4 Wired Gamepad,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b7,rightshoulder:b4,righttrigger:b6,start:b9,x:b0,y:b3,", "030000006f0e00000901000000000000,PDP Versus Fighting Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,", "03000000632500002306000000000000,PS Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,", @@ -416,12 +416,13 @@ static const char *s_GamepadMappings[] = { "03000000790000004418000000010000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,", "0300000025090000e803000000000000,Mayflash Wii Classic Controller,a:b1,b:b0,back:b8,dpdown:b13,dpleft:b12,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,", "03000000790000000018000000000000,Mayflash WiiU Pro Game Controller Adapter (DInput),a:b4,b:b8,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b16,leftstick:b40,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,rightstick:b44,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b12,", + "03000000853200008906000000010000,NACON Revolution X Unlimited,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,", "030000001008000001e5000006010000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b6,start:b9,x:b3,y:b0,", "03000000550900001472000025050000,NVIDIA Controller v01.04,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b4,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,", "030000004b120000014d000000010000,NYKO AIRFLO EX,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,", "030000007e0500000920000000000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,", "050000007e05000009200000ff070000,Nintendo Switch Pro Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b3,y:b2,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,", - "0300000009120000072f000000010000,OrangeFox86 DreamPicoPort,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a2,leftx:a0,lefty:a1,rightx:a3,righty:a4,righttrigger:a5,start:b11,x:b3,y:b4,", + "0300000009120000072f000000010000,OrangeFox86 DreamPicoPort,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a2,leftx:a0,lefty:a1,righttrigger:a5,rightx:a3,righty:a4,start:b11,x:b3,y:b4,", "030000006f0e00000901000002010000,PDP Versus Fighting Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,", "030000004c0500006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,", "030000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,", @@ -796,6 +797,7 @@ static const char *s_GamepadMappings[] = { "03000000c0160000e105000010010000,Xin-Mo Dual Arcade,crc:82d5,a:b1,b:b2,back:b9,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b4,righttrigger:b5,start:b8,x:b0,y:b3,", /* Ultimate Atari Fight Stick */ "03000000120c0000100e000011010000,ZEROPLUS P4 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,", "03000000120c0000101e000011010000,ZEROPLUS P4 Wired Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,", + "03000000120c0000182e000011010000,ZEROPLUS P4 Wired Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,", "03000000666600006706000000010000,boom PSX to PC Converter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,", "03000000830500006020000010010000,iBuffalo SNES Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,", "050000006964726f69643a636f6e0000,idroid:con,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,", diff --git a/third-party/SDL/src/joystick/SDL_joystick.c b/third-party/SDL/src/joystick/SDL_joystick.c index 921576854e..3ad49bbb3e 100644 --- a/third-party/SDL/src/joystick/SDL_joystick.c +++ b/third-party/SDL/src/joystick/SDL_joystick.c @@ -2143,6 +2143,7 @@ void SDL_PrivateJoystickAdded(SDL_JoystickID instance_id) SDL_JoystickDriver *driver; int device_index; int player_index = -1; + bool is_gamepad; SDL_AssertJoysticksLocked(); @@ -2177,9 +2178,12 @@ void SDL_PrivateJoystickAdded(SDL_JoystickID instance_id) } } + // This might create an automatic gamepad mapping, so wait to send the event + is_gamepad = SDL_IsGamepad(instance_id); + SDL_joystick_being_added = false; - if (SDL_IsGamepad(instance_id)) { + if (is_gamepad) { SDL_PrivateGamepadAdded(instance_id); } } diff --git a/third-party/SDL/src/joystick/controller_list.h b/third-party/SDL/src/joystick/controller_list.h index 5a62ece1c3..c6f4f4262a 100644 --- a/third-party/SDL/src/joystick/controller_list.h +++ b/third-party/SDL/src/joystick/controller_list.h @@ -21,7 +21,6 @@ static const ControllerDescription_t arrControllers[] = { { MAKE_CONTROLLER_ID( 0x0079, 0x181a ), k_eControllerType_PS3Controller, NULL }, // Venom Arcade Stick - { MAKE_CONTROLLER_ID( 0x0079, 0x1844 ), k_eControllerType_PS3Controller, NULL }, // From SDL { MAKE_CONTROLLER_ID( 0x044f, 0xb315 ), k_eControllerType_PS3Controller, NULL }, // Firestorm Dual Analog 3 { MAKE_CONTROLLER_ID( 0x044f, 0xd007 ), k_eControllerType_PS3Controller, NULL }, // Thrustmaster wireless 3-1 { MAKE_CONTROLLER_ID( 0x046d, 0xcad1 ), k_eControllerType_PS3Controller, NULL }, // Logitech Chillstream @@ -96,6 +95,7 @@ static const ControllerDescription_t arrControllers[] = { { MAKE_CONTROLLER_ID( 0x0c12, 0x0ef6 ), k_eControllerType_PS4Controller, NULL }, // Hitbox Arcade Stick { MAKE_CONTROLLER_ID( 0x0c12, 0x1cf6 ), k_eControllerType_PS4Controller, NULL }, // EMIO PS4 Elite Controller { MAKE_CONTROLLER_ID( 0x0c12, 0x1e10 ), k_eControllerType_PS4Controller, NULL }, // P4 Wired Gamepad generic knock off - lightbar but not trackpad or gyro + { MAKE_CONTROLLER_ID( 0x0c12, 0x2e18 ), k_eControllerType_PS4Controller, NULL }, // ZEROPLUS P4 Wired Gamepad { MAKE_CONTROLLER_ID( 0x0e6f, 0x0203 ), k_eControllerType_PS4Controller, NULL }, // Victrix Pro FS (PS4 peripheral but no trackpad/lightbar) { MAKE_CONTROLLER_ID( 0x0e6f, 0x0207 ), k_eControllerType_PS4Controller, NULL }, // Victrix Pro FS V2 w/ Touchpad for PS4 { MAKE_CONTROLLER_ID( 0x0e6f, 0x020a ), k_eControllerType_PS4Controller, NULL }, // Victrix Pro FS PS4/PS5 (PS4 mode) diff --git a/third-party/SDL/src/joystick/darwin/SDL_iokitjoystick.c b/third-party/SDL/src/joystick/darwin/SDL_iokitjoystick.c index 9327276a42..e87ab8243c 100644 --- a/third-party/SDL/src/joystick/darwin/SDL_iokitjoystick.c +++ b/third-party/SDL/src/joystick/darwin/SDL_iokitjoystick.c @@ -27,6 +27,7 @@ #include "SDL_iokitjoystick_c.h" #include "../hidapi/SDL_hidapijoystick_c.h" #include "../../haptic/darwin/SDL_syshaptic_c.h" // For haptic hot plugging +#include "../usb_ids.h" #define SDL_JOYSTICK_RUNLOOP_MODE CFSTR("SDLJoystick") @@ -213,6 +214,29 @@ static bool GetHIDScaledCalibratedState(recDevice *pDevice, recElement *pElement return result; } +static bool GetHIDScaledCalibratedState_NACON_Revolution_X_Unlimited(recDevice *pDevice, recElement *pElement, SInt32 min, SInt32 max, SInt32 *pValue) +{ + if (pElement->minReport == 0 && pElement->maxReport == 255) { + return GetHIDScaledCalibratedState(pDevice, pElement, min, max, pValue); + } + + // This device thumbstick axes have an unusual axis range that + // doesn't work with GetHIDScaledCalibratedState() above. + // + // See https://github.com/libsdl-org/SDL/issues/13143 for details + if (GetHIDElementState(pDevice, pElement, pValue)) { + if (*pValue >= 0) { + // Negative axis values range from 32767 (at rest) to 0 (minimum) + *pValue = -32767 + *pValue; + } else if (*pValue < 0) { + // Positive axis values range from -32768 (at rest) to 0 (maximum) + *pValue = 32768 + *pValue; + } + return true; + } + return false; +} + static void JoystickDeviceWasRemovedCallback(void *ctx, IOReturn result, void *sender) { recDevice *device = (recDevice *)ctx; @@ -506,6 +530,11 @@ static bool GetDeviceInfo(IOHIDDeviceRef hidDevice, recDevice *pDevice) pDevice->guid = SDL_CreateJoystickGUID(SDL_HARDWARE_BUS_USB, (Uint16)vendor, (Uint16)product, (Uint16)version, manufacturer_string, product_string, 0, 0); pDevice->steam_virtual_gamepad_slot = GetSteamVirtualGamepadSlot((Uint16)vendor, (Uint16)product, product_string); + if (vendor == USB_VENDOR_NACON_ALT && + product == USB_PRODUCT_NACON_REVOLUTION_X_UNLIMITED_BT) { + pDevice->nacon_revolution_x_unlimited = true; + } + array = IOHIDDeviceCopyMatchingElements(hidDevice, NULL, kIOHIDOptionsTypeNone); if (array) { AddHIDElements(array, pDevice); @@ -957,7 +986,11 @@ static void DARWIN_JoystickUpdate(SDL_Joystick *joystick) i = 0; while (element) { - goodRead = GetHIDScaledCalibratedState(device, element, -32768, 32767, &value); + if (device->nacon_revolution_x_unlimited) { + goodRead = GetHIDScaledCalibratedState_NACON_Revolution_X_Unlimited(device, element, -32768, 32767, &value); + } else { + goodRead = GetHIDScaledCalibratedState(device, element, -32768, 32767, &value); + } if (goodRead) { SDL_SendJoystickAxis(timestamp, joystick, i, value); } diff --git a/third-party/SDL/src/joystick/darwin/SDL_iokitjoystick_c.h b/third-party/SDL/src/joystick/darwin/SDL_iokitjoystick_c.h index 91deb240e4..3a70d2bde1 100644 --- a/third-party/SDL/src/joystick/darwin/SDL_iokitjoystick_c.h +++ b/third-party/SDL/src/joystick/darwin/SDL_iokitjoystick_c.h @@ -72,6 +72,7 @@ struct joystick_hwdata int instance_id; SDL_GUID guid; int steam_virtual_gamepad_slot; + bool nacon_revolution_x_unlimited; struct joystick_hwdata *pNext; // next device }; diff --git a/third-party/SDL/src/joystick/hidapi/SDL_hidapi_gamecube.c b/third-party/SDL/src/joystick/hidapi/SDL_hidapi_gamecube.c index 4d45c7a264..c03d92702e 100644 --- a/third-party/SDL/src/joystick/hidapi/SDL_hidapi_gamecube.c +++ b/third-party/SDL/src/joystick/hidapi/SDL_hidapi_gamecube.c @@ -31,7 +31,9 @@ #ifdef SDL_JOYSTICK_HIDAPI_GAMECUBE // Define this if you want to log all packets from the controller -// #define DEBUG_GAMECUBE_PROTOCOL +#if 0 +#define DEBUG_GAMECUBE_PROTOCOL +#endif #define MAX_CONTROLLERS 4 @@ -119,22 +121,15 @@ static bool HIDAPI_DriverGameCube_InitDevice(SDL_HIDAPI_Device *device) } device->context = ctx; - ctx->joysticks[0] = 0; - ctx->joysticks[1] = 0; - ctx->joysticks[2] = 0; - ctx->joysticks[3] = 0; ctx->rumble[0] = rumbleMagic; - ctx->useRumbleBrake = false; if (device->vendor_id != USB_VENDOR_NINTENDO) { ctx->pc_mode = true; } if (ctx->pc_mode) { - for (i = 0; i < MAX_CONTROLLERS; ++i) { - ResetAxisRange(ctx, i); - HIDAPI_JoystickConnected(device, &ctx->joysticks[i]); - } + ResetAxisRange(ctx, 0); + HIDAPI_JoystickConnected(device, &ctx->joysticks[0]); } else { // This is all that's needed to initialize the device. Really! if (SDL_hid_write(device->dev, &initMagic, sizeof(initMagic)) != sizeof(initMagic)) { @@ -204,69 +199,61 @@ static void HIDAPI_DriverGameCube_SetDevicePlayerIndex(SDL_HIDAPI_Device *device { } -static void HIDAPI_DriverGameCube_HandleJoystickPacket(SDL_HIDAPI_Device *device, SDL_DriverGameCube_Context *ctx, const Uint8 *packet, int size) +static void HIDAPI_DriverGameCube_HandleJoystickPacket(SDL_HIDAPI_Device *device, SDL_DriverGameCube_Context *ctx, const Uint8 *packet, bool invert_c_stick) { SDL_Joystick *joystick; - Uint8 i, v; + const Uint8 i = 0; // We have a separate context for each connected controller in PC mode, just use the first index + Uint8 v; Sint16 axis_value; Uint64 timestamp = SDL_GetTicksNS(); - if (size != 10) { - return; // How do we handle this packet? - } - - i = packet[0] - 1; - if (i >= MAX_CONTROLLERS) { - return; // How do we handle this packet? - } - joystick = SDL_GetJoystickFromID(ctx->joysticks[i]); if (!joystick) { // Hasn't been opened yet, skip return; } -#define READ_BUTTON(off, flag, button) \ - SDL_SendJoystickButton( \ - timestamp, \ - joystick, \ - button, \ +#define READ_BUTTON(off, flag, button) \ + SDL_SendJoystickButton( \ + timestamp, \ + joystick, \ + button, \ ((packet[off] & flag) != 0)); - READ_BUTTON(1, 0x02, 0) // A - READ_BUTTON(1, 0x04, 1) // B - READ_BUTTON(1, 0x08, 3) // Y - READ_BUTTON(1, 0x01, 2) // X - READ_BUTTON(2, 0x80, 4) // DPAD_LEFT - READ_BUTTON(2, 0x20, 5) // DPAD_RIGHT - READ_BUTTON(2, 0x40, 6) // DPAD_DOWN - READ_BUTTON(2, 0x10, 7) // DPAD_UP - READ_BUTTON(2, 0x02, 8) // START - READ_BUTTON(1, 0x80, 9) // RIGHTSHOULDER + READ_BUTTON(0, 0x02, 0) // A + READ_BUTTON(0, 0x04, 1) // B + READ_BUTTON(0, 0x08, 3) // Y + READ_BUTTON(0, 0x01, 2) // X + READ_BUTTON(1, 0x80, 4) // DPAD_LEFT + READ_BUTTON(1, 0x20, 5) // DPAD_RIGHT + READ_BUTTON(1, 0x40, 6) // DPAD_DOWN + READ_BUTTON(1, 0x10, 7) // DPAD_UP + READ_BUTTON(1, 0x02, 8) // START + READ_BUTTON(0, 0x80, 9) // RIGHTSHOULDER /* These two buttons are for the bottoms of the analog triggers. * More than likely, you're going to want to read the axes instead! * -flibit */ - READ_BUTTON(1, 0x20, 10) // TRIGGERRIGHT - READ_BUTTON(1, 0x10, 11) // TRIGGERLEFT + READ_BUTTON(0, 0x20, 10) // TRIGGERRIGHT + READ_BUTTON(0, 0x10, 11) // TRIGGERLEFT #undef READ_BUTTON -#define READ_AXIS(off, axis, invert) \ - v = invert ? (0xff - packet[off]) : packet[off]; \ - if (v < ctx->min_axis[i * SDL_GAMEPAD_AXIS_COUNT + axis]) \ - ctx->min_axis[i * SDL_GAMEPAD_AXIS_COUNT + axis] = v; \ - if (v > ctx->max_axis[i * SDL_GAMEPAD_AXIS_COUNT + axis]) \ - ctx->max_axis[i * SDL_GAMEPAD_AXIS_COUNT + axis] = v; \ +#define READ_AXIS(off, axis, invert) \ + v = (invert) ? (0xff - packet[off]) : packet[off]; \ + if (v < ctx->min_axis[i * SDL_GAMEPAD_AXIS_COUNT + axis]) \ + ctx->min_axis[i * SDL_GAMEPAD_AXIS_COUNT + axis] = v; \ + if (v > ctx->max_axis[i * SDL_GAMEPAD_AXIS_COUNT + axis]) \ + ctx->max_axis[i * SDL_GAMEPAD_AXIS_COUNT + axis] = v; \ axis_value = (Sint16)HIDAPI_RemapVal(v, ctx->min_axis[i * SDL_GAMEPAD_AXIS_COUNT + axis], ctx->max_axis[i * SDL_GAMEPAD_AXIS_COUNT + axis], SDL_MIN_SINT16, SDL_MAX_SINT16); \ - SDL_SendJoystickAxis( \ - timestamp, \ - joystick, \ + SDL_SendJoystickAxis( \ + timestamp, \ + joystick, \ axis, axis_value); - READ_AXIS(3, SDL_GAMEPAD_AXIS_LEFTX, 0) - READ_AXIS(4, SDL_GAMEPAD_AXIS_LEFTY, 1) - READ_AXIS(6, SDL_GAMEPAD_AXIS_RIGHTX, 0) - READ_AXIS(5, SDL_GAMEPAD_AXIS_RIGHTY, 1) - READ_AXIS(7, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, 0) - READ_AXIS(8, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, 0) + READ_AXIS(2, SDL_GAMEPAD_AXIS_LEFTX, 0) + READ_AXIS(3, SDL_GAMEPAD_AXIS_LEFTY, 1) + READ_AXIS(5, SDL_GAMEPAD_AXIS_RIGHTX, invert_c_stick ? 1 : 0) + READ_AXIS(4, SDL_GAMEPAD_AXIS_RIGHTY, invert_c_stick ? 0 : 1) + READ_AXIS(6, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, 0) + READ_AXIS(7, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, 0) #undef READ_AXIS } @@ -365,7 +352,18 @@ static bool HIDAPI_DriverGameCube_UpdateDevice(SDL_HIDAPI_Device *device) HIDAPI_DumpPacket("Nintendo GameCube packet: size = %d", packet, size); #endif if (ctx->pc_mode) { - HIDAPI_DriverGameCube_HandleJoystickPacket(device, ctx, packet, size); + if (size == 10) { + // This is the older firmware + // The first byte is the index of the connected controller + // The C stick has an inverted value range compared to the left stick + HIDAPI_DriverGameCube_HandleJoystickPacket(device, ctx, &packet[1], true); + } else if (size == 9) { + // This is the newer firmware (version 0x7) + // The C stick has the same value range compared to the left stick + HIDAPI_DriverGameCube_HandleJoystickPacket(device, ctx, packet, false); + } else { + // How do we handle this packet? + } } else { HIDAPI_DriverGameCube_HandleNintendoPacket(device, ctx, packet, size); } diff --git a/third-party/SDL/src/joystick/hidapi/SDL_hidapi_ps4.c b/third-party/SDL/src/joystick/hidapi/SDL_hidapi_ps4.c index 7404bf2268..d4332df7b3 100644 --- a/third-party/SDL/src/joystick/hidapi/SDL_hidapi_ps4.c +++ b/third-party/SDL/src/joystick/hidapi/SDL_hidapi_ps4.c @@ -1003,8 +1003,8 @@ static bool HIDAPI_DriverPS4_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device static void HIDAPI_DriverPS4_HandleStatePacket(SDL_Joystick *joystick, SDL_hid_device *dev, SDL_DriverPS4_Context *ctx, PS4StatePacket_t *packet, int size) { - static const float TOUCHPAD_SCALEX = 1.0f / 1920; - static const float TOUCHPAD_SCALEY = 1.0f / 920; // This is noted as being 944 resolution, but 920 feels better + static const float TOUCHPAD_SCALEX = 5.20833333e-4f; // 1.0f / 1920 + static const float TOUCHPAD_SCALEY = 1.08695652e-3f; // 1.0f / 920 // This is noted as being 944 resolution, but 920 feels better Sint16 axis; bool touchpad_down; int touchpad_x, touchpad_y; diff --git a/third-party/SDL/src/joystick/hidapi/SDL_hidapi_ps5.c b/third-party/SDL/src/joystick/hidapi/SDL_hidapi_ps5.c index abf59a87f2..4249578e80 100644 --- a/third-party/SDL/src/joystick/hidapi/SDL_hidapi_ps5.c +++ b/third-party/SDL/src/joystick/hidapi/SDL_hidapi_ps5.c @@ -812,14 +812,19 @@ static void HIDAPI_DriverPS5_SetEnhancedModeAvailable(SDL_DriverPS5_Context *ctx } if (ctx->sensors_supported) { + // Standard DualSense sensor update rate is 250 Hz over USB + float update_rate = 250.0f; + if (ctx->device->is_bluetooth) { // Bluetooth sensor update rate appears to be 1000 Hz - SDL_PrivateJoystickAddSensor(ctx->joystick, SDL_SENSOR_GYRO, 1000.0f); - SDL_PrivateJoystickAddSensor(ctx->joystick, SDL_SENSOR_ACCEL, 1000.0f); - } else { - SDL_PrivateJoystickAddSensor(ctx->joystick, SDL_SENSOR_GYRO, 250.0f); - SDL_PrivateJoystickAddSensor(ctx->joystick, SDL_SENSOR_ACCEL, 250.0f); + update_rate = 1000.0f; + } else if (SDL_IsJoystickDualSenseEdge(ctx->device->vendor_id, ctx->device->product_id)) { + // DualSense Edge sensor update rate is 1000 Hz over USB + update_rate = 1000.0f; } + + SDL_PrivateJoystickAddSensor(ctx->joystick, SDL_SENSOR_GYRO, update_rate); + SDL_PrivateJoystickAddSensor(ctx->joystick, SDL_SENSOR_ACCEL, update_rate); } ctx->report_battery = true; @@ -1355,8 +1360,8 @@ static void HIDAPI_DriverPS5_HandleStatePacketCommon(SDL_Joystick *joystick, SDL static void HIDAPI_DriverPS5_HandleStatePacket(SDL_Joystick *joystick, SDL_hid_device *dev, SDL_DriverPS5_Context *ctx, PS5StatePacket_t *packet, Uint64 timestamp) { - static const float TOUCHPAD_SCALEX = 1.0f / 1920; - static const float TOUCHPAD_SCALEY = 1.0f / 1070; + static const float TOUCHPAD_SCALEX = 5.20833333e-4f; // 1.0f / 1920 + static const float TOUCHPAD_SCALEY = 9.34579439e-4f; // 1.0f / 1070 bool touchpad_down; int touchpad_x, touchpad_y; @@ -1406,8 +1411,8 @@ static void HIDAPI_DriverPS5_HandleStatePacket(SDL_Joystick *joystick, SDL_hid_d static void HIDAPI_DriverPS5_HandleStatePacketAlt(SDL_Joystick *joystick, SDL_hid_device *dev, SDL_DriverPS5_Context *ctx, PS5StatePacketAlt_t *packet, Uint64 timestamp) { - static const float TOUCHPAD_SCALEX = 1.0f / 1920; - static const float TOUCHPAD_SCALEY = 1.0f / 1070; + static const float TOUCHPAD_SCALEX = 5.20833333e-4f; // 1.0f / 1920 + static const float TOUCHPAD_SCALEY = 9.34579439e-4f; // 1.0f / 1070 bool touchpad_down; int touchpad_x, touchpad_y; diff --git a/third-party/SDL/src/joystick/hidapi/SDL_hidapi_switch.c b/third-party/SDL/src/joystick/hidapi/SDL_hidapi_switch.c index 0e7b823c55..8f9b1bf520 100644 --- a/third-party/SDL/src/joystick/hidapi/SDL_hidapi_switch.c +++ b/third-party/SDL/src/joystick/hidapi/SDL_hidapi_switch.c @@ -58,9 +58,7 @@ #define SWITCH_GYRO_SCALE 14.2842f #define SWITCH_ACCEL_SCALE 4096.f -#define SWITCH_GYRO_SCALE_OFFSET 13371.0f #define SWITCH_GYRO_SCALE_MULT 936.0f -#define SWITCH_ACCEL_SCALE_OFFSET 16384.0f #define SWITCH_ACCEL_SCALE_MULT 4.0f enum @@ -930,13 +928,14 @@ static bool SetIMUEnabled(SDL_DriverSwitch_Context *ctx, bool enabled) static bool LoadStickCalibration(SDL_DriverSwitch_Context *ctx) { - Uint8 *pLeftStickCal; - Uint8 *pRightStickCal; + Uint8 *pLeftStickCal = NULL; + Uint8 *pRightStickCal = NULL; size_t stick, axis; SwitchSubcommandInputPacket_t *user_reply = NULL; SwitchSubcommandInputPacket_t *factory_reply = NULL; SwitchSPIOpData_t readUserParams; SwitchSPIOpData_t readFactoryParams; + Uint8 userParamsReadSuccessCount = 0; // Read User Calibration Info readUserParams.unAddress = k_unSPIStickUserCalibrationStartOffset; @@ -949,33 +948,46 @@ static bool LoadStickCalibration(SDL_DriverSwitch_Context *ctx) readFactoryParams.unAddress = k_unSPIStickFactoryCalibrationStartOffset; readFactoryParams.ucLength = k_unSPIStickFactoryCalibrationLength; - const int MAX_ATTEMPTS = 3; - for (int attempt = 0; ; ++attempt) { - if (!WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SPIFlashRead, (uint8_t *)&readFactoryParams, sizeof(readFactoryParams), &factory_reply)) { - return false; - } - - if (factory_reply->stickFactoryCalibration.opData.unAddress == k_unSPIStickFactoryCalibrationStartOffset) { - // We successfully read the calibration data - break; - } - - if (attempt == MAX_ATTEMPTS) { - return false; - } - } - // Automatically select the user calibration if magic bytes are set if (user_reply && user_reply->stickUserCalibration.rgucLeftMagic[0] == 0xB2 && user_reply->stickUserCalibration.rgucLeftMagic[1] == 0xA1) { + userParamsReadSuccessCount += 1; pLeftStickCal = user_reply->stickUserCalibration.rgucLeftCalibration; - } else { - pLeftStickCal = factory_reply->stickFactoryCalibration.rgucLeftCalibration; } if (user_reply && user_reply->stickUserCalibration.rgucRightMagic[0] == 0xB2 && user_reply->stickUserCalibration.rgucRightMagic[1] == 0xA1) { + userParamsReadSuccessCount += 1; pRightStickCal = user_reply->stickUserCalibration.rgucRightCalibration; - } else { - pRightStickCal = factory_reply->stickFactoryCalibration.rgucRightCalibration; + } + + // Only read the factory calibration info if we failed to receive the correct magic bytes + if (userParamsReadSuccessCount < 2) { + // Read Factory Calibration Info + readFactoryParams.unAddress = k_unSPIStickFactoryCalibrationStartOffset; + readFactoryParams.ucLength = k_unSPIStickFactoryCalibrationLength; + + const int MAX_ATTEMPTS = 3; + for (int attempt = 0;; ++attempt) { + if (!WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SPIFlashRead, (uint8_t *)&readFactoryParams, sizeof(readFactoryParams), &factory_reply)) { + return false; + } + + if (factory_reply->stickFactoryCalibration.opData.unAddress == k_unSPIStickFactoryCalibrationStartOffset) { + // We successfully read the calibration data + pLeftStickCal = factory_reply->stickFactoryCalibration.rgucLeftCalibration; + pRightStickCal = factory_reply->stickFactoryCalibration.rgucRightCalibration; + break; + } + + if (attempt == MAX_ATTEMPTS) { + return false; + } + } + } + + // If we still don't have calibration data, return false + if (pLeftStickCal == NULL || pRightStickCal == NULL) + { + return false; } /* Stick calibration values are 12-bits each and are packed by bit @@ -1044,6 +1056,8 @@ static bool LoadIMUCalibration(SDL_DriverSwitch_Context *ctx) if (WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SPIFlashRead, (uint8_t *)&readParams, sizeof(readParams), &reply)) { Uint8 *pIMUScale; Sint16 sAccelRawX, sAccelRawY, sAccelRawZ, sGyroRawX, sGyroRawY, sGyroRawZ; + Sint16 sAccelSensCoeffX, sAccelSensCoeffY, sAccelSensCoeffZ; + Sint16 sGyroSensCoeffX, sGyroSensCoeffY, sGyroSensCoeffZ; // IMU scale gives us multipliers for converting raw values to real world values pIMUScale = reply->spiReadData.rgucReadData; @@ -1052,10 +1066,18 @@ static bool LoadIMUCalibration(SDL_DriverSwitch_Context *ctx) sAccelRawY = (pIMUScale[3] << 8) | pIMUScale[2]; sAccelRawZ = (pIMUScale[5] << 8) | pIMUScale[4]; + sAccelSensCoeffX = (pIMUScale[7] << 8) | pIMUScale[6]; + sAccelSensCoeffY = (pIMUScale[9] << 8) | pIMUScale[8]; + sAccelSensCoeffZ = (pIMUScale[11] << 8) | pIMUScale[10]; + sGyroRawX = (pIMUScale[13] << 8) | pIMUScale[12]; sGyroRawY = (pIMUScale[15] << 8) | pIMUScale[14]; sGyroRawZ = (pIMUScale[17] << 8) | pIMUScale[16]; + sGyroSensCoeffX = (pIMUScale[19] << 8) | pIMUScale[18]; + sGyroSensCoeffY = (pIMUScale[21] << 8) | pIMUScale[20]; + sGyroSensCoeffZ = (pIMUScale[23] << 8) | pIMUScale[22]; + // Check for user calibration data. If it's present and set, it'll override the factory settings readParams.unAddress = k_unSPIIMUUserScaleStartOffset; readParams.ucLength = k_unSPIIMUUserScaleLength; @@ -1072,14 +1094,14 @@ static bool LoadIMUCalibration(SDL_DriverSwitch_Context *ctx) } // Accelerometer scale - ctx->m_IMUScaleData.fAccelScaleX = SWITCH_ACCEL_SCALE_MULT / (SWITCH_ACCEL_SCALE_OFFSET - (float)sAccelRawX) * SDL_STANDARD_GRAVITY; - ctx->m_IMUScaleData.fAccelScaleY = SWITCH_ACCEL_SCALE_MULT / (SWITCH_ACCEL_SCALE_OFFSET - (float)sAccelRawY) * SDL_STANDARD_GRAVITY; - ctx->m_IMUScaleData.fAccelScaleZ = SWITCH_ACCEL_SCALE_MULT / (SWITCH_ACCEL_SCALE_OFFSET - (float)sAccelRawZ) * SDL_STANDARD_GRAVITY; + ctx->m_IMUScaleData.fAccelScaleX = SWITCH_ACCEL_SCALE_MULT / ((float)sAccelSensCoeffX - (float)sAccelRawX) * SDL_STANDARD_GRAVITY; + ctx->m_IMUScaleData.fAccelScaleY = SWITCH_ACCEL_SCALE_MULT / ((float)sAccelSensCoeffY - (float)sAccelRawY) * SDL_STANDARD_GRAVITY; + ctx->m_IMUScaleData.fAccelScaleZ = SWITCH_ACCEL_SCALE_MULT / ((float)sAccelSensCoeffZ - (float)sAccelRawZ) * SDL_STANDARD_GRAVITY; // Gyro scale - ctx->m_IMUScaleData.fGyroScaleX = SWITCH_GYRO_SCALE_MULT / (SWITCH_GYRO_SCALE_OFFSET - (float)sGyroRawX) * SDL_PI_F / 180.0f; - ctx->m_IMUScaleData.fGyroScaleY = SWITCH_GYRO_SCALE_MULT / (SWITCH_GYRO_SCALE_OFFSET - (float)sGyroRawY) * SDL_PI_F / 180.0f; - ctx->m_IMUScaleData.fGyroScaleZ = SWITCH_GYRO_SCALE_MULT / (SWITCH_GYRO_SCALE_OFFSET - (float)sGyroRawZ) * SDL_PI_F / 180.0f; + ctx->m_IMUScaleData.fGyroScaleX = SWITCH_GYRO_SCALE_MULT / ((float)sGyroSensCoeffX - (float)sGyroRawX) * SDL_PI_F / 180.0f; + ctx->m_IMUScaleData.fGyroScaleY = SWITCH_GYRO_SCALE_MULT / ((float)sGyroSensCoeffY - (float)sGyroRawY) * SDL_PI_F / 180.0f; + ctx->m_IMUScaleData.fGyroScaleZ = SWITCH_GYRO_SCALE_MULT / ((float)sGyroSensCoeffZ - (float)sGyroRawZ) * SDL_PI_F / 180.0f; } else { // Use default values @@ -1101,14 +1123,17 @@ static Sint16 ApplyStickCalibration(SDL_DriverSwitch_Context *ctx, int nStick, i { sRawValue -= ctx->m_StickCalData[nStick].axis[nAxis].sCenter; - if (sRawValue > ctx->m_StickExtents[nStick].axis[nAxis].sMax) { - ctx->m_StickExtents[nStick].axis[nAxis].sMax = sRawValue; + if (sRawValue >= 0) { + if (sRawValue > ctx->m_StickExtents[nStick].axis[nAxis].sMax) { + ctx->m_StickExtents[nStick].axis[nAxis].sMax = sRawValue; + } + return (Sint16)HIDAPI_RemapVal(sRawValue, 0, ctx->m_StickExtents[nStick].axis[nAxis].sMax, 0, SDL_MAX_SINT16); + } else { + if (sRawValue < ctx->m_StickExtents[nStick].axis[nAxis].sMin) { + ctx->m_StickExtents[nStick].axis[nAxis].sMin = sRawValue; + } + return (Sint16)HIDAPI_RemapVal(sRawValue, ctx->m_StickExtents[nStick].axis[nAxis].sMin, 0, SDL_MIN_SINT16, 0); } - if (sRawValue < ctx->m_StickExtents[nStick].axis[nAxis].sMin) { - ctx->m_StickExtents[nStick].axis[nAxis].sMin = sRawValue; - } - - return (Sint16)HIDAPI_RemapVal(sRawValue, ctx->m_StickExtents[nStick].axis[nAxis].sMin, ctx->m_StickExtents[nStick].axis[nAxis].sMax, SDL_MIN_SINT16, SDL_MAX_SINT16); } static Sint16 ApplySimpleStickCalibration(SDL_DriverSwitch_Context *ctx, int nStick, int nAxis, Sint16 sRawValue) @@ -1118,14 +1143,17 @@ static Sint16 ApplySimpleStickCalibration(SDL_DriverSwitch_Context *ctx, int nSt sRawValue -= usJoystickCenter; - if (sRawValue > ctx->m_SimpleStickExtents[nStick].axis[nAxis].sMax) { - ctx->m_SimpleStickExtents[nStick].axis[nAxis].sMax = sRawValue; + if (sRawValue >= 0) { + if (sRawValue > ctx->m_SimpleStickExtents[nStick].axis[nAxis].sMax) { + ctx->m_SimpleStickExtents[nStick].axis[nAxis].sMax = sRawValue; + } + return (Sint16)HIDAPI_RemapVal(sRawValue, 0, ctx->m_SimpleStickExtents[nStick].axis[nAxis].sMax, 0, SDL_MAX_SINT16); + } else { + if (sRawValue < ctx->m_SimpleStickExtents[nStick].axis[nAxis].sMin) { + ctx->m_SimpleStickExtents[nStick].axis[nAxis].sMin = sRawValue; + } + return (Sint16)HIDAPI_RemapVal(sRawValue, ctx->m_SimpleStickExtents[nStick].axis[nAxis].sMin, 0, SDL_MIN_SINT16, 0); } - if (sRawValue < ctx->m_SimpleStickExtents[nStick].axis[nAxis].sMin) { - ctx->m_SimpleStickExtents[nStick].axis[nAxis].sMin = sRawValue; - } - - return (Sint16)HIDAPI_RemapVal(sRawValue, ctx->m_SimpleStickExtents[nStick].axis[nAxis].sMin, ctx->m_SimpleStickExtents[nStick].axis[nAxis].sMax, SDL_MIN_SINT16, SDL_MAX_SINT16); } static Uint8 RemapButton(SDL_DriverSwitch_Context *ctx, Uint8 button) diff --git a/third-party/SDL/src/joystick/usb_ids.h b/third-party/SDL/src/joystick/usb_ids.h index 15c1bb7454..4c85b6dad3 100644 --- a/third-party/SDL/src/joystick/usb_ids.h +++ b/third-party/SDL/src/joystick/usb_ids.h @@ -84,6 +84,7 @@ #define USB_PRODUCT_NACON_REVOLUTION_5_PRO_PS4_WIRED 0x0d17 #define USB_PRODUCT_NACON_REVOLUTION_5_PRO_PS5_WIRELESS 0x0d18 #define USB_PRODUCT_NACON_REVOLUTION_5_PRO_PS5_WIRED 0x0d19 +#define USB_PRODUCT_NACON_REVOLUTION_X_UNLIMITED_BT 0x0689 #define USB_PRODUCT_NINTENDO_GAMECUBE_ADAPTER 0x0337 #define USB_PRODUCT_NINTENDO_N64_CONTROLLER 0x2019 #define USB_PRODUCT_NINTENDO_SEGA_GENESIS_CONTROLLER 0x201e diff --git a/third-party/SDL/src/joystick/windows/SDL_dinputjoystick.c b/third-party/SDL/src/joystick/windows/SDL_dinputjoystick.c index b00218d969..5b4b6fc0bb 100644 --- a/third-party/SDL/src/joystick/windows/SDL_dinputjoystick.c +++ b/third-party/SDL/src/joystick/windows/SDL_dinputjoystick.c @@ -40,11 +40,7 @@ #define CONVERT_MAGNITUDE(x) (((x)*10000) / 0x7FFF) // external variables referenced. -#ifdef SDL_VIDEO_DRIVER_WINDOWS extern HWND SDL_HelperWindow; -#else -static const HWND SDL_HelperWindow = NULL; -#endif // local variables static bool coinitialized = false; diff --git a/third-party/SDL/src/render/SDL_render.c b/third-party/SDL/src/render/SDL_render.c index 1292c1bdda..cd3a9298da 100644 --- a/third-party/SDL/src/render/SDL_render.c +++ b/third-party/SDL/src/render/SDL_render.c @@ -2685,6 +2685,8 @@ static void UpdateLogicalPresentation(SDL_Renderer *renderer) view->pixel_h = (int) view->logical_dst_rect.h; UpdatePixelViewport(renderer, view); UpdatePixelClipRect(renderer, view); + QueueCmdSetViewport(renderer); + QueueCmdSetClipRect(renderer); } bool SDL_SetRenderLogicalPresentation(SDL_Renderer *renderer, int w, int h, SDL_RendererLogicalPresentation mode) @@ -3605,7 +3607,7 @@ bool SDL_RenderLines(SDL_Renderer *renderer, const SDL_FPoint *points, int count #endif SDL_RenderViewState *view = renderer->view; - const bool islogical = ((view == &renderer->main_view) && (view->logical_presentation_mode != SDL_LOGICAL_PRESENTATION_DISABLED)); + const bool islogical = (view->logical_presentation_mode != SDL_LOGICAL_PRESENTATION_DISABLED); if (islogical || (renderer->line_method == SDL_RENDERLINEMETHOD_GEOMETRY)) { const float scale_x = view->current_scale.x; @@ -3624,7 +3626,7 @@ bool SDL_RenderLines(SDL_Renderer *renderer, const SDL_FPoint *points, int count int num_indices = 0; const int size_indices = 4; int cur_index = -4; - const int is_looping = (points[0].x == points[count - 1].x && points[0].y == points[count - 1].y); + const bool is_looping = (points[0].x == points[count - 1].x && points[0].y == points[count - 1].y); SDL_FPoint p; // previous point p.x = p.y = 0.0f; /* p q @@ -3656,7 +3658,7 @@ bool SDL_RenderLines(SDL_Renderer *renderer, const SDL_FPoint *points, int count num_indices += 3; // closed polyline, don´t draw twice the point - if (i || is_looping == 0) { + if (i || !is_looping) { ADD_TRIANGLE(4, 5, 6) ADD_TRIANGLE(4, 6, 7) } diff --git a/third-party/SDL/src/render/opengles2/SDL_render_gles2.c b/third-party/SDL/src/render/opengles2/SDL_render_gles2.c index d15f4f020e..410326b90e 100644 --- a/third-party/SDL/src/render/opengles2/SDL_render_gles2.c +++ b/third-party/SDL/src/render/opengles2/SDL_render_gles2.c @@ -1651,6 +1651,8 @@ static bool GLES2_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD } else { renderdata->glGenTextures(1, &data->texture_v); if (!GL_CheckError("glGenTexures()", renderer)) { + SDL_free(data->pixel_data); + SDL_free(data); return false; } } @@ -1665,6 +1667,8 @@ static bool GLES2_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD } else { renderdata->glGenTextures(1, &data->texture_u); if (!GL_CheckError("glGenTexures()", renderer)) { + SDL_free(data->pixel_data); + SDL_free(data); return false; } } @@ -1672,11 +1676,15 @@ static bool GLES2_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD renderdata->glBindTexture(data->texture_type, data->texture_u); renderdata->glTexImage2D(data->texture_type, 0, format, (texture->w + 1) / 2, (texture->h + 1) / 2, 0, format, type, NULL); if (!GL_CheckError("glTexImage2D()", renderer)) { + SDL_free(data->pixel_data); + SDL_free(data); return false; } SDL_SetNumberProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_OPENGLES2_TEXTURE_U_NUMBER, data->texture_u); if (!SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace, texture->w, texture->h, 8)) { + SDL_free(data->pixel_data); + SDL_free(data); return SDL_SetError("Unsupported YUV colorspace"); } } else if (data->nv12) { @@ -1686,6 +1694,8 @@ static bool GLES2_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD } else { renderdata->glGenTextures(1, &data->texture_u); if (!GL_CheckError("glGenTexures()", renderer)) { + SDL_free(data->pixel_data); + SDL_free(data); return false; } } @@ -1693,11 +1703,15 @@ static bool GLES2_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD renderdata->glBindTexture(data->texture_type, data->texture_u); renderdata->glTexImage2D(data->texture_type, 0, GL_LUMINANCE_ALPHA, (texture->w + 1) / 2, (texture->h + 1) / 2, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, NULL); if (!GL_CheckError("glTexImage2D()", renderer)) { + SDL_free(data->pixel_data); + SDL_free(data); return false; } SDL_SetNumberProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_OPENGLES2_TEXTURE_UV_NUMBER, data->texture_u); if (!SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace, texture->w, texture->h, 8)) { + SDL_free(data->pixel_data); + SDL_free(data); return SDL_SetError("Unsupported YUV colorspace"); } } @@ -1709,6 +1723,8 @@ static bool GLES2_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD } else { renderdata->glGenTextures(1, &data->texture); if (!GL_CheckError("glGenTexures()", renderer)) { + SDL_free(data->pixel_data); + SDL_free(data); return false; } } diff --git a/third-party/SDL/src/render/vulkan/SDL_render_vulkan.c b/third-party/SDL/src/render/vulkan/SDL_render_vulkan.c index 65af0a527f..de248b0740 100644 --- a/third-party/SDL/src/render/vulkan/SDL_render_vulkan.c +++ b/third-party/SDL/src/render/vulkan/SDL_render_vulkan.c @@ -3763,7 +3763,7 @@ static bool VULKAN_SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand VULKAN_SetupShaderConstants(renderer, cmd, texture, &constants); switch (cmd->data.draw.texture_scale_mode) { - case VK_FILTER_NEAREST: + case SDL_SCALEMODE_NEAREST: switch (cmd->data.draw.texture_address_mode) { case SDL_TEXTURE_ADDRESS_CLAMP: textureSampler = rendererData->samplers[VULKAN_SAMPLER_NEAREST_CLAMP]; @@ -3775,7 +3775,7 @@ static bool VULKAN_SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand return SDL_SetError("Unknown texture address mode: %d", cmd->data.draw.texture_address_mode); } break; - case VK_FILTER_LINEAR: + case SDL_SCALEMODE_LINEAR: switch (cmd->data.draw.texture_address_mode) { case SDL_TEXTURE_ADDRESS_CLAMP: textureSampler = rendererData->samplers[VULKAN_SAMPLER_LINEAR_CLAMP]; diff --git a/third-party/SDL/src/stdlib/SDL_getenv.c b/third-party/SDL/src/stdlib/SDL_getenv.c index b4a1922465..54fb6a7b46 100644 --- a/third-party/SDL/src/stdlib/SDL_getenv.c +++ b/third-party/SDL/src/stdlib/SDL_getenv.c @@ -41,7 +41,12 @@ #define environ (*_NSGetEnviron()) #elif defined(SDL_PLATFORM_FREEBSD) #include -#define environ ((char **)dlsym(RTLD_DEFAULT, "environ")) +static char **get_environ_rtld(void) +{ + char ***environ_rtld = (char ***)dlsym(RTLD_DEFAULT, "environ"); + return environ_rtld ? *environ_rtld : NULL; +} +#define environ (get_environ_rtld()) #else extern char **environ; #endif diff --git a/third-party/SDL/src/stdlib/SDL_malloc.c b/third-party/SDL/src/stdlib/SDL_malloc.c index 008675f312..89c28304c6 100644 --- a/third-party/SDL/src/stdlib/SDL_malloc.c +++ b/third-party/SDL/src/stdlib/SDL_malloc.c @@ -1478,6 +1478,13 @@ DLMALLOC_EXPORT int mspace_mallopt(int, int); #endif /* NO_MALLOC_STATS */ #ifndef LACKS_ERRNO_H #include /* for MALLOC_FAILURE_ACTION */ +#else /* LACKS_ERRNO_H */ +#ifndef EINVAL +#define EINVAL 22 +#endif +#ifndef ENOMEM +#define ENOMEM 12 +#endif #endif /* LACKS_ERRNO_H */ #ifdef DEBUG #if ABORT_ON_ASSERT_FAILURE diff --git a/third-party/SDL/src/thread/psp/SDL_systhread.c b/third-party/SDL/src/thread/psp/SDL_systhread.c index 3d6071893b..2c500cb8ad 100644 --- a/third-party/SDL/src/thread/psp/SDL_systhread.c +++ b/third-party/SDL/src/thread/psp/SDL_systhread.c @@ -32,6 +32,8 @@ #include #include +#define PSP_THREAD_NAME_MAX 32 + static int ThreadEntry(SceSize args, void *argp) { SDL_RunThread(*(SDL_Thread **)argp); @@ -44,6 +46,7 @@ bool SDL_SYS_CreateThread(SDL_Thread *thread, { SceKernelThreadInfo status; int priority = 32; + char thread_name[PSP_THREAD_NAME_MAX]; // Set priority of new thread to the same as the current thread status.size = sizeof(SceKernelThreadInfo); @@ -51,7 +54,12 @@ bool SDL_SYS_CreateThread(SDL_Thread *thread, priority = status.currentPriority; } - thread->handle = sceKernelCreateThread(thread->name, ThreadEntry, + SDL_strlcpy(thread_name, "SDL thread", PSP_THREAD_NAME_MAX); + if (thread->name) { + SDL_strlcpy(thread_name, thread->name, PSP_THREAD_NAME_MAX); + } + + thread->handle = sceKernelCreateThread(thread_name, ThreadEntry, priority, thread->stacksize ? ((int)thread->stacksize) : 0x8000, PSP_THREAD_ATTR_VFPU, NULL); if (thread->handle < 0) { diff --git a/third-party/SDL/src/video/SDL_clipboard.c b/third-party/SDL/src/video/SDL_clipboard.c index 105c2889e3..9d8c27d451 100644 --- a/third-party/SDL/src/video/SDL_clipboard.c +++ b/third-party/SDL/src/video/SDL_clipboard.c @@ -42,6 +42,10 @@ void SDL_CancelClipboardData(Uint32 sequence) { SDL_VideoDevice *_this = SDL_GetVideoDevice(); + if (!_this) { + return; + } + if (sequence && sequence != _this->clipboard_sequence) { // This clipboard data was already canceled return; @@ -62,6 +66,10 @@ bool SDL_SaveClipboardMimeTypes(const char **mime_types, size_t num_mime_types) { SDL_VideoDevice *_this = SDL_GetVideoDevice(); + if (!_this) { + return SDL_UninitializedVideo(); + } + SDL_FreeClipboardMimeTypes(_this); if (mime_types && num_mime_types > 0) { @@ -234,13 +242,11 @@ bool SDL_HasClipboardData(const char *mime_type) SDL_VideoDevice *_this = SDL_GetVideoDevice(); if (!_this) { - SDL_UninitializedVideo(); - return false; + return SDL_UninitializedVideo(); } if (!mime_type) { - SDL_InvalidParamError("mime_type"); - return false; + return SDL_InvalidParamError("mime_type"); } if (_this->HasClipboardData) { diff --git a/third-party/SDL/src/video/SDL_surface.c b/third-party/SDL/src/video/SDL_surface.c index 73049a50be..92715d2efc 100644 --- a/third-party/SDL/src/video/SDL_surface.c +++ b/third-party/SDL/src/video/SDL_surface.c @@ -1247,7 +1247,7 @@ bool SDL_BlitSurfaceScaled(SDL_Surface *src, const SDL_Rect *srcrect, SDL_Surfac // Clip again SDL_GetRectIntersection(clip_rect, &final_dst, &final_dst); - if (final_dst.w == 0 || final_dst.h == 0 || + if (final_dst.w <= 0 || final_dst.h <= 0 || final_src.w < 0 || final_src.h < 0) { // No-op. return true; diff --git a/third-party/SDL/src/video/SDL_sysvideo.h b/third-party/SDL/src/video/SDL_sysvideo.h index ad5bed288b..ec8489183c 100644 --- a/third-party/SDL/src/video/SDL_sysvideo.h +++ b/third-party/SDL/src/video/SDL_sysvideo.h @@ -103,6 +103,7 @@ struct SDL_Window bool restore_on_show; // Child was hidden recursively by the parent, restore when shown. bool last_position_pending; // This should NOT be cleared by the backend, as it is used for fullscreen positioning. bool last_size_pending; // This should be cleared by the backend if the new size cannot be applied. + bool constrain_popup; bool is_destroying; bool is_dropping; // drag/drop in progress, expecting SDL_SendDropComplete(). @@ -129,6 +130,9 @@ struct SDL_Window SDL_WindowData *internal; + // If a toplevel window, holds the current keyboard focus for grabbing popups. + SDL_Window *keyboard_focus; + SDL_Window *prev; SDL_Window *next; @@ -565,6 +569,8 @@ extern bool SDL_RecreateWindow(SDL_Window *window, SDL_WindowFlags flags); extern bool SDL_HasWindows(void); extern void SDL_RelativeToGlobalForWindow(SDL_Window *window, int rel_x, int rel_y, int *abs_x, int *abs_y); extern void SDL_GlobalToRelativeForWindow(SDL_Window *window, int abs_x, int abs_y, int *rel_x, int *rel_y); +extern bool SDL_ShouldFocusPopup(SDL_Window *window); +extern bool SDL_ShouldRelinquishPopupFocus(SDL_Window *window, SDL_Window **new_focus); extern void SDL_OnDisplayAdded(SDL_VideoDisplay *display); extern void SDL_OnDisplayMoved(SDL_VideoDisplay *display); diff --git a/third-party/SDL/src/video/SDL_video.c b/third-party/SDL/src/video/SDL_video.c index db38a19d0d..fdfe239c8c 100644 --- a/third-party/SDL/src/video/SDL_video.c +++ b/third-party/SDL/src/video/SDL_video.c @@ -171,9 +171,10 @@ static VideoBootStrap *bootstrap[] = { } #if defined(SDL_PLATFORM_MACOS) && defined(SDL_VIDEO_DRIVER_COCOA) -// Support for macOS fullscreen spaces +// Support for macOS fullscreen spaces, etc. extern bool Cocoa_IsWindowInFullscreenSpace(SDL_Window *window); extern bool Cocoa_SetWindowFullscreenSpace(SDL_Window *window, bool state, bool blocking); +extern bool Cocoa_IsShowingModalDialog(SDL_Window *window); #endif #ifdef SDL_VIDEO_DRIVER_UIKIT @@ -2503,6 +2504,7 @@ SDL_Window *SDL_CreateWindowWithProperties(SDL_PropertiesID props) window->is_destroying = false; window->last_displayID = SDL_GetDisplayForWindow(window); window->external_graphics_context = external_graphics_context; + window->constrain_popup = SDL_GetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_CONSTRAIN_POPUP_BOOLEAN, true); if (_this->windows) { _this->windows->prev = window; @@ -3661,6 +3663,10 @@ bool SDL_SetWindowParent(SDL_Window *window, SDL_Window *parent) CHECK_WINDOW_NOT_POPUP(parent, false); } + if (window == parent) { + return SDL_SetError("Cannot set the parent of a window to itself."); + } + if (!_this->SetWindowParent) { return SDL_Unsupported(); } @@ -3706,6 +3712,48 @@ bool SDL_SetWindowModal(SDL_Window *window, bool modal) return _this->SetWindowModal(_this, window, modal); } +bool SDL_ShouldRelinquishPopupFocus(SDL_Window *window, SDL_Window **new_focus) +{ + SDL_Window *focus = window->parent; + bool set_focus = !!(window->flags & SDL_WINDOW_INPUT_FOCUS); + + // Find the highest level window, up to the toplevel parent, that isn't being hidden or destroyed, and can grab the keyboard focus. + while (SDL_WINDOW_IS_POPUP(focus) && ((focus->flags & SDL_WINDOW_NOT_FOCUSABLE) || focus->is_hiding || focus->is_destroying)) { + focus = focus->parent; + + // If some window in the chain currently had focus, set it to the new lowest-level window. + if (!set_focus) { + set_focus = !!(focus->flags & SDL_WINDOW_INPUT_FOCUS); + } + } + + *new_focus = focus; + return set_focus; +} + +bool SDL_ShouldFocusPopup(SDL_Window *window) +{ + SDL_Window *toplevel_parent; + for (toplevel_parent = window->parent; SDL_WINDOW_IS_POPUP(toplevel_parent); toplevel_parent = toplevel_parent->parent) { + } + + SDL_Window *current_focus = toplevel_parent->keyboard_focus; + bool found_higher_focus = false; + + /* Traverse the window tree from the currently focused window to the toplevel parent and see if we encounter + * the new focus request. If the new window is found, a higher-level window already has focus. + */ + SDL_Window *w; + for (w = current_focus; w != toplevel_parent; w = w->parent) { + if (w == window) { + found_higher_focus = true; + break; + } + } + + return !found_higher_focus || w == toplevel_parent; +} + bool SDL_SetWindowFocusable(SDL_Window *window, bool focusable) { CHECK_WINDOW_MAGIC(window, false); @@ -4124,7 +4172,9 @@ static bool SDL_ShouldMinimizeOnFocusLoss(SDL_Window *window) #if defined(SDL_PLATFORM_MACOS) && defined(SDL_VIDEO_DRIVER_COCOA) if (SDL_strcmp(_this->name, "cocoa") == 0) { // don't do this for X11, etc - if (Cocoa_IsWindowInFullscreenSpace(window)) { + if (Cocoa_IsShowingModalDialog(window)) { + return false; // modal system dialogs can live over fullscreen windows, don't minimize. + } else if (Cocoa_IsWindowInFullscreenSpace(window)) { return false; } } diff --git a/third-party/SDL/src/video/cocoa/SDL_cocoaevents.m b/third-party/SDL/src/video/cocoa/SDL_cocoaevents.m index 58cae99554..fd178b8dac 100644 --- a/third-party/SDL/src/video/cocoa/SDL_cocoaevents.m +++ b/third-party/SDL/src/video/cocoa/SDL_cocoaevents.m @@ -311,15 +311,26 @@ static void Cocoa_DispatchEvent(NSEvent *theEvent) - (void)applicationDidFinishLaunching:(NSNotification *)notification { - if (!SDL_GetHintBoolean("SDL_MAC_REGISTER_ACTIVATION_HANDLERS", true)) + if (!SDL_GetHintBoolean("SDL_MAC_REGISTER_ACTIVATION_HANDLERS", true)) { return; + } /* The menu bar of SDL apps which don't have the typical .app bundle * structure fails to work the first time a window is created (until it's * de-focused and re-focused), if this call is in Cocoa_RegisterApp instead - * of here. https://bugzilla.libsdl.org/show_bug.cgi?id=3051 + * of here. https://github.com/libsdl-org/SDL/issues/1913 */ - if (!SDL_GetHintBoolean(SDL_HINT_MAC_BACKGROUND_APP, false)) { + + /* this apparently became unnecessary on macOS 14.0, and will addition pop up a + hidden dock if you're moving the mouse during launch, so change the default + behaviour there. https://github.com/libsdl-org/SDL/issues/10340 + (13.6 still needs it, presumably 13.7 does, too.) */ + bool background_app_default = false; + if (@available(macOS 14.0, *)) { + background_app_default = true; /* by default, don't explicitly activate the dock and then us again to force to foreground */ + } + + if (!SDL_GetHintBoolean(SDL_HINT_MAC_BACKGROUND_APP, background_app_default)) { // Get more aggressive for Catalina: activate the Dock first so we definitely reset all activation state. for (NSRunningApplication *i in [NSRunningApplication runningApplicationsWithBundleIdentifier:@"com.apple.dock"]) { [i activateWithOptions:NSApplicationActivateIgnoringOtherApps]; diff --git a/third-party/SDL/src/video/cocoa/SDL_cocoawindow.h b/third-party/SDL/src/video/cocoa/SDL_cocoawindow.h index 6df69f442a..e4ab6efed4 100644 --- a/third-party/SDL/src/video/cocoa/SDL_cocoawindow.h +++ b/third-party/SDL/src/video/cocoa/SDL_cocoawindow.h @@ -146,13 +146,13 @@ typedef enum @property(nonatomic) BOOL was_zoomed; @property(nonatomic) NSInteger window_number; @property(nonatomic) NSInteger flash_request; -@property(nonatomic) SDL_Window *keyboard_focus; @property(nonatomic) SDL3Cocoa_WindowListener *listener; @property(nonatomic) NSModalSession modal_session; @property(nonatomic) SDL_CocoaVideoData *videodata; @property(nonatomic) bool pending_size; @property(nonatomic) bool pending_position; @property(nonatomic) bool border_toggled; +@property(nonatomic) bool has_modal_dialog; #ifdef SDL_VIDEO_OPENGL_EGL @property(nonatomic) EGLSurface egl_surface; diff --git a/third-party/SDL/src/video/cocoa/SDL_cocoawindow.m b/third-party/SDL/src/video/cocoa/SDL_cocoawindow.m index 7be564af0f..27f9bd3d71 100644 --- a/third-party/SDL/src/video/cocoa/SDL_cocoawindow.m +++ b/third-party/SDL/src/video/cocoa/SDL_cocoawindow.m @@ -432,6 +432,18 @@ bool Cocoa_IsWindowZoomed(SDL_Window *window) return zoomed; } +bool Cocoa_IsShowingModalDialog(SDL_Window *window) +{ + SDL_CocoaWindowData *data = (__bridge SDL_CocoaWindowData *)window->internal; + return data.has_modal_dialog; +} + +void Cocoa_SetWindowHasModalDialog(SDL_Window *window, bool has_modal) +{ + SDL_CocoaWindowData *data = (__bridge SDL_CocoaWindowData *)window->internal; + data.has_modal_dialog = has_modal; +} + typedef enum CocoaMenuVisibility { COCOA_MENU_VISIBILITY_AUTO = 0, @@ -707,10 +719,7 @@ static SDL_Window *GetParentToplevelWindow(SDL_Window *window) static void Cocoa_SetKeyboardFocus(SDL_Window *window, bool set_active_focus) { SDL_Window *toplevel = GetParentToplevelWindow(window); - SDL_CocoaWindowData *toplevel_data; - - toplevel_data = (__bridge SDL_CocoaWindowData *)toplevel->internal; - toplevel_data.keyboard_focus = window; + toplevel->keyboard_focus = window; if (set_active_focus && !window->is_hiding && !window->is_destroying) { SDL_SetKeyboardFocus(window); @@ -1190,21 +1199,27 @@ static NSCursor *Cocoa_GetDesiredCursor(void) ScheduleContextUpdates(_data); - /* isZoomed always returns true if the window is not resizable - * and fullscreen windows are considered zoomed. + /* The OS can resize the window automatically if the display density + * changes while the window is miniaturized or hidden. */ - if ((window->flags & SDL_WINDOW_RESIZABLE) && [nswindow isZoomed] && - !(window->flags & SDL_WINDOW_FULLSCREEN) && ![self isInFullscreenSpace]) { - zoomed = YES; - } else { - zoomed = NO; - } - if (!zoomed) { - SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESTORED, 0, 0); - } else { - SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_MAXIMIZED, 0, 0); - if ([self windowOperationIsPending:PENDING_OPERATION_MINIMIZE]) { - [nswindow miniaturize:nil]; + if ([nswindow isVisible]) + { + /* isZoomed always returns true if the window is not resizable + * and fullscreen windows are considered zoomed. + */ + if ((window->flags & SDL_WINDOW_RESIZABLE) && [nswindow isZoomed] && + !(window->flags & SDL_WINDOW_FULLSCREEN) && ![self isInFullscreenSpace]) { + zoomed = YES; + } else { + zoomed = NO; + } + if (!zoomed) { + SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESTORED, 0, 0); + } else { + SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_MAXIMIZED, 0, 0); + if ([self windowOperationIsPending:PENDING_OPERATION_MINIMIZE]) { + [nswindow miniaturize:nil]; + } } } @@ -1252,7 +1267,7 @@ static NSCursor *Cocoa_GetDesiredCursor(void) // We're going to get keyboard events, since we're key. // This needs to be done before restoring the relative mouse mode. - Cocoa_SetKeyboardFocus(_data.keyboard_focus ? _data.keyboard_focus : window, true); + Cocoa_SetKeyboardFocus(window->keyboard_focus ? window->keyboard_focus : window, true); // If we just gained focus we need the updated mouse position if (!(window->flags & SDL_WINDOW_MOUSE_RELATIVE_MODE)) { @@ -2261,7 +2276,9 @@ static bool SetupWindowData(SDL_VideoDevice *_this, SDL_Window *window, NSWindow [nswindow setIgnoresMouseEvents:YES]; [nswindow setAcceptsMouseMovedEvents:NO]; } else if ((window->flags & SDL_WINDOW_POPUP_MENU) && !(window->flags & SDL_WINDOW_HIDDEN)) { - Cocoa_SetKeyboardFocus(window, window->parent == SDL_GetKeyboardFocus()); + if (!(window->flags & SDL_WINDOW_NOT_FOCUSABLE)) { + Cocoa_SetKeyboardFocus(window, true); + } Cocoa_UpdateMouseFocus(); } } @@ -2351,7 +2368,7 @@ bool Cocoa_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_Properti rect.origin.y -= screenRect.origin.y; // Constrain the popup - if (SDL_WINDOW_IS_POPUP(window)) { + if (SDL_WINDOW_IS_POPUP(window) && window->constrain_popup) { if (rect.origin.x + rect.size.width > screenRect.origin.x + screenRect.size.width) { rect.origin.x -= (rect.origin.x + rect.size.width) - (screenRect.origin.x + screenRect.size.width); } @@ -2507,7 +2524,7 @@ bool Cocoa_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window *window) ConvertNSRect(&rect); // Position and constrain the popup - if (SDL_WINDOW_IS_POPUP(window)) { + if (SDL_WINDOW_IS_POPUP(window) && window->constrain_popup) { NSRect screenRect = [ScreenForRect(&rect) frame]; if (rect.origin.x + rect.size.width > screenRect.origin.x + screenRect.size.width) { @@ -2648,7 +2665,9 @@ void Cocoa_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window) } } } else if (window->flags & SDL_WINDOW_POPUP_MENU) { - Cocoa_SetKeyboardFocus(window, window->parent == SDL_GetKeyboardFocus()); + if (!(window->flags & SDL_WINDOW_NOT_FOCUSABLE)) { + Cocoa_SetKeyboardFocus(window, true); + } Cocoa_UpdateMouseFocus(); } } @@ -2682,20 +2701,9 @@ void Cocoa_HideWindow(SDL_VideoDevice *_this, SDL_Window *window) Cocoa_SetWindowModal(_this, window, false); // Transfer keyboard focus back to the parent when closing a popup menu - if (window->flags & SDL_WINDOW_POPUP_MENU) { - SDL_Window *new_focus = window->parent; - bool set_focus = window == SDL_GetKeyboardFocus(); - - // Find the highest level window, up to the toplevel parent, that isn't being hidden or destroyed. - while (SDL_WINDOW_IS_POPUP(new_focus) && (new_focus->is_hiding || new_focus->is_destroying)) { - new_focus = new_focus->parent; - - // If some window in the chain currently had focus, set it to the new lowest-level window. - if (!set_focus) { - set_focus = new_focus == SDL_GetKeyboardFocus(); - } - } - + if ((window->flags & SDL_WINDOW_POPUP_MENU) && !(window->flags & SDL_WINDOW_NOT_FOCUSABLE)) { + SDL_Window *new_focus; + const bool set_focus = SDL_ShouldRelinquishPopupFocus(window, &new_focus); Cocoa_SetKeyboardFocus(new_focus, set_focus); Cocoa_UpdateMouseFocus(); } else if (window->parent && waskey) { @@ -3122,20 +3130,19 @@ void Cocoa_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window) #endif // SDL_VIDEO_OPENGL SDL_Window *topmost = GetParentToplevelWindow(window); - SDL_CocoaWindowData *topmost_data = (__bridge SDL_CocoaWindowData *)topmost->internal; /* Reset the input focus of the root window if this window is still set as keyboard focus. * SDL_DestroyWindow will have already taken care of reassigning focus if this is the SDL * keyboard focus, this ensures that an inactive window with this window set as input focus * does not try to reference it the next time it gains focus. */ - if (topmost_data.keyboard_focus == window) { + if (topmost->keyboard_focus == window) { SDL_Window *new_focus = window; while (SDL_WINDOW_IS_POPUP(new_focus) && (new_focus->is_hiding || new_focus->is_destroying)) { new_focus = new_focus->parent; } - topmost_data.keyboard_focus = new_focus; + topmost->keyboard_focus = new_focus; } if ([data.listener isInFullscreenSpace]) { @@ -3300,6 +3307,20 @@ bool Cocoa_FlashWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_FlashOper bool Cocoa_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, bool focusable) { + if (window->flags & SDL_WINDOW_POPUP_MENU) { + if (!(window->flags & SDL_WINDOW_HIDDEN)) { + if (!focusable && (window->flags & SDL_WINDOW_INPUT_FOCUS)) { + SDL_Window *new_focus; + const bool set_focus = SDL_ShouldRelinquishPopupFocus(window, &new_focus); + Cocoa_SetKeyboardFocus(new_focus, set_focus); + } else if (focusable) { + if (SDL_ShouldFocusPopup(window)) { + Cocoa_SetKeyboardFocus(window, true); + } + } + } + } + return true; // just succeed, the real work is done elsewhere. } diff --git a/third-party/SDL/src/video/wayland/SDL_waylandevents.c b/third-party/SDL/src/video/wayland/SDL_waylandevents.c index 291a467647..6b24bce11f 100644 --- a/third-party/SDL/src/video/wayland/SDL_waylandevents.c +++ b/third-party/SDL/src/video/wayland/SDL_waylandevents.c @@ -1717,7 +1717,7 @@ static void keyboard_handle_enter(void *data, struct wl_keyboard *keyboard, window->keyboard_device = input; // Restore the keyboard focus to the child popup that was holding it - SDL_SetKeyboardFocus(window->keyboard_focus ? window->keyboard_focus : window->sdlwindow); + SDL_SetKeyboardFocus(window->sdlwindow->keyboard_focus ? window->sdlwindow->keyboard_focus : window->sdlwindow); #ifdef SDL_USE_IME if (!input->text_input) { diff --git a/third-party/SDL/src/video/wayland/SDL_waylandvideo.c b/third-party/SDL/src/video/wayland/SDL_waylandvideo.c index 47971e6a30..d6cd949c03 100644 --- a/third-party/SDL/src/video/wayland/SDL_waylandvideo.c +++ b/third-party/SDL/src/video/wayland/SDL_waylandvideo.c @@ -642,6 +642,7 @@ static SDL_VideoDevice *Wayland_CreateDevice(bool require_preferred_protocols) device->HasScreenKeyboardSupport = Wayland_HasScreenKeyboardSupport; device->ShowWindowSystemMenu = Wayland_ShowWindowSystemMenu; device->SyncWindow = Wayland_SyncWindow; + device->SetWindowFocusable = Wayland_SetWindowFocusable; #ifdef SDL_USE_LIBDBUS if (SDL_SystemTheme_Init()) diff --git a/third-party/SDL/src/video/wayland/SDL_waylandwindow.c b/third-party/SDL/src/video/wayland/SDL_waylandwindow.c index 1fea144e17..75d18bd32f 100644 --- a/third-party/SDL/src/video/wayland/SDL_waylandwindow.c +++ b/third-party/SDL/src/video/wayland/SDL_waylandwindow.c @@ -181,7 +181,7 @@ static void SetMinMaxDimensions(SDL_Window *window) #ifdef HAVE_LIBDECOR_H if (wind->shell_surface_type == WAYLAND_SHELL_SURFACE_TYPE_LIBDECOR) { - if (!wind->shell_surface.libdecor.initial_configure_seen || !wind->shell_surface.libdecor.frame) { + if (!wind->shell_surface.libdecor.frame) { return; // Can't do anything yet, wait for ShowWindow } /* No need to change these values if the window is non-resizable, @@ -198,7 +198,7 @@ static void SetMinMaxDimensions(SDL_Window *window) } else #endif if (wind->shell_surface_type == WAYLAND_SHELL_SURFACE_TYPE_XDG_TOPLEVEL) { - if (wind->shell_surface.xdg.toplevel.xdg_toplevel == NULL) { + if (!wind->shell_surface.xdg.toplevel.xdg_toplevel) { return; // Can't do anything yet, wait for ShowWindow } xdg_toplevel_set_min_size(wind->shell_surface.xdg.toplevel.xdg_toplevel, @@ -215,32 +215,32 @@ static void EnsurePopupPositionIsValid(SDL_Window *window, int *x, int *y) int adj_count = 0; /* Per the xdg-positioner spec, child popup windows must intersect or at - * least be partially adjacent to the parent window. + * least be partially adjoining the parent window. * * Failure to ensure this on a compositor that enforces this restriction * can result in behavior ranging from the window being spuriously closed * to a protocol violation. */ - if (*x + window->w < 0) { + if (*x + window->w <= 0) { *x = -window->w; ++adj_count; } - if (*y + window->h < 0) { + if (*y + window->h <= 0) { *y = -window->h; ++adj_count; } - if (*x > window->parent->w) { + if (*x >= window->parent->w) { *x = window->parent->w; ++adj_count; } - if (*y > window->parent->h) { + if (*y >= window->parent->h) { *y = window->parent->h; ++adj_count; } /* If adjustment was required on the x and y axes, the popup is aligned with - * the parent corner-to-corner and is neither overlapping nor adjacent, so it - * must be nudged by 1 to be considered adjacent. + * the parent corner-to-corner and is neither overlapping nor adjoining, so it + * must be nudged by 1 to be considered adjoining. */ if (adj_count > 1) { *x += *x < 0 ? 1 : -1; @@ -753,7 +753,9 @@ static void handle_configure_xdg_shell_surface(void *data, struct xdg_surface *x xdg_surface_ack_configure(xdg, serial); } - wind->shell_surface.xdg.initial_configure_seen = true; + if (wind->shell_surface_status == WAYLAND_SHELL_SURFACE_STATUS_WAITING_FOR_CONFIGURE) { + wind->shell_surface_status = WAYLAND_SHELL_SURFACE_STATUS_WAITING_FOR_FRAME; + } } static const struct xdg_surface_listener shell_surface_listener_xdg = { @@ -970,10 +972,6 @@ static void handle_configure_xdg_toplevel(void *data, wind->active = active; window->tiled = tiled; wind->resizing = resizing; - - if (wind->shell_surface_status == WAYLAND_SHELL_SURFACE_STATUS_WAITING_FOR_CONFIGURE) { - wind->shell_surface_status = WAYLAND_SHELL_SURFACE_STATUS_WAITING_FOR_FRAME; - } } static void handle_close_xdg_toplevel(void *data, struct xdg_toplevel *xdg_toplevel) @@ -1122,9 +1120,7 @@ static void handle_configure_zxdg_decoration(void *data, WAYLAND_wl_display_roundtrip(internal->waylandData->display); Wayland_HideWindow(device, window); - SDL_zero(internal->shell_surface); internal->shell_surface_type = WAYLAND_SHELL_SURFACE_TYPE_LIBDECOR; - Wayland_ShowWindow(device, window); } } @@ -1406,11 +1402,8 @@ static void decoration_frame_configure(struct libdecor_frame *frame, libdecor_state_free(state); } - if (!wind->shell_surface.libdecor.initial_configure_seen) { - LibdecorGetMinContentSize(frame, &wind->system_limits.min_width, &wind->system_limits.min_height); - wind->shell_surface.libdecor.initial_configure_seen = true; - } if (wind->shell_surface_status == WAYLAND_SHELL_SURFACE_STATUS_WAITING_FOR_CONFIGURE) { + LibdecorGetMinContentSize(frame, &wind->system_limits.min_width, &wind->system_limits.min_height); wind->shell_surface_status = WAYLAND_SHELL_SURFACE_STATUS_WAITING_FOR_FRAME; } @@ -1666,7 +1659,7 @@ static const struct wp_color_management_surface_feedback_v1_listener color_manag feedback_surface_preferred_changed }; -static void SetKeyboardFocus(SDL_Window *window, bool set_focus) +static void Wayland_SetKeyboardFocus(SDL_Window *window, bool set_focus) { SDL_Window *toplevel = window; @@ -1675,7 +1668,7 @@ static void SetKeyboardFocus(SDL_Window *window, bool set_focus) toplevel = toplevel->parent; } - toplevel->internal->keyboard_focus = window; + toplevel->keyboard_focus = window; if (set_focus && !window->is_hiding && !window->is_destroying) { SDL_SetKeyboardFocus(window); @@ -1819,12 +1812,10 @@ void Wayland_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window) } } - /* The window was hidden, but the sync point hasn't yet been reached. - * Pump events to avoid a possible protocol violation. - */ - if (data->show_hide_sync_required) { + // Always roundtrip to ensure there are no pending buffer attachments. + do { WAYLAND_wl_display_roundtrip(c->display); - } + } while (data->show_hide_sync_required); data->shell_surface_status = WAYLAND_SHELL_SURFACE_STATUS_WAITING_FOR_CONFIGURE; @@ -1905,8 +1896,9 @@ void Wayland_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window) data->shell_surface.xdg.popup.xdg_positioner = xdg_wm_base_create_positioner(c->shell.xdg); xdg_positioner_set_anchor(data->shell_surface.xdg.popup.xdg_positioner, XDG_POSITIONER_ANCHOR_TOP_LEFT); xdg_positioner_set_anchor_rect(data->shell_surface.xdg.popup.xdg_positioner, 0, 0, parent->internal->current.logical_width, parent->internal->current.logical_width); - xdg_positioner_set_constraint_adjustment(data->shell_surface.xdg.popup.xdg_positioner, - XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X | XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y); + + const Uint32 constraint = window->constrain_popup ? (XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X | XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y) : XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_NONE; + xdg_positioner_set_constraint_adjustment(data->shell_surface.xdg.popup.xdg_positioner, constraint); xdg_positioner_set_gravity(data->shell_surface.xdg.popup.xdg_positioner, XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT); xdg_positioner_set_size(data->shell_surface.xdg.popup.xdg_positioner, data->current.logical_width, data->current.logical_height); @@ -1935,8 +1927,8 @@ void Wayland_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window) wl_region_add(region, 0, 0, 0, 0); wl_surface_set_input_region(data->surface, region); wl_region_destroy(region); - } else if (window->flags & SDL_WINDOW_POPUP_MENU) { - SetKeyboardFocus(window, window->parent == SDL_GetKeyboardFocus()); + } else if ((window->flags & SDL_WINDOW_POPUP_MENU) && !(window->flags & SDL_WINDOW_NOT_FOCUSABLE)) { + Wayland_SetKeyboardFocus(window, true); } SDL_SetPointerProperty(props, SDL_PROP_WINDOW_WAYLAND_XDG_POPUP_POINTER, data->shell_surface.xdg.popup.xdg_popup); @@ -1984,7 +1976,7 @@ void Wayland_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window) #ifdef HAVE_LIBDECOR_H if (data->shell_surface_type == WAYLAND_SHELL_SURFACE_TYPE_LIBDECOR) { if (data->shell_surface.libdecor.frame) { - while (!data->shell_surface.libdecor.initial_configure_seen) { + while (data->shell_surface_status == WAYLAND_SHELL_SURFACE_STATUS_WAITING_FOR_CONFIGURE) { WAYLAND_wl_display_flush(c->display); WAYLAND_wl_display_dispatch(c->display); } @@ -1998,7 +1990,7 @@ void Wayland_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window) */ wl_surface_commit(data->surface); if (data->shell_surface.xdg.surface) { - while (!data->shell_surface.xdg.initial_configure_seen) { + while (data->shell_surface_status == WAYLAND_SHELL_SURFACE_STATUS_WAITING_FOR_CONFIGURE) { WAYLAND_wl_display_flush(c->display); WAYLAND_wl_display_dispatch(c->display); } @@ -2083,21 +2075,10 @@ static void Wayland_ReleasePopup(SDL_VideoDevice *_this, SDL_Window *popup) return; } - if (popup->flags & SDL_WINDOW_POPUP_MENU) { - SDL_Window *new_focus = popup->parent; - bool set_focus = popup == SDL_GetKeyboardFocus(); - - // Find the highest level window, up to the toplevel parent, that isn't being hidden or destroyed. - while (SDL_WINDOW_IS_POPUP(new_focus) && (new_focus->is_hiding || new_focus->is_destroying)) { - new_focus = new_focus->parent; - - // If some window in the chain currently had focus, set it to the new lowest-level window. - if (!set_focus) { - set_focus = new_focus == SDL_GetKeyboardFocus(); - } - } - - SetKeyboardFocus(new_focus, set_focus); + if ((popup->flags & SDL_WINDOW_POPUP_MENU) && !(popup->flags & SDL_WINDOW_NOT_FOCUSABLE)) { + SDL_Window *new_focus; + const bool set_focus = SDL_ShouldRelinquishPopupFocus(popup, &new_focus); + Wayland_SetKeyboardFocus(new_focus, set_focus); } xdg_popup_destroy(popupdata->shell_surface.xdg.popup.xdg_popup); @@ -2135,12 +2116,6 @@ void Wayland_HideWindow(SDL_VideoDevice *_this, SDL_Window *window) wind->server_decoration = NULL; } - // Be sure to detach after this is done, otherwise ShowWindow crashes! - if (wind->shell_surface_type != WAYLAND_SHELL_SURFACE_TYPE_XDG_POPUP) { - wl_surface_attach(wind->surface, NULL, 0, 0); - wl_surface_commit(wind->surface); - } - // Clean up the export handle. if (wind->exported) { zxdg_exported_v2_destroy(wind->exported); @@ -2158,26 +2133,31 @@ void Wayland_HideWindow(SDL_VideoDevice *_this, SDL_Window *window) if (wind->shell_surface_type == WAYLAND_SHELL_SURFACE_TYPE_LIBDECOR) { if (wind->shell_surface.libdecor.frame) { libdecor_frame_unref(wind->shell_surface.libdecor.frame); - wind->shell_surface.libdecor.frame = NULL; SDL_SetPointerProperty(props, SDL_PROP_WINDOW_WAYLAND_XDG_SURFACE_POINTER, NULL); SDL_SetPointerProperty(props, SDL_PROP_WINDOW_WAYLAND_XDG_TOPLEVEL_POINTER, NULL); } } else #endif + { if (wind->shell_surface_type == WAYLAND_SHELL_SURFACE_TYPE_XDG_POPUP) { - Wayland_ReleasePopup(_this, window); - } else if (wind->shell_surface.xdg.toplevel.xdg_toplevel) { - xdg_toplevel_destroy(wind->shell_surface.xdg.toplevel.xdg_toplevel); - wind->shell_surface.xdg.toplevel.xdg_toplevel = NULL; - SDL_SetPointerProperty(props, SDL_PROP_WINDOW_WAYLAND_XDG_TOPLEVEL_POINTER, NULL); - } - if (wind->shell_surface.xdg.surface) { - xdg_surface_destroy(wind->shell_surface.xdg.surface); - wind->shell_surface.xdg.surface = NULL; - SDL_SetPointerProperty(props, SDL_PROP_WINDOW_WAYLAND_XDG_SURFACE_POINTER, NULL); + Wayland_ReleasePopup(_this, window); + } else if (wind->shell_surface.xdg.toplevel.xdg_toplevel) { + xdg_toplevel_destroy(wind->shell_surface.xdg.toplevel.xdg_toplevel); + SDL_SetPointerProperty(props, SDL_PROP_WINDOW_WAYLAND_XDG_TOPLEVEL_POINTER, NULL); + } + + if (wind->shell_surface.xdg.surface) { + xdg_surface_destroy(wind->shell_surface.xdg.surface); + SDL_SetPointerProperty(props, SDL_PROP_WINDOW_WAYLAND_XDG_SURFACE_POINTER, NULL); + } } + // Attach a null buffer to unmap the surface. + wl_surface_attach(wind->surface, NULL, 0, 0); + wl_surface_commit(wind->surface); + + SDL_zero(wind->shell_surface); wind->show_hide_sync_required = true; struct wl_callback *cb = wl_display_sync(_this->internal->display); wl_callback_add_listener(cb, &show_hide_sync_listener, (void*)((uintptr_t)window->id)); @@ -2991,6 +2971,27 @@ bool Wayland_SyncWindow(SDL_VideoDevice *_this, SDL_Window *window) return true; } +bool Wayland_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, bool focusable) +{ + if (window->flags & SDL_WINDOW_POPUP_MENU) { + if (!(window->flags & SDL_WINDOW_HIDDEN)) { + if (!focusable && (window->flags & SDL_WINDOW_INPUT_FOCUS)) { + SDL_Window *new_focus; + const bool set_focus = SDL_ShouldRelinquishPopupFocus(window, &new_focus); + Wayland_SetKeyboardFocus(new_focus, set_focus); + } else if (focusable) { + if (SDL_ShouldFocusPopup(window)) { + Wayland_SetKeyboardFocus(window, true); + } + } + } + + return true; + } + + return SDL_SetError("wayland: focus can only be toggled on popup menu windows"); +} + void Wayland_ShowWindowSystemMenu(SDL_Window *window, int x, int y) { SDL_WindowData *wind = window->internal; diff --git a/third-party/SDL/src/video/wayland/SDL_waylandwindow.h b/third-party/SDL/src/video/wayland/SDL_waylandwindow.h index ee27f337b8..569f8707c4 100644 --- a/third-party/SDL/src/video/wayland/SDL_waylandwindow.h +++ b/third-party/SDL/src/video/wayland/SDL_waylandwindow.h @@ -48,7 +48,6 @@ struct SDL_WindowData struct { struct libdecor_frame *frame; - bool initial_configure_seen; } libdecor; #endif struct @@ -66,7 +65,6 @@ struct SDL_WindowData struct xdg_positioner *xdg_positioner; } popup; }; - bool initial_configure_seen; } xdg; } shell_surface; enum @@ -125,8 +123,6 @@ struct SDL_WindowData SDL_DisplayData **outputs; int num_outputs; - SDL_Window *keyboard_focus; - char *app_id; double scale_factor; @@ -237,6 +233,7 @@ extern void Wayland_ShowWindowSystemMenu(SDL_Window *window, int x, int y); extern void Wayland_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window); extern bool Wayland_SuspendScreenSaver(SDL_VideoDevice *_this); extern bool Wayland_SetWindowIcon(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surface *icon); +extern bool Wayland_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, bool focusable); extern float Wayland_GetWindowContentScale(SDL_VideoDevice *_this, SDL_Window *window); extern void *Wayland_GetWindowICCProfile(SDL_VideoDevice *_this, SDL_Window *window, size_t *size); diff --git a/third-party/SDL/src/video/windows/SDL_windowsevents.c b/third-party/SDL/src/video/windows/SDL_windowsevents.c index 837e0eb26c..b3bb2749f4 100644 --- a/third-party/SDL/src/video/windows/SDL_windowsevents.c +++ b/third-party/SDL/src/video/windows/SDL_windowsevents.c @@ -385,7 +385,7 @@ static void WIN_UpdateFocus(SDL_Window *window, bool expect_focus, DWORD pos) } } - SDL_SetKeyboardFocus(data->keyboard_focus ? data->keyboard_focus : window); + SDL_SetKeyboardFocus(window->keyboard_focus ? window->keyboard_focus : window); // In relative mode we are guaranteed to have mouse focus if we have keyboard focus if (!SDL_GetMouse()->relative_mode) { @@ -732,6 +732,10 @@ static void WIN_HandleRawMouseInput(Uint64 timestamp, SDL_VideoData *data, HANDL float fAmount = (float)amount / WHEEL_DELTA; SDL_SendMouseWheel(WIN_GetEventTimestamp(), window, mouseID, fAmount, 0.0f, SDL_MOUSEWHEEL_NORMAL); } + + /* Invalidate the mouse button flags. If we don't do this then disabling raw input + will cause held down mouse buttons to persist when released. */ + windowdata->mouse_button_flags = (WPARAM)-1; } } diff --git a/third-party/SDL/src/video/windows/SDL_windowsgameinput.c b/third-party/SDL/src/video/windows/SDL_windowsgameinput.c index 183733a380..3f4ad804ea 100644 --- a/third-party/SDL/src/video/windows/SDL_windowsgameinput.c +++ b/third-party/SDL/src/video/windows/SDL_windowsgameinput.c @@ -277,6 +277,9 @@ static void GAMEINPUT_InitialMouseReading(WIN_GameInputData *data, SDL_Window *w bool down = ((state.buttons & mask) != 0); SDL_SendMouseButton(timestamp, window, mouseID, GAMEINPUT_button_map[i], down); } + + // Invalidate mouse button flags + window->internal->mouse_button_flags = (WPARAM)-1; } } @@ -307,6 +310,9 @@ static void GAMEINPUT_HandleMouseDelta(WIN_GameInputData *data, SDL_Window *wind SDL_SendMouseButton(timestamp, window, mouseID, GAMEINPUT_button_map[i], down); } } + + // Invalidate mouse button flags + window->internal->mouse_button_flags = (WPARAM)-1; } if (delta.wheelX || delta.wheelY) { float fAmountX = (float)delta.wheelX / WHEEL_DELTA; diff --git a/third-party/SDL/src/video/windows/SDL_windowsrawinput.c b/third-party/SDL/src/video/windows/SDL_windowsrawinput.c index fa249914d0..64c612cae9 100644 --- a/third-party/SDL/src/video/windows/SDL_windowsrawinput.c +++ b/third-party/SDL/src/video/windows/SDL_windowsrawinput.c @@ -112,7 +112,9 @@ static DWORD WINAPI WIN_RawInputThread(LPVOID param) } devices[0].dwFlags |= RIDEV_REMOVE; + devices[0].hwndTarget = NULL; devices[1].dwFlags |= RIDEV_REMOVE; + devices[1].hwndTarget = NULL; RegisterRawInputDevices(devices, count, sizeof(devices[0])); DestroyWindow(window); diff --git a/third-party/SDL/src/video/windows/SDL_windowswindow.c b/third-party/SDL/src/video/windows/SDL_windowswindow.c index da752c08cd..5dc16dcad0 100644 --- a/third-party/SDL/src/video/windows/SDL_windowswindow.c +++ b/third-party/SDL/src/video/windows/SDL_windowswindow.c @@ -33,6 +33,7 @@ #include "../SDL_sysvideo.h" #include "SDL_windowsvideo.h" +#include "SDL_windowskeyboard.h" #include "SDL_windowswindow.h" // Dropfile support @@ -91,12 +92,6 @@ typedef void (NTAPI *RtlGetVersion_t)(NT_OSVERSIONINFOW *); // #define HIGHDPI_DEBUG -// Fake window to help with DirectInput events. -HWND SDL_HelperWindow = NULL; -static const TCHAR *SDL_HelperWindowClassName = TEXT("SDLHelperWindowInputCatcher"); -static const TCHAR *SDL_HelperWindowName = TEXT("SDLHelperWindowInputMsgWindow"); -static ATOM SDL_HelperWindowClass = 0; - /* For borderless Windows, still want the following flag: - WS_MINIMIZEBOX: window will respond to Windows minimize commands sent to all windows, such as windows key + m, shaking title bar, etc. Additionally, non-fullscreen windows can add: @@ -638,7 +633,7 @@ static void CleanupWindowData(SDL_VideoDevice *_this, SDL_Window *window) static void WIN_ConstrainPopup(SDL_Window *window, bool output_to_pending) { - // Clamp popup windows to the output borders + // Possibly clamp popup windows to the output borders if (SDL_WINDOW_IS_POPUP(window)) { SDL_Window *w; SDL_DisplayID displayID; @@ -649,28 +644,30 @@ static void WIN_ConstrainPopup(SDL_Window *window, bool output_to_pending) const int height = window->last_size_pending ? window->pending.h : window->floating.h; int offset_x = 0, offset_y = 0; - // Calculate the total offset from the parents - for (w = window->parent; SDL_WINDOW_IS_POPUP(w); w = w->parent) { + if (window->constrain_popup) { + // Calculate the total offset from the parents + for (w = window->parent; SDL_WINDOW_IS_POPUP(w); w = w->parent) { + offset_x += w->x; + offset_y += w->y; + } + offset_x += w->x; offset_y += w->y; - } + abs_x += offset_x; + abs_y += offset_y; - offset_x += w->x; - offset_y += w->y; - abs_x += offset_x; - abs_y += offset_y; - - // Constrain the popup window to the display of the toplevel parent - displayID = SDL_GetDisplayForWindow(w); - SDL_GetDisplayBounds(displayID, &rect); - if (abs_x + width > rect.x + rect.w) { - abs_x -= (abs_x + width) - (rect.x + rect.w); + // Constrain the popup window to the display of the toplevel parent + displayID = SDL_GetDisplayForWindow(w); + SDL_GetDisplayBounds(displayID, &rect); + if (abs_x + width > rect.x + rect.w) { + abs_x -= (abs_x + width) - (rect.x + rect.w); + } + if (abs_y + height > rect.y + rect.h) { + abs_y -= (abs_y + height) - (rect.y + rect.h); + } + abs_x = SDL_max(abs_x, rect.x); + abs_y = SDL_max(abs_y, rect.y); } - if (abs_y + height > rect.y + rect.h) { - abs_y -= (abs_y + height) - (rect.y + rect.h); - } - abs_x = SDL_max(abs_x, rect.x); - abs_y = SDL_max(abs_y, rect.y); if (output_to_pending) { window->pending.x = abs_x - offset_x; @@ -695,7 +692,7 @@ static void WIN_SetKeyboardFocus(SDL_Window *window, bool set_active_focus) toplevel = toplevel->parent; } - toplevel->internal->keyboard_focus = window; + toplevel->keyboard_focus = window; if (set_active_focus && !window->is_hiding && !window->is_destroying) { SDL_SetKeyboardFocus(window); @@ -750,6 +747,9 @@ bool WIN_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_Properties return false; } + // Ensure that the IME isn't active on the new window until explicitly requested. + WIN_StopTextInput(_this, window); + // Inform Windows of the frame change so we can respond to WM_NCCALCSIZE SetWindowPos(hwnd, NULL, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE); @@ -1068,8 +1068,8 @@ void WIN_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window) } data->showing_window = false; - if (window->flags & SDL_WINDOW_POPUP_MENU && bActivate) { - WIN_SetKeyboardFocus(window, window->parent == SDL_GetKeyboardFocus()); + if ((window->flags & SDL_WINDOW_POPUP_MENU) && !(window->flags & SDL_WINDOW_NOT_FOCUSABLE) && bActivate) { + WIN_SetKeyboardFocus(window, true); } if (window->flags & SDL_WINDOW_MODAL) { WIN_SetWindowModal(_this, window, true); @@ -1086,21 +1086,10 @@ void WIN_HideWindow(SDL_VideoDevice *_this, SDL_Window *window) ShowWindow(hwnd, SW_HIDE); - // Transfer keyboard focus back to the parent - if (window->flags & SDL_WINDOW_POPUP_MENU) { - SDL_Window *new_focus = window->parent; - bool set_focus = window == SDL_GetKeyboardFocus(); - - // Find the highest level window, up to the toplevel parent, that isn't being hidden or destroyed. - while (SDL_WINDOW_IS_POPUP(new_focus) && (new_focus->is_hiding || new_focus->is_destroying)) { - new_focus = new_focus->parent; - - // If some window in the chain currently had keyboard focus, set it to the new lowest-level window. - if (!set_focus) { - set_focus = new_focus == SDL_GetKeyboardFocus(); - } - } - + // Transfer keyboard focus back to the parent from a grabbing popup. + if ((window->flags & SDL_WINDOW_POPUP_MENU) && !(window->flags & SDL_WINDOW_NOT_FOCUSABLE)) { + SDL_Window *new_focus; + const bool set_focus = SDL_ShouldRelinquishPopupFocus(window, &new_focus); WIN_SetKeyboardFocus(new_focus, set_focus); } } @@ -1138,7 +1127,7 @@ void WIN_RaiseWindow(SDL_VideoDevice *_this, SDL_Window *window) } if (bActivate) { SetForegroundWindow(hwnd); - if (window->flags & SDL_WINDOW_POPUP_MENU) { + if ((window->flags & SDL_WINDOW_POPUP_MENU) && !(window->flags & SDL_WINDOW_NOT_FOCUSABLE)) { WIN_SetKeyboardFocus(window, window->parent == SDL_GetKeyboardFocus()); } } else { @@ -1515,72 +1504,6 @@ void WIN_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window) CleanupWindowData(_this, window); } -/* - * Creates a HelperWindow used for DirectInput. - */ -bool SDL_HelperWindowCreate(void) -{ - HINSTANCE hInstance = GetModuleHandle(NULL); - WNDCLASS wce; - - // Make sure window isn't created twice. - if (SDL_HelperWindow != NULL) { - return true; - } - - // Create the class. - SDL_zero(wce); - wce.lpfnWndProc = DefWindowProc; - wce.lpszClassName = SDL_HelperWindowClassName; - wce.hInstance = hInstance; - - // Register the class. - SDL_HelperWindowClass = RegisterClass(&wce); - if (SDL_HelperWindowClass == 0 && GetLastError() != ERROR_CLASS_ALREADY_EXISTS) { - return WIN_SetError("Unable to create Helper Window Class"); - } - - // Create the window. - SDL_HelperWindow = CreateWindowEx(0, SDL_HelperWindowClassName, - SDL_HelperWindowName, - WS_OVERLAPPED, CW_USEDEFAULT, - CW_USEDEFAULT, CW_USEDEFAULT, - CW_USEDEFAULT, HWND_MESSAGE, NULL, - hInstance, NULL); - if (!SDL_HelperWindow) { - UnregisterClass(SDL_HelperWindowClassName, hInstance); - return WIN_SetError("Unable to create Helper Window"); - } - - return true; -} - -/* - * Destroys the HelperWindow previously created with SDL_HelperWindowCreate. - */ -void SDL_HelperWindowDestroy(void) -{ - HINSTANCE hInstance = GetModuleHandle(NULL); - - // Destroy the window. - if (SDL_HelperWindow != NULL) { - if (DestroyWindow(SDL_HelperWindow) == 0) { - WIN_SetError("Unable to destroy Helper Window"); - return; - } - SDL_HelperWindow = NULL; - } - - // Unregister the class. - if (SDL_HelperWindowClass != 0) { - if ((UnregisterClass(SDL_HelperWindowClassName, hInstance)) == 0) { - WIN_SetError("Unable to destroy Helper Window Class"); - return; - } - SDL_HelperWindowClass = 0; - } -} - #if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) void WIN_OnWindowEnter(SDL_VideoDevice *_this, SDL_Window *window) { @@ -2254,24 +2177,40 @@ void WIN_ShowWindowSystemMenu(SDL_Window *window, int x, int y) bool WIN_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, bool focusable) { - SDL_WindowData *data = window->internal; - HWND hwnd = data->hwnd; - const LONG style = GetWindowLong(hwnd, GWL_EXSTYLE); + if (!SDL_WINDOW_IS_POPUP(window)) { + SDL_WindowData *data = window->internal; + HWND hwnd = data->hwnd; + const LONG style = GetWindowLong(hwnd, GWL_EXSTYLE); - SDL_assert(style != 0); + SDL_assert(style != 0); - if (focusable) { - if (style & WS_EX_NOACTIVATE) { - if (SetWindowLong(hwnd, GWL_EXSTYLE, style & ~WS_EX_NOACTIVATE) == 0) { - return WIN_SetError("SetWindowLong()"); + if (focusable) { + if (style & WS_EX_NOACTIVATE) { + if (SetWindowLong(hwnd, GWL_EXSTYLE, style & ~WS_EX_NOACTIVATE) == 0) { + return WIN_SetError("SetWindowLong()"); + } + } + } else { + if (!(style & WS_EX_NOACTIVATE)) { + if (SetWindowLong(hwnd, GWL_EXSTYLE, style | WS_EX_NOACTIVATE) == 0) { + return WIN_SetError("SetWindowLong()"); + } } } - } else { - if (!(style & WS_EX_NOACTIVATE)) { - if (SetWindowLong(hwnd, GWL_EXSTYLE, style | WS_EX_NOACTIVATE) == 0) { - return WIN_SetError("SetWindowLong()"); + } else if (window->flags & SDL_WINDOW_POPUP_MENU) { + if (!(window->flags & SDL_WINDOW_HIDDEN)) { + if (!focusable && (window->flags & SDL_WINDOW_INPUT_FOCUS)) { + SDL_Window *new_focus; + const bool set_focus = SDL_ShouldRelinquishPopupFocus(window, &new_focus); + WIN_SetKeyboardFocus(new_focus, set_focus); + } else if (focusable) { + if (SDL_ShouldFocusPopup(window)) { + WIN_SetKeyboardFocus(window, true); + } } } + + return true; } return true; @@ -2281,38 +2220,38 @@ bool WIN_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, bool foc void WIN_UpdateDarkModeForHWND(HWND hwnd) { #if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) - SDL_SharedObject *ntdll = SDL_LoadObject("ntdll.dll"); + HMODULE ntdll = LoadLibrary(TEXT("ntdll.dll")); if (!ntdll) { return; } // There is no function to get Windows build number, so let's get it here via RtlGetVersion - RtlGetVersion_t RtlGetVersionFunc = (RtlGetVersion_t)SDL_LoadFunction(ntdll, "RtlGetVersion"); + RtlGetVersion_t RtlGetVersionFunc = (RtlGetVersion_t)GetProcAddress(ntdll, "RtlGetVersion"); NT_OSVERSIONINFOW os_info; os_info.dwOSVersionInfoSize = sizeof(NT_OSVERSIONINFOW); os_info.dwBuildNumber = 0; if (RtlGetVersionFunc) { RtlGetVersionFunc(&os_info); } - SDL_UnloadObject(ntdll); + FreeLibrary(ntdll); os_info.dwBuildNumber &= ~0xF0000000; if (os_info.dwBuildNumber < 17763) { // Too old to support dark mode return; } - SDL_SharedObject *uxtheme = SDL_LoadObject("uxtheme.dll"); + HMODULE uxtheme = LoadLibrary(TEXT("uxtheme.dll")); if (!uxtheme) { return; } - RefreshImmersiveColorPolicyState_t RefreshImmersiveColorPolicyStateFunc = (RefreshImmersiveColorPolicyState_t)SDL_LoadFunction(uxtheme, MAKEINTRESOURCEA(104)); - ShouldAppsUseDarkMode_t ShouldAppsUseDarkModeFunc = (ShouldAppsUseDarkMode_t)SDL_LoadFunction(uxtheme, MAKEINTRESOURCEA(132)); - AllowDarkModeForWindow_t AllowDarkModeForWindowFunc = (AllowDarkModeForWindow_t)SDL_LoadFunction(uxtheme, MAKEINTRESOURCEA(133)); + RefreshImmersiveColorPolicyState_t RefreshImmersiveColorPolicyStateFunc = (RefreshImmersiveColorPolicyState_t)GetProcAddress(uxtheme, MAKEINTRESOURCEA(104)); + ShouldAppsUseDarkMode_t ShouldAppsUseDarkModeFunc = (ShouldAppsUseDarkMode_t)GetProcAddress(uxtheme, MAKEINTRESOURCEA(132)); + AllowDarkModeForWindow_t AllowDarkModeForWindowFunc = (AllowDarkModeForWindow_t)GetProcAddress(uxtheme, MAKEINTRESOURCEA(133)); if (os_info.dwBuildNumber < 18362) { - AllowDarkModeForApp_t AllowDarkModeForAppFunc = (AllowDarkModeForApp_t)SDL_LoadFunction(uxtheme, MAKEINTRESOURCEA(135)); + AllowDarkModeForApp_t AllowDarkModeForAppFunc = (AllowDarkModeForApp_t)GetProcAddress(uxtheme, MAKEINTRESOURCEA(135)); if (AllowDarkModeForAppFunc) { AllowDarkModeForAppFunc(true); } } else { - SetPreferredAppMode_t SetPreferredAppModeFunc = (SetPreferredAppMode_t)SDL_LoadFunction(uxtheme, MAKEINTRESOURCEA(135)); + SetPreferredAppMode_t SetPreferredAppModeFunc = (SetPreferredAppMode_t)GetProcAddress(uxtheme, MAKEINTRESOURCEA(135)); if (SetPreferredAppModeFunc) { SetPreferredAppModeFunc(UXTHEME_APPMODE_ALLOW_DARK); } @@ -2330,7 +2269,7 @@ void WIN_UpdateDarkModeForHWND(HWND hwnd) } else { value = (SDL_GetSystemTheme() == SDL_SYSTEM_THEME_DARK) ? TRUE : FALSE; } - SDL_UnloadObject(uxtheme); + FreeLibrary(uxtheme); if (os_info.dwBuildNumber < 18362) { SetProp(hwnd, TEXT("UseImmersiveDarkModeColors"), SDL_reinterpret_cast(HANDLE, SDL_static_cast(INT_PTR, value))); } else { diff --git a/third-party/SDL/src/video/windows/SDL_windowswindow.h b/third-party/SDL/src/video/windows/SDL_windowswindow.h index 73eea72d65..7e4a3be205 100644 --- a/third-party/SDL/src/video/windows/SDL_windowswindow.h +++ b/third-party/SDL/src/video/windows/SDL_windowswindow.h @@ -93,7 +93,6 @@ struct SDL_WindowData bool destroy_parent_with_window; SDL_DisplayID last_displayID; WCHAR *ICMFileName; - SDL_Window *keyboard_focus; SDL_WindowEraseBackgroundMode hint_erase_background_mode; struct SDL_VideoData *videodata; #ifdef SDL_VIDEO_OPENGL_EGL diff --git a/third-party/SDL/src/video/x11/SDL_x11events.c b/third-party/SDL/src/video/x11/SDL_x11events.c index 84e466255d..d639346b6b 100644 --- a/third-party/SDL/src/video/x11/SDL_x11events.c +++ b/third-party/SDL/src/video/x11/SDL_x11events.c @@ -194,7 +194,7 @@ static bool X11_KeyRepeat(Display *display, XEvent *event) return d.found; } -static bool X11_IsWheelEvent(Display *display, int button, int *xticks, int *yticks) +bool X11_IsWheelEvent(int button, int *xticks, int *yticks) { /* according to the xlib docs, no specific mouse wheel events exist. However, the defacto standard is that the vertical wheel is X buttons @@ -1016,8 +1016,6 @@ void X11_HandleKeyEvent(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_ void X11_HandleButtonPress(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_MouseID mouseID, int button, float x, float y, unsigned long time) { SDL_Window *window = windowdata->window; - const SDL_VideoData *videodata = _this->internal; - Display *display = videodata->display; int xticks = 0, yticks = 0; Uint64 timestamp = X11_GetEventTimestamp(time); @@ -1031,7 +1029,7 @@ void X11_HandleButtonPress(SDL_VideoDevice *_this, SDL_WindowData *windowdata, S SDL_SendMouseMotion(timestamp, window, mouseID, false, x, y); } - if (X11_IsWheelEvent(display, button, &xticks, &yticks)) { + if (X11_IsWheelEvent(button, &xticks, &yticks)) { SDL_SendMouseWheel(timestamp, window, mouseID, (float)-xticks, (float)yticks, SDL_MOUSEWHEEL_NORMAL); } else { bool ignore_click = false; @@ -1063,8 +1061,6 @@ void X11_HandleButtonPress(SDL_VideoDevice *_this, SDL_WindowData *windowdata, S void X11_HandleButtonRelease(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_MouseID mouseID, int button, unsigned long time) { SDL_Window *window = windowdata->window; - const SDL_VideoData *videodata = _this->internal; - Display *display = videodata->display; // The X server sends a Release event for each Press for wheels. Ignore them. int xticks = 0, yticks = 0; Uint64 timestamp = X11_GetEventTimestamp(time); @@ -1072,7 +1068,7 @@ void X11_HandleButtonRelease(SDL_VideoDevice *_this, SDL_WindowData *windowdata, #ifdef DEBUG_XEVENTS SDL_Log("window 0x%lx: ButtonRelease (X11 button = %d)", windowdata->xwindow, button); #endif - if (!X11_IsWheelEvent(display, button, &xticks, &yticks)) { + if (!X11_IsWheelEvent(button, &xticks, &yticks)) { if (button > 7) { // see explanation at case ButtonPress button -= (8 - SDL_BUTTON_X1); @@ -1513,9 +1509,10 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent) &ChildReturn); } - /* Xfce sends ConfigureNotify before PropertyNotify when toggling fullscreen and maximized, which - * is backwards from every other window manager, as well as what is expected by SDL and its clients. - * Defer emitting the size/move events until the corresponding PropertyNotify arrives. + /* Some window managers send ConfigureNotify before PropertyNotify when changing state (Xfce and + * fvwm are known to do this), which is backwards from other window managers, as well as what is + * expected by SDL and its clients. Defer emitting the size/move events until the corresponding + * PropertyNotify arrives for consistency. */ const Uint32 changed = X11_GetNetWMState(_this, data->window, xevent->xproperty.window) ^ data->window->flags; if (changed & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_MAXIMIZED)) { @@ -1950,13 +1947,12 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent) right approach, but it seems to work. */ X11_UpdateKeymap(_this, true); } else if (xevent->xproperty.atom == videodata->atoms._NET_FRAME_EXTENTS) { - /* Events are disabled when leaving fullscreen until the borders appear to avoid - * incorrect size/position events. - */ + X11_GetBorderValues(data); if (data->size_move_event_flags) { + /* Events are disabled when leaving fullscreen until the borders appear to avoid + * incorrect size/position events on compositing window managers. + */ data->size_move_event_flags &= ~X11_SIZE_MOVE_EVENTS_WAIT_FOR_BORDERS; - X11_GetBorderValues(data); - } if (!(data->window->flags & SDL_WINDOW_FULLSCREEN) && data->toggle_borders) { data->toggle_borders = false; diff --git a/third-party/SDL/src/video/x11/SDL_x11events.h b/third-party/SDL/src/video/x11/SDL_x11events.h index bb76f836f8..bc0c226dfa 100644 --- a/third-party/SDL/src/video/x11/SDL_x11events.h +++ b/third-party/SDL/src/video/x11/SDL_x11events.h @@ -36,5 +36,6 @@ extern void X11_HandleButtonRelease(SDL_VideoDevice *_this, SDL_WindowData *wind extern SDL_WindowData *X11_FindWindow(SDL_VideoDevice *_this, Window window); extern bool X11_ProcessHitTest(SDL_VideoDevice *_this, SDL_WindowData *data, const float x, const float y, bool force_new_result); extern bool X11_TriggerHitTestAction(SDL_VideoDevice *_this, SDL_WindowData *data, const float x, const float y); +extern bool X11_IsWheelEvent(int button, int *xticks, int *yticks); #endif // SDL_x11events_h_ diff --git a/third-party/SDL/src/video/x11/SDL_x11messagebox.c b/third-party/SDL/src/video/x11/SDL_x11messagebox.c index ab2174e02a..cbc3c98c18 100644 --- a/third-party/SDL/src/video/x11/SDL_x11messagebox.c +++ b/third-party/SDL/src/video/x11/SDL_x11messagebox.c @@ -30,7 +30,10 @@ #include #include +#ifndef SDL_FORK_MESSAGEBOX #define SDL_FORK_MESSAGEBOX 1 +#endif + #define SDL_SET_LOCALE 1 #if SDL_FORK_MESSAGEBOX diff --git a/third-party/SDL/src/video/x11/SDL_x11window.c b/third-party/SDL/src/video/x11/SDL_x11window.c index 3a6b4ae78d..5422f9abf5 100644 --- a/third-party/SDL/src/video/x11/SDL_x11window.c +++ b/third-party/SDL/src/video/x11/SDL_x11window.c @@ -86,6 +86,23 @@ static Bool X11_XIfEventTimeout(Display *display, XEvent *event_return, Bool (*p } */ +static bool X11_CheckCurrentDesktop(const char *name) +{ + SDL_Environment *env = SDL_GetEnvironment(); + const char *desktopVar = SDL_GetEnvironmentVariable(env, "DESKTOP_SESSION"); + if (desktopVar && SDL_strcasecmp(desktopVar, name) == 0) { + return true; + } + + desktopVar = SDL_GetEnvironmentVariable(env, "XDG_CURRENT_DESKTOP"); + + if (desktopVar && SDL_strcasestr(desktopVar, name)) { + return true; + } + + return false; +} + static bool X11_IsWindowMapped(SDL_VideoDevice *_this, SDL_Window *window) { SDL_WindowData *data = window->internal; @@ -215,28 +232,30 @@ static void X11_ConstrainPopup(SDL_Window *window, bool output_to_pending) int abs_y = window->last_position_pending ? window->pending.y : window->floating.y; int offset_x = 0, offset_y = 0; - // Calculate the total offset from the parents - for (w = window->parent; SDL_WINDOW_IS_POPUP(w); w = w->parent) { + if (window->constrain_popup) { + // Calculate the total offset from the parents + for (w = window->parent; SDL_WINDOW_IS_POPUP(w); w = w->parent) { + offset_x += w->x; + offset_y += w->y; + } + offset_x += w->x; offset_y += w->y; - } + abs_x += offset_x; + abs_y += offset_y; - offset_x += w->x; - offset_y += w->y; - abs_x += offset_x; - abs_y += offset_y; + displayID = SDL_GetDisplayForWindow(w); - displayID = SDL_GetDisplayForWindow(w); - - SDL_GetDisplayBounds(displayID, &rect); - if (abs_x + window->w > rect.x + rect.w) { - abs_x -= (abs_x + window->w) - (rect.x + rect.w); + SDL_GetDisplayBounds(displayID, &rect); + if (abs_x + window->w > rect.x + rect.w) { + abs_x -= (abs_x + window->w) - (rect.x + rect.w); + } + if (abs_y + window->h > rect.y + rect.h) { + abs_y -= (abs_y + window->h) - (rect.y + rect.h); + } + abs_x = SDL_max(abs_x, rect.x); + abs_y = SDL_max(abs_y, rect.y); } - if (abs_y + window->h > rect.y + rect.h) { - abs_y -= (abs_y + window->h) - (rect.y + rect.h); - } - abs_x = SDL_max(abs_x, rect.x); - abs_y = SDL_max(abs_y, rect.y); if (output_to_pending) { window->pending.x = abs_x - offset_x; @@ -257,7 +276,7 @@ static void X11_SetKeyboardFocus(SDL_Window *window, bool set_active_focus) toplevel = toplevel->parent; } - toplevel->internal->keyboard_focus = window; + toplevel->keyboard_focus = window; if (set_active_focus && !window->is_hiding && !window->is_destroying) { SDL_SetKeyboardFocus(window); @@ -1542,9 +1561,9 @@ void X11_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window) X11_XFlush(display); } - // Popup menus grab the keyboard - if (window->flags & SDL_WINDOW_POPUP_MENU) { - X11_SetKeyboardFocus(window, window->parent == SDL_GetKeyboardFocus()); + // Grabbing popup menus get keyboard focus. + if ((window->flags & SDL_WINDOW_POPUP_MENU) && !(window->flags & SDL_WINDOW_NOT_FOCUSABLE)) { + X11_SetKeyboardFocus(window, true); } // Get some valid border values, if we haven't received them yet @@ -1566,6 +1585,15 @@ void X11_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window) X11_XMoveWindow(display, data->xwindow, x, y); } + /* XMonad ignores size hints and shrinks the client area to overlay borders on fixed-size windows, + * even if no borders were requested, resulting in the window client area being smaller than + * requested. Calling XResizeWindow after mapping seems to fix it, even though resizing fixed-size + * windows in this manner doesn't work on any other window manager. + */ + if (!(window->flags & SDL_WINDOW_RESIZABLE) && X11_CheckCurrentDesktop("xmonad")) { + X11_XResizeWindow(display, data->xwindow, window->w, window->h); + } + /* Some window managers can send garbage coordinates while mapping the window, so don't emit size and position * events during the initial configure events. */ @@ -1607,20 +1635,9 @@ void X11_HideWindow(SDL_VideoDevice *_this, SDL_Window *window) } // Transfer keyboard focus back to the parent - if (window->flags & SDL_WINDOW_POPUP_MENU) { - SDL_Window *new_focus = window->parent; - bool set_focus = window == SDL_GetKeyboardFocus(); - - // Find the highest level window, up to the toplevel parent, that isn't being hidden or destroyed. - while (SDL_WINDOW_IS_POPUP(new_focus) && (new_focus->is_hiding || new_focus->is_destroying)) { - new_focus = new_focus->parent; - - // If some window in the chain currently had focus, set it to the new lowest-level window. - if (!set_focus) { - set_focus = new_focus == SDL_GetKeyboardFocus(); - } - } - + if ((window->flags & SDL_WINDOW_POPUP_MENU) && !(window->flags & SDL_WINDOW_NOT_FOCUSABLE)) { + SDL_Window *new_focus; + const bool set_focus = SDL_ShouldRelinquishPopupFocus(window, &new_focus); X11_SetKeyboardFocus(new_focus, set_focus); } @@ -2338,21 +2355,37 @@ bool X11_SyncWindow(SDL_VideoDevice *_this, SDL_Window *window) bool X11_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, bool focusable) { - SDL_WindowData *data = window->internal; - Display *display = data->videodata->display; - XWMHints *wmhints; + if (!SDL_WINDOW_IS_POPUP(window)) { + SDL_WindowData *data = window->internal; + Display *display = data->videodata->display; + XWMHints *wmhints; - wmhints = X11_XGetWMHints(display, data->xwindow); - if (!wmhints) { - return SDL_SetError("Couldn't get WM hints"); + wmhints = X11_XGetWMHints(display, data->xwindow); + if (!wmhints) { + return SDL_SetError("Couldn't get WM hints"); + } + + wmhints->input = focusable ? True : False; + wmhints->flags |= InputHint; + + X11_XSetWMHints(display, data->xwindow, wmhints); + X11_XFree(wmhints); + } else if (window->flags & SDL_WINDOW_POPUP_MENU) { + if (!(window->flags & SDL_WINDOW_HIDDEN)) { + if (!focusable && (window->flags & SDL_WINDOW_INPUT_FOCUS)) { + SDL_Window *new_focus; + const bool set_focus = SDL_ShouldRelinquishPopupFocus(window, &new_focus); + X11_SetKeyboardFocus(new_focus, set_focus); + } else if (focusable) { + if (SDL_ShouldFocusPopup(window)) { + X11_SetKeyboardFocus(window, true); + } + } + } + + return true; } - wmhints->input = focusable ? True : False; - wmhints->flags |= InputHint; - - X11_XSetWMHints(display, data->xwindow, wmhints); - X11_XFree(wmhints); - return true; } diff --git a/third-party/SDL/src/video/x11/SDL_x11window.h b/third-party/SDL/src/video/x11/SDL_x11window.h index ce90ed3061..893334075f 100644 --- a/third-party/SDL/src/video/x11/SDL_x11window.h +++ b/third-party/SDL/src/video/x11/SDL_x11window.h @@ -75,7 +75,6 @@ struct SDL_WindowData Window xdnd_source; bool flashing_window; Uint64 flash_cancel_time; - SDL_Window *keyboard_focus; #ifdef SDL_VIDEO_OPENGL_EGL EGLSurface egl_surface; #endif diff --git a/third-party/SDL/src/video/x11/SDL_x11xinput2.c b/third-party/SDL/src/video/x11/SDL_x11xinput2.c index afe4a7c85b..eb212378c6 100644 --- a/third-party/SDL/src/video/x11/SDL_x11xinput2.c +++ b/third-party/SDL/src/video/x11/SDL_x11xinput2.c @@ -406,11 +406,15 @@ void X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie) case XI_ButtonRelease: { const XIDeviceEvent *xev = (const XIDeviceEvent *)cookie->data; - X11_PenHandle *pen = X11_FindPenByDeviceID(xev->deviceid); + X11_PenHandle *pen = X11_FindPenByDeviceID(xev->sourceid); const int button = xev->detail; const bool down = (cookie->evtype == XI_ButtonPress); if (pen) { + if (xev->deviceid != xev->sourceid) { + // Discard events from "Master" devices to avoid duplicates. + break; + } // Only report button event; if there was also pen movement / pressure changes, we expect an XI_Motion event first anyway. SDL_Window *window = xinput2_get_sdlwindow(videodata, xev->event); if (button == 1) { // button 1 is the pen tip @@ -421,9 +425,12 @@ void X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie) } else { // Otherwise assume a regular mouse SDL_WindowData *windowdata = xinput2_get_sdlwindowdata(videodata, xev->event); + int x_ticks = 0, y_ticks = 0; - if (xev->deviceid != xev->sourceid) { - // Discard events from "Master" devices to avoid duplicates. + /* Discard wheel events from "Master" devices to avoid duplicates, + * as coarse wheel events are stateless and can't be deduplicated. + */ + if (xev->deviceid != xev->sourceid && X11_IsWheelEvent(button, &x_ticks, &y_ticks)) { break; } @@ -449,7 +456,8 @@ void X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie) videodata->global_mouse_changed = true; - X11_PenHandle *pen = X11_FindPenByDeviceID(xev->deviceid); + X11_PenHandle *pen = X11_FindPenByDeviceID(xev->sourceid); + if (pen) { if (xev->deviceid != xev->sourceid) { // Discard events from "Master" devices to avoid duplicates. diff --git a/third-party/SDL/test/CMakeLists.txt b/third-party/SDL/test/CMakeLists.txt index 3be04e215e..8bf5c25a46 100644 --- a/third-party/SDL/test/CMakeLists.txt +++ b/third-party/SDL/test/CMakeLists.txt @@ -411,7 +411,7 @@ add_sdl_test_executable(testdialog SOURCES testdialog.c) add_sdl_test_executable(testtime SOURCES testtime.c) add_sdl_test_executable(testmanymouse SOURCES testmanymouse.c) add_sdl_test_executable(testmodal SOURCES testmodal.c) -add_sdl_test_executable(testtray SOURCES testtray.c) +add_sdl_test_executable(testtray NEEDS_RESOURCES TESTUTILS SOURCES testtray.c) add_sdl_test_executable(testprocess diff --git a/third-party/SDL/test/testautomation_surface.c b/third-party/SDL/test/testautomation_surface.c index 39135d015a..2a52fbb4bf 100644 --- a/third-party/SDL/test/testautomation_surface.c +++ b/third-party/SDL/test/testautomation_surface.c @@ -989,6 +989,45 @@ static int SDLCALL surface_testBlitInvalid(void *arg) return TEST_COMPLETED; } +static int SDLCALL surface_testBlitsWithBadCoordinates(void *arg) +{ + const SDL_Rect rect[8] = { + { SDL_MAX_SINT32, 0, 2, 2 }, + { 0, SDL_MAX_SINT32, 2, 2 }, + { 0, 0, SDL_MAX_SINT32, 2 }, + { 0, 0, 2, SDL_MAX_SINT32 }, + { SDL_MIN_SINT32, 0, 2, 2 }, + { 0, SDL_MIN_SINT32, 2, 2 }, + { 0, 0, SDL_MIN_SINT32, 2 }, + { 0, 0, 2, SDL_MIN_SINT32 } + }; + + SDL_Surface *s; + bool result; + int i; + + s = SDL_CreateSurface(1, 1, SDL_PIXELFORMAT_RGBA8888); + SDLTest_AssertCheck(s != NULL, "Check surface creation"); + + for (i = 0; i < 8; i++) { + result = SDL_BlitSurface(s, NULL, s, &rect[i]); + SDLTest_AssertCheck(result == true, "SDL_BlitSurface(valid, NULL, valid, &rect), result = %s", result ? "true" : "false"); + + result = SDL_BlitSurface(s, &rect[i], s, NULL); + SDLTest_AssertCheck(result == true, "SDL_BlitSurface(valid, &rect, valid, NULL), result = %s", result ? "true" : "false"); + + result = SDL_BlitSurfaceScaled(s, NULL, s, &rect[i], SDL_SCALEMODE_NEAREST); + SDLTest_AssertCheck(result == true, "SDL_BlitSurfaceScaled(valid, NULL, valid, &rect, SDL_SCALEMODE_NEAREST), result = %s", result ? "true" : "false"); + + result = SDL_BlitSurfaceScaled(s, &rect[i], s, NULL, SDL_SCALEMODE_NEAREST); + SDLTest_AssertCheck(result == true, "SDL_BlitSurfaceScaled(valid, &rect, valid, NULL, SDL_SCALEMODE_NEAREST), result = %s", result ? "true" : "false"); + } + + SDL_DestroySurface(s); + + return TEST_COMPLETED; +} + static int SDLCALL surface_testOverflow(void *arg) { char buf[1024]; @@ -1664,6 +1703,10 @@ static const SDLTest_TestCaseReference surfaceTestBlitInvalid = { surface_testBlitInvalid, "surface_testBlitInvalid", "Tests blitting routines with invalid surfaces.", TEST_ENABLED }; +static const SDLTest_TestCaseReference surfaceTestBlitsWithBadCoordinates = { + surface_testBlitsWithBadCoordinates, "surface_testBlitsWithBadCoordinates", "Test blitting routines with bad coordinates.", TEST_ENABLED +}; + static const SDLTest_TestCaseReference surfaceTestOverflow = { surface_testOverflow, "surface_testOverflow", "Test overflow detection.", TEST_ENABLED }; @@ -1713,6 +1756,7 @@ static const SDLTest_TestCaseReference *surfaceTests[] = { &surfaceTestBlitBlendMod, &surfaceTestBlitBlendMul, &surfaceTestBlitInvalid, + &surfaceTestBlitsWithBadCoordinates, &surfaceTestOverflow, &surfaceTestFlip, &surfaceTestPalette, diff --git a/third-party/SDL/test/testffmpeg_vulkan.c b/third-party/SDL/test/testffmpeg_vulkan.c index 4f2ba81120..a6da6cbd07 100644 --- a/third-party/SDL/test/testffmpeg_vulkan.c +++ b/third-party/SDL/test/testffmpeg_vulkan.c @@ -679,6 +679,16 @@ void SetupVulkanRenderProperties(VulkanVideoContext *context, SDL_PropertiesID p SDL_SetNumberProperty(props, SDL_PROP_RENDERER_CREATE_VULKAN_GRAPHICS_QUEUE_FAMILY_INDEX_NUMBER, context->graphicsQueueFamilyIndex); } +#if LIBAVUTIL_VERSION_MAJOR >= 59 +static void AddQueueFamily(AVVulkanDeviceContext *ctx, int idx, int num, VkQueueFlagBits flags) +{ + AVVulkanDeviceQueueFamily *entry = &ctx->qf[ctx->nb_qf++]; + entry->idx = idx; + entry->num = num; + entry->flags = flags; +} +#endif /* LIBAVUTIL_VERSION_MAJOR */ + void SetupVulkanDeviceContextData(VulkanVideoContext *context, AVVulkanDeviceContext *ctx) { ctx->get_proc_addr = context->vkGetInstanceProcAddr; @@ -690,6 +700,12 @@ void SetupVulkanDeviceContextData(VulkanVideoContext *context, AVVulkanDeviceCon ctx->nb_enabled_inst_extensions = context->instanceExtensionsCount; ctx->enabled_dev_extensions = context->deviceExtensions; ctx->nb_enabled_dev_extensions = context->deviceExtensionsCount; +#if LIBAVUTIL_VERSION_MAJOR >= 59 + AddQueueFamily(ctx, context->graphicsQueueFamilyIndex, context->graphicsQueueCount, VK_QUEUE_GRAPHICS_BIT); + AddQueueFamily(ctx, context->transferQueueFamilyIndex, context->transferQueueCount, VK_QUEUE_TRANSFER_BIT); + AddQueueFamily(ctx, context->computeQueueFamilyIndex, context->computeQueueCount, VK_QUEUE_COMPUTE_BIT); + AddQueueFamily(ctx, context->decodeQueueFamilyIndex, context->decodeQueueCount, VK_QUEUE_VIDEO_DECODE_BIT_KHR); +#else ctx->queue_family_index = context->graphicsQueueFamilyIndex; ctx->nb_graphics_queues = context->graphicsQueueCount; ctx->queue_family_tx_index = context->transferQueueFamilyIndex; @@ -700,6 +716,7 @@ void SetupVulkanDeviceContextData(VulkanVideoContext *context, AVVulkanDeviceCon ctx->nb_encode_queues = 0; ctx->queue_family_decode_index = context->decodeQueueFamilyIndex; ctx->nb_decode_queues = context->decodeQueueCount; +#endif /* LIBAVUTIL_VERSION_MAJOR */ } static int CreateCommandBuffers(VulkanVideoContext *context, SDL_Renderer *renderer) diff --git a/third-party/SDL/test/testmodal.c b/third-party/SDL/test/testmodal.c index 9e5ea63e40..d898b89697 100644 --- a/third-party/SDL/test/testmodal.c +++ b/third-party/SDL/test/testmodal.c @@ -97,7 +97,7 @@ int main(int argc, char *argv[]) } if (e.key.key == SDLK_M) { - if (SDL_SetWindowParent(w2, w2)) { + if (SDL_SetWindowParent(w2, w1)) { if (SDL_SetWindowModal(w2, true)) { SDL_SetWindowTitle(w2, "Modal Window"); } diff --git a/third-party/SDL/test/testpopup.c b/third-party/SDL/test/testpopup.c index 786f03c206..987eab8dd9 100644 --- a/third-party/SDL/test/testpopup.c +++ b/third-party/SDL/test/testpopup.c @@ -49,6 +49,9 @@ struct PopupWindow static struct PopupWindow *menus; static struct PopupWindow tooltip; +static bool no_constraints; +static bool no_grab; + /* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */ static void quit(int rc) { @@ -95,14 +98,27 @@ static bool create_popup(struct PopupWindow *new_popup, bool is_menu) const int w = is_menu ? MENU_WIDTH : TOOLTIP_WIDTH; const int h = is_menu ? MENU_HEIGHT : TOOLTIP_HEIGHT; const int v_off = is_menu ? 0 : 32; - const SDL_WindowFlags flags = is_menu ? SDL_WINDOW_POPUP_MENU : SDL_WINDOW_TOOLTIP; float x, y; focus = SDL_GetMouseFocus(); SDL_GetMouseState(&x, &y); - new_win = SDL_CreatePopupWindow(focus, - (int)x, (int)y + v_off, w, h, flags); + + SDL_PropertiesID props = SDL_CreateProperties(); + SDL_SetPointerProperty(props, SDL_PROP_WINDOW_CREATE_PARENT_POINTER, focus); + SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_CONSTRAIN_POPUP_BOOLEAN, !no_constraints); + SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_FOCUSABLE_BOOLEAN, !no_grab); + if (is_menu) { + SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_MENU_BOOLEAN, true); + } else { + SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_TOOLTIP_BOOLEAN, true); + } + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, w); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, h); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_X_NUMBER, (int)x); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_Y_NUMBER, (int)y + v_off); + new_win = SDL_CreateWindowWithProperties(props); + SDL_DestroyProperties(props); if (new_win) { new_renderer = SDL_CreateRenderer(new_win, state->renderdriver); @@ -249,8 +265,30 @@ int main(int argc, char *argv[]) } /* Parse commandline */ - if (!SDLTest_CommonDefaultArgs(state, argc, argv)) { - return 1; + for (i = 1; i < argc;) { + int consumed; + + consumed = SDLTest_CommonArg(state, i); + if (consumed == 0) { + consumed = -1; + if (SDL_strcasecmp(argv[i], "--no-constraints") == 0) { + no_constraints = true; + consumed = 1; + } else if (SDL_strcasecmp(argv[i], "--no-grab") == 0) { + no_grab = true; + consumed = 1; + } + } + if (consumed < 0) { + static const char *options[] = { + "[--no-constraints]", + "[--no-grab]", + NULL + }; + SDLTest_CommonLogUsage(state, argv[0], options); + return 1; + } + i += consumed; } if (!SDLTest_CommonInit(state)) { diff --git a/third-party/SDL/test/testtray.c b/third-party/SDL/test/testtray.c index fdb12daf28..8046d60c39 100644 --- a/third-party/SDL/test/testtray.c +++ b/third-party/SDL/test/testtray.c @@ -1,3 +1,4 @@ +#include "testutils.h" #include #include #include @@ -520,14 +521,17 @@ int main(int argc, char **argv) goto quit; } - /* TODO: Resource paths? */ - SDL_Surface *icon = SDL_LoadBMP("../test/sdl-test_round.bmp"); + char *icon1filename = GetResourceFilename(NULL, "sdl-test_round.bmp"); + SDL_Surface *icon = SDL_LoadBMP(icon1filename); + SDL_free(icon1filename); if (!icon) { SDL_Log("Couldn't load icon 1, proceeding without: %s", SDL_GetError()); } - SDL_Surface *icon2 = SDL_LoadBMP("../test/speaker.bmp"); + char *icon2filename = GetResourceFilename(NULL, "speaker.bmp"); + SDL_Surface *icon2 = SDL_LoadBMP(icon2filename); + SDL_free(icon2filename); if (!icon2) { SDL_Log("Couldn't load icon 2, proceeding without: %s", SDL_GetError()); diff --git a/vendor.yaml b/vendor.yaml index ce69925abf..b3f8df9217 100644 --- a/vendor.yaml +++ b/vendor.yaml @@ -28,7 +28,7 @@ third-party/sqlite3: third-party/fmt: git: https://github.com/fmtlib/fmt/tree/11.1.4 third-party/SDL: - git: https://github.com/libsdl-org/SDL/tree/release-3.2.16 + git: https://github.com/libsdl-org/SDL/tree/release-3.2.20 third-party/imgui: git: https://github.com/ocornut/imgui/releases/tag/v1.91.8 third-party/tree-sitter: