diff --git a/libs/SDL3/.wikiheaders-options b/libs/SDL3/.wikiheaders-options index 720a9de2..99c19dee 100644 --- a/libs/SDL3/.wikiheaders-options +++ b/libs/SDL3/.wikiheaders-options @@ -3,7 +3,7 @@ projectshortname = SDL incsubdir = include/SDL3 wikisubdir = readmesubdir = docs -apiprefixregex = (SDL_|SDLK_) +apiprefixregex = (SDL_|SDLK_|[US]int\d+) mainincludefname = SDL3/SDL.h versionfname = include/SDL3/SDL_version.h versionmajorregex = \A\#define\s+SDL_MAJOR_VERSION\s+(\d+)\Z @@ -21,3 +21,10 @@ manpageheaderfiletext = Defined in SDL3/%fname% # All SDL_test_* headers become undefined categories, everything else just converts like SDL_audio.h -> Audio # A handful of others we fix up in the header itself with /* WIKI CATEGORY: x */ comments. headercategoryeval = s/\ASDL_test_?.*?\.h\Z//; s/\ASDL_?(.*?)\.h\Z/$1/; ucfirst(); + +quickrefenabled = 1 +quickrefcategoryorder = Init,Hints,Error,Version,Properties,Log,Video,Events,Keyboard,Mouse,Touch,Gamepad,Joystick,Haptic,Audio,Time,Timer,Render,SharedObject,Thread,Mutex,Atomic,Filesystem,IOStream,AsyncIO,Storage,Pixels,Surface,Blendmode,Rect,Camera,Clipboard,Dialog,GPU,Messagebox,Vulkan,Metal,Platform,Power,Sensor,Process,Bits,Endian,Assert,CPUInfo,Intrinsics,Locale,System,Misc,GUID,Main,Stdinc +quickreftitle = SDL3 API Quick Reference +quickrefurl = https://libsdl.org/ +quickrefdesc = The latest version of this document can be found at https://wiki.libsdl.org/SDL3/QuickReference +quickrefmacroregex = \A(SDL_PLATFORM_.*|SDL_.*_INTRINSICS|SDL_Atomic...Ref|SDL_assert.*?|SDL_COMPILE_TIME_ASSERT|SDL_arraysize|SDL_Swap[BL]E\d\d|SDL_[a-z]+_cast)\Z diff --git a/libs/SDL3/Android.mk b/libs/SDL3/Android.mk index b352cb31..3e584f9a 100644 --- a/libs/SDL3/Android.mk +++ b/libs/SDL3/Android.mk @@ -30,11 +30,13 @@ LOCAL_SRC_FILES := \ $(wildcard $(LOCAL_PATH)/src/core/*.c) \ $(wildcard $(LOCAL_PATH)/src/core/android/*.c) \ $(wildcard $(LOCAL_PATH)/src/cpuinfo/*.c) \ + $(LOCAL_PATH)/src/dialog/SDL_dialog.c \ $(LOCAL_PATH)/src/dialog/SDL_dialog_utils.c \ $(LOCAL_PATH)/src/dialog/android/SDL_androiddialog.c \ $(wildcard $(LOCAL_PATH)/src/dynapi/*.c) \ $(wildcard $(LOCAL_PATH)/src/events/*.c) \ $(wildcard $(LOCAL_PATH)/src/file/*.c) \ + $(wildcard $(LOCAL_PATH)/src/file/generic/*.c) \ $(wildcard $(LOCAL_PATH)/src/gpu/*.c) \ $(wildcard $(LOCAL_PATH)/src/gpu/vulkan/*.c) \ $(wildcard $(LOCAL_PATH)/src/haptic/*.c) \ @@ -76,6 +78,7 @@ LOCAL_SRC_FILES := \ $(wildcard $(LOCAL_PATH)/src/time/unix/*.c) \ $(wildcard $(LOCAL_PATH)/src/timer/*.c) \ $(wildcard $(LOCAL_PATH)/src/timer/unix/*.c) \ + $(wildcard $(LOCAL_PATH)/src/tray/dummy/*.c) \ $(wildcard $(LOCAL_PATH)/src/video/*.c) \ $(wildcard $(LOCAL_PATH)/src/video/android/*.c) \ $(wildcard $(LOCAL_PATH)/src/video/yuv2rgb/*.c)) diff --git a/libs/SDL3/CMakeLists.txt b/libs/SDL3/CMakeLists.txt index 217d9f55..f5bcf2a7 100644 --- a/libs/SDL3/CMakeLists.txt +++ b/libs/SDL3/CMakeLists.txt @@ -1,7 +1,11 @@ cmake_minimum_required(VERSION 3.16) +if(NOT DEFINED CMAKE_BUILD_TYPE) + set(cmake_build_type_undefined 1) +endif() + # See docs/release_checklist.md -project(SDL3 LANGUAGES C VERSION "3.1.6") +project(SDL3 LANGUAGES C VERSION "3.1.7") if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) set(SDL3_SUBPROJECT OFF) @@ -9,6 +13,23 @@ else() set(SDL3_SUBPROJECT ON) endif() +# By default, configure SDL3 in RelWithDebInfo configuration +if(NOT SDL3_SUBPROJECT) + get_property(is_multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) + if(is_multi_config) + # The first item in CMAKE_CONFIGURATION_TYPES is the default configuration + if(DEFINED CMAKE_CONFIGURATION_TYPES AND "RelWithDebInfo" IN_LIST CMAKE_CONFIGURATION_TYPES) + list(REMOVE_ITEM CMAKE_CONFIGURATION_TYPES "RelWithDebInfo") + list(INSERT CMAKE_CONFIGURATION_TYPES 0 "RelWithDebInfo") + set(CMAKE_CONFIGURATION_TYPES "${CMAKE_CONFIGURATION_TYPES}" CACHE STRING "CMake configuration types" FORCE) + endif() + else() + if(cmake_build_type_undefined) + set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING "CMake build type" FORCE) + endif() + endif() +endif() + # CMake 3.0 expands the "if(${A})" in "set(OFF 1);set(A OFF);if(${A})" to "if(1)" # CMake 3.24+ emits a warning when not set. unset(OFF) @@ -286,6 +307,7 @@ set_option(SDL_SYSTEM_ICONV "Use iconv() from system-installed libraries" set_option(SDL_LIBICONV "Prefer iconv() from libiconv, if available, over libc version" OFF) set_option(SDL_GCC_ATOMICS "Use gcc builtin atomics" ${SDL_GCC_ATOMICS_DEFAULT}) dep_option(SDL_DBUS "Enable D-Bus support" ON "${UNIX_SYS}" OFF) +dep_option(SDL_LIBURING "Enable liburing support" ON "${UNIX_SYS}" OFF) dep_option(SDL_DISKAUDIO "Support the disk writer audio driver" ON "SDL_AUDIO" OFF) dep_option(SDL_DUMMYAUDIO "Support the dummy audio driver" ON "SDL_AUDIO" OFF) dep_option(SDL_DUMMYVIDEO "Use dummy video driver" ON "SDL_VIDEO" OFF) @@ -309,7 +331,7 @@ set_option(SDL_RPATH "Use an rpath when linking SDL" ${SDL_RPATH_D set_option(SDL_CLOCK_GETTIME "Use clock_gettime() instead of gettimeofday()" ${SDL_CLOCK_GETTIME_DEFAULT}) dep_option(SDL_X11 "Use X11 video driver" ${UNIX_SYS} "SDL_VIDEO" OFF) dep_option(SDL_X11_SHARED "Dynamically load X11 support" ON "SDL_X11" OFF) -set(SDL_X11_OPTIONS Xcursor Xdbe XInput Xfixes Xrandr Xscrnsaver XShape) +set(SDL_X11_OPTIONS Xcursor Xdbe XInput Xfixes Xrandr Xscrnsaver XShape Xsync) foreach(_SUB ${SDL_X11_OPTIONS}) string(TOUPPER "SDL_X11_${_SUB}" _OPT) dep_option(${_OPT} "Enable ${_SUB} support" ON "SDL_X11" OFF) @@ -915,7 +937,7 @@ endif() set(SDL_DISABLE_ALLOCA 0) check_include_file("alloca.h" "HAVE_ALLOCA_H") if(MSVC) - check_include_file("malloc.h" "HAVE_MALLOC") + check_include_file("malloc.h" "HAVE_MALLOC_H") check_symbol_exists("_alloca" "malloc.h" _ALLOCA_IN_MALLOC_H) if(NOT HAVE_ALLOCA_H AND NOT _ALLOCA_IN_MALLOC_H) set(SDL_DISABLE_ALLOCA 1) @@ -961,15 +983,15 @@ if(SDL_LIBC) set(symbols_to_check abs acos acosf asin asinf atan atan2 atan2f atanf atof atoi bcopy - calloc ceil ceilf copysign copysignf cos cosf + ceil ceilf copysign copysignf cos cosf _Exit exp expf - fabs fabsf floor floorf fmod fmodf fopen64 free fseeko fseeko64 + fabs fabsf floor floorf fmod fmodf fopen64 fseeko fseeko64 getenv _i64toa index itoa log log10 log10f logf lround lroundf _ltoa malloc memcmp memcpy memmove memset modf modff pow powf putenv - realloc rindex round roundf + rindex round roundf scalbn scalbnf setenv sin sinf sqr sqrt sqrtf sscanf strchr strcmp strlcat strlcpy strlen strncmp strnlen strpbrk strrchr strstr strnstr strtod strtok_r strtol strtoll strtoul strtoull @@ -980,7 +1002,7 @@ if(SDL_LIBC) ) if(WINDOWS) list(APPEND symbols_to_check - _strrev _ui64toa _uitoa _ultoa _wcsdup + _copysign _fseeki64 _strrev _ui64toa _uitoa _ultoa _wcsdup ) else() list(APPEND symbols_to_check @@ -1119,6 +1141,7 @@ sdl_glob_sources( "${SDL3_SOURCE_DIR}/src/dynapi/*.c" "${SDL3_SOURCE_DIR}/src/events/*.c" "${SDL3_SOURCE_DIR}/src/file/*.c" + "${SDL3_SOURCE_DIR}/src/file/generic/*.c" "${SDL3_SOURCE_DIR}/src/filesystem/*.c" "${SDL3_SOURCE_DIR}/src/gpu/*.c" "${SDL3_SOURCE_DIR}/src/joystick/*.c" @@ -1295,7 +1318,6 @@ if(ANDROID) set(SDL_JOYSTICK_ANDROID 1) sdl_glob_sources( "${SDL3_SOURCE_DIR}/src/joystick/android/*.c" - "${SDL3_SOURCE_DIR}/src/joystick/steam/*.c" ) set(HAVE_SDL_JOYSTICK TRUE) endif() @@ -1545,6 +1567,9 @@ elseif(UNIX AND NOT APPLE AND NOT RISCOS AND NOT HAIKU) CheckVivante() CheckVulkan() CheckQNXScreen() + + sdl_glob_sources("${SDL3_SOURCE_DIR}/src/tray/unix/*.c") + set(HAVE_SDL_TRAY TRUE) endif() if(UNIX) @@ -1657,6 +1682,16 @@ elseif(UNIX AND NOT APPLE AND NOT RISCOS AND NOT HAIKU) set(SDL_USE_IME 1) endif() + if(SDL_LIBURING) + pkg_search_module(LIBURING liburing-ffi) + find_path(HAVE_LIBURING_H NAMES liburing.h) + if(LIBURING_FOUND AND HAVE_LIBURING_H) + set(HAVE_LIBURING_LIBURING_H TRUE) + sdl_include_directories(PRIVATE SYSTEM ${LIBURING_INCLUDE_DIRS}) + set(HAVE_LIBURING TRUE) + endif() + endif() + if((FREEBSD OR NETBSD) AND NOT HAVE_INOTIFY) set(LibInotify_PKG_CONFIG_SPEC libinotify) pkg_check_modules(PC_LIBINOTIFY IMPORTED_TARGET ${LibInotify_PKG_CONFIG_SPEC}) @@ -1720,6 +1755,10 @@ elseif(UNIX AND NOT APPLE AND NOT RISCOS AND NOT HAIKU) endif() endif() + if(HAVE_LIBURING_H) + sdl_sources("${SDL3_SOURCE_DIR}/src/file/io_uring/SDL_asyncio_liburing.c") + endif() + # Always compiled for Linux, unconditionally: sdl_sources( "${SDL3_SOURCE_DIR}/src/core/linux/SDL_evdev_capabilities.c" @@ -1741,7 +1780,6 @@ elseif(UNIX AND NOT APPLE AND NOT RISCOS AND NOT HAIKU) set(SDL_JOYSTICK_LINUX 1) sdl_glob_sources( "${SDL3_SOURCE_DIR}/src/joystick/linux/*.c" - "${SDL3_SOURCE_DIR}/src/joystick/steam/*.c" ) set(HAVE_SDL_JOYSTICK TRUE) endif() @@ -2040,6 +2078,9 @@ elseif(WINDOWS) set(HAVE_RENDER_VULKAN TRUE) endif() endif() + + sdl_glob_sources("${SDL3_SOURCE_DIR}/src/tray/windows/*.c") + set(HAVE_SDL_TRAY TRUE) endif() if(SDL_HIDAPI) @@ -2113,8 +2154,6 @@ elseif(APPLE) set(HAVE_SDL_MAIN_CALLBACKS TRUE) endif() - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/file/cocoa/*.m") - if(SDL_CAMERA) if(MACOS OR IOS) set(SDL_CAMERA_DRIVER_COREMEDIA 1) @@ -2146,7 +2185,6 @@ elseif(APPLE) if(SDL_JOYSTICK) sdl_glob_sources("${SDL3_SOURCE_DIR}/src/joystick/apple/*.m") if(IOS OR TVOS OR VISIONOS OR WATCHOS) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/joystick/steam/*.c") set(SDL_JOYSTICK_MFI 1) if(IOS OR VISIONOS OR WATCHOS) set(SDL_FRAMEWORK_COREMOTION 1) @@ -2319,6 +2357,11 @@ elseif(APPLE) endif() endif() endif() + + if(MACOS) + sdl_glob_sources("${SDL3_SOURCE_DIR}/src/tray/cocoa/*.m") + set(HAVE_SDL_TRAY TRUE) + endif() endif() # Minimum version for $ @@ -2675,7 +2718,9 @@ elseif(PSP) sdl_glob_sources("${SDL3_SOURCE_DIR}/src/filesystem/psp/*.c") set(HAVE_SDL_FILESYSTEM TRUE) - # !!! FIXME: do we need a FSops implementation for this? + set(SDL_FSOPS_POSIX 1) + sdl_sources("${SDL3_SOURCE_DIR}/src/filesystem/posix/SDL_sysfsops.c") + set(HAVE_SDL_FSOPS TRUE) if(SDL_JOYSTICK) set(SDL_JOYSTICK_PSP 1) @@ -2698,6 +2743,9 @@ elseif(PSP) ) set(HAVE_SDL_THREADS TRUE) + sdl_glob_sources("${SDL3_SOURCE_DIR}/src/locale/psp/*.c") + set(HAVE_SDL_LOCALE TRUE) + set(SDL_TIME_PSP 1) sdl_glob_sources("${SDL3_SOURCE_DIR}/src/time/psp/*.c") set(HAVE_SDL_TIME TRUE) @@ -2729,10 +2777,6 @@ elseif(PSP) ) elseif(PS2) - sdl_compile_definitions(PRIVATE "PS2" "__PS2__") - sdl_include_directories(PRIVATE SYSTEM "$ENV{PS2SDK}/ports/include" "$ENV{PS2DEV}/gsKit/include") - target_include_directories(SDL_uclibc PRIVATE "$ENV{PS2SDK}/ports/include" "$ENV{PS2DEV}/gsKit/include") - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/main/ps2/*.c") if(SDL_AUDIO) @@ -2745,7 +2789,9 @@ elseif(PS2) sdl_glob_sources("${SDL3_SOURCE_DIR}/src/filesystem/ps2/*.c") set(HAVE_SDL_FILESYSTEM TRUE) - # !!! FIXME: do we need a FSops implementation for this? + set(SDL_FSOPS_POSIX 1) + sdl_sources("${SDL3_SOURCE_DIR}/src/filesystem/posix/SDL_sysfsops.c") + set(HAVE_SDL_FSOPS TRUE) if(SDL_JOYSTICK) set(SDL_JOYSTICK_PS2 1) @@ -2854,6 +2900,7 @@ elseif(N3DS) sdl_glob_sources("${SDL3_SOURCE_DIR}/src/file/n3ds/*.c") endif() +sdl_sources(${SDL3_SOURCE_DIR}/src/dialog/SDL_dialog.c) if (SDL_DIALOG) sdl_sources(${SDL3_SOURCE_DIR}/src/dialog/SDL_dialog_utils.c) if(ANDROID) @@ -2937,6 +2984,8 @@ if(SDL_VIDEO) endif() endif() +sdl_glob_sources(${SDL3_SOURCE_DIR}/src/tray/*.c) + if(SDL_GPU) if(HAVE_D3D11_H) sdl_glob_sources("${SDL3_SOURCE_DIR}/src/gpu/d3d11/*.c") @@ -3019,6 +3068,10 @@ if(NOT HAVE_SDL_PROCESS) set(SDL_PROCESS_DUMMY 1) sdl_glob_sources(${SDL3_SOURCE_DIR}/src/process/dummy/*.c) endif() +if(NOT HAVE_SDL_TRAY) + set(SDL_TRAY_DUMMY 1) + sdl_glob_sources(${SDL3_SOURCE_DIR}/src/tray/dummy/*.c) +endif() if(NOT HAVE_CAMERA) set(SDL_CAMERA_DRIVER_DUMMY 1) sdl_glob_sources("${SDL3_SOURCE_DIR}/src/camera/dummy/*.c") @@ -3063,15 +3116,17 @@ endforeach() # If REVISION.txt exists, then we are building from a SDL release. # SDL_revision.h(.cmake) in source releases have SDL_REVISION baked into them. -if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/REVISION.txt") - set(SDL_REVISION "" CACHE STRING "Custom SDL revision") - if(SDL_REVISION) - set(SDL_REVISION_CENTER "${SDL_VERSION_MAJOR}.${SDL_VERSION_MINOR}.${SDL_VERSION_MICRO}-${SDL_REVISION}") - else() - # If SDL_REVISION is not overrided, use git to describe - git_describe(SDL_REVISION_CENTER) - endif() - set(SDL_REVISION "SDL3-${SDL_REVISION_CENTER}") +if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/REVISION.txt") + file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/REVISION.txt" revisions) + list(GET revisions 0 revisions_0) + string(STRIP "${revisions_0}" SDL_REVISION) +else() + set(SDL_REVISION "" CACHE STRING "Custom SDL revision (only used when REVISION.txt does not exist)") +endif() +if(NOT SDL_REVISION) + # If SDL_REVISION is not overrided, use git to describe + git_describe(SDL_REVISION_GIT) + set(SDL_REVISION "SDL3-${SDL3_VERSION}-${SDL_REVISION_GIT}") endif() execute_process(COMMAND "${CMAKE_COMMAND}" -E make_directory "${SDL3_BINARY_DIR}/include-revision/SDL3") @@ -3435,7 +3490,7 @@ endif() if(WINDOWS AND NOT MINGW) set(SDL_INSTALL_CMAKEDIR_ROOT_DEFAULT "cmake") else() - set(SDL_INSTALL_CMAKEDIR_ROOT_DEFAULT "${CMAKE_INSTALL_LIBDIR}/cmake") + set(SDL_INSTALL_CMAKEDIR_ROOT_DEFAULT "${CMAKE_INSTALL_LIBDIR}/cmake/SDL3") endif() set(SDL_INSTALL_CMAKEDIR_ROOT "${SDL_INSTALL_CMAKEDIR_ROOT_DEFAULT}" CACHE STRING "Root folder where to install SDL3Config.cmake related files (SDL3 subfolder for MSVC projects)") @@ -3455,7 +3510,7 @@ elseif(SDL_FRAMEWORK) set(SDL_INSTALL_LICENSEDIR "Resources") set(SDL_INSTALL_HEADERSDIR "Headers") else() - set(SDL_INSTALL_CMAKEDIR "${SDL_INSTALL_CMAKEDIR_ROOT}/SDL3") + set(SDL_INSTALL_CMAKEDIR "${SDL_INSTALL_CMAKEDIR_ROOT}") set(SDL_INSTALL_LICENSEDIR "${CMAKE_INSTALL_DATAROOTDIR}/licenses/${PROJECT_NAME}") set(SDL_INSTALL_HEADERSDIR "${CMAKE_INSTALL_INCLUDEDIR}/SDL3") endif() diff --git a/libs/SDL3/INSTALL.md b/libs/SDL3/INSTALL.md index 647341d3..b8b0afb2 100644 --- a/libs/SDL3/INSTALL.md +++ b/libs/SDL3/INSTALL.md @@ -26,7 +26,7 @@ Run: `cmake -S . -B build -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" && cmake --bu ## Linux and other UNIX systems: -Run: `cmake -S . -B build && cmake --build build && cmake --install build` +Run: `cmake -S . -B build && cmake --build build --parallel $(nproc) && cmake --install build` ## Android: diff --git a/libs/SDL3/VisualC-GDK/SDL/SDL.vcxproj b/libs/SDL3/VisualC-GDK/SDL/SDL.vcxproj index c779b40c..2b6437fb 100644 --- a/libs/SDL3/VisualC-GDK/SDL/SDL.vcxproj +++ b/libs/SDL3/VisualC-GDK/SDL/SDL.vcxproj @@ -102,10 +102,10 @@ - $(SolutionDir)/../src;$(IncludePath) + $(ProjectDir)../../src;$(IncludePath) - $(SolutionDir)/../src;$(IncludePath) + $(ProjectDir)../../src;$(IncludePath) @@ -165,8 +165,10 @@ true - $(SolutionDir)..\src\render\direct3d12\compile_shaders_xbox.bat $(SolutionDir) - $(SolutionDir)..\src\gpu\d3d12\compile_shaders_xbox.bat $(SolutionDir) + + call $(ProjectDir)..\..\src\render\direct3d12\compile_shaders_xbox.bat $(ProjectDir)..\ + call $(ProjectDir)..\..\src\gpu\d3d12\compile_shaders_xbox.bat $(ProjectDir)..\ + Building shader blobs (Xbox Series) @@ -200,8 +202,10 @@ true - $(SolutionDir)..\src\render\direct3d12\compile_shaders_xbox.bat $(SolutionDir) one - $(SolutionDir)..\src\gpu\d3d12\compile_shaders_xbox.bat $(SolutionDir) one + + call $(ProjectDir)..\..\src\render\direct3d12\compile_shaders_xbox.bat $(ProjectDir)..\ one + call $(ProjectDir)..\..\src\gpu\d3d12\compile_shaders_xbox.bat $(ProjectDir)..\ one + Building shader blobs (Xbox One) @@ -267,8 +271,10 @@ true - $(SolutionDir)..\src\render\direct3d12\compile_shaders_xbox.bat $(SolutionDir) - $(SolutionDir)..\src\gpu\d3d12\compile_shaders_xbox.bat $(SolutionDir) + + call $(ProjectDir)..\..\src\render\direct3d12\compile_shaders_xbox.bat $(ProjectDir)..\ + call $(ProjectDir)..\..\src\gpu\d3d12\compile_shaders_xbox.bat $(ProjectDir)..\ + Building shader blobs (Xbox Series) @@ -303,8 +309,10 @@ true - $(SolutionDir)..\src\render\direct3d12\compile_shaders_xbox.bat $(SolutionDir) one - $(SolutionDir)..\src\gpu\d3d12\compile_shaders_xbox.bat $(SolutionDir) one + + call $(ProjectDir)..\..\src\render\direct3d12\compile_shaders_xbox.bat $(ProjectDir)..\ one + call $(ProjectDir)..\..\src\gpu\d3d12\compile_shaders_xbox.bat $(ProjectDir)..\ one + Building shader blobs (Xbox One) @@ -339,6 +347,7 @@ + @@ -432,6 +441,8 @@ + + @@ -514,9 +525,13 @@ + + + + @@ -577,6 +592,7 @@ + @@ -616,7 +632,6 @@ - @@ -725,31 +740,7 @@ CompileAsCpp CompileAsCpp - - - - - - - - - - - - - - - - - - - - - - - - @@ -837,6 +828,16 @@ + + true + true + + + true + true + true + true + @@ -865,6 +866,7 @@ + @@ -884,7 +886,6 @@ - CompileAsCpp CompileAsCpp diff --git a/libs/SDL3/VisualC-GDK/SDL/SDL.vcxproj.filters b/libs/SDL3/VisualC-GDK/SDL/SDL.vcxproj.filters index 46d3b477..5ae2609c 100644 --- a/libs/SDL3/VisualC-GDK/SDL/SDL.vcxproj.filters +++ b/libs/SDL3/VisualC-GDK/SDL/SDL.vcxproj.filters @@ -4,15 +4,6 @@ - - dialog - - - filesystem - - - filesystem\windows - @@ -35,7 +26,6 @@ - @@ -58,7 +48,6 @@ - @@ -96,31 +85,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - @@ -191,12 +156,6 @@ - - time - - - time\windows - @@ -222,6 +181,7 @@ + @@ -237,8 +197,32 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + @@ -350,9 +334,6 @@ - - filesystem - @@ -457,6 +438,7 @@ + @@ -476,6 +458,21 @@ + + + + + + + + + + + + + + + diff --git a/libs/SDL3/VisualC/SDL.sln b/libs/SDL3/VisualC/SDL.sln index 039ff338..bd804d44 100644 --- a/libs/SDL3/VisualC/SDL.sln +++ b/libs/SDL3/VisualC/SDL.sln @@ -71,7 +71,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "01-read-and-draw", "example EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "game", "game", "{D1BF59F6-22DC-493B-BDEB-451A50DA793D}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "01-snake", "examples\game\01-snake\01-snake.vcxproj", "{7820969A-5B7B-4046-BB0A-82905D457FC5}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "01-snake", "examples\demo\01-snake\01-snake.vcxproj", "{7820969A-5B7B-4046-BB0A-82905D457FC5}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "pen", "pen", "{F2247885-8EE8-42F4-A702-4155587620E0}" EndProject @@ -111,9 +111,9 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "18-debug-text", "examples\r EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "03-load-wav", "examples\audio\03-load-wav\03-load-wav.vcxproj", "{608C6C67-7766-471F-BBFF-8B00086039AF}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "02-woodeneye-008", "examples\game\02-woodeneye-008\02-woodeneye-008.vcxproj", "{A3F601E0-B54C-4DD8-8A97-FDEF7624EE60}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "02-woodeneye-008", "examples\demo\02-woodeneye-008\02-woodeneye-008.vcxproj", "{A3F601E0-B54C-4DD8-8A97-FDEF7624EE60}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "03-infinite-monkeys", "examples\game\03-infinite-monkeys\03-infinite-monkeys.vcxproj", "{75AEE75A-C016-4497-960B-D767B822237D}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "03-infinite-monkeys", "examples\demo\03-infinite-monkeys\03-infinite-monkeys.vcxproj", "{75AEE75A-C016-4497-960B-D767B822237D}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/libs/SDL3/VisualC/SDL/SDL.vcxproj b/libs/SDL3/VisualC/SDL/SDL.vcxproj index a6371238..42d0c13d 100644 --- a/libs/SDL3/VisualC/SDL/SDL.vcxproj +++ b/libs/SDL3/VisualC/SDL/SDL.vcxproj @@ -259,6 +259,7 @@ + @@ -352,6 +353,8 @@ + + @@ -409,13 +412,16 @@ + + - + + @@ -483,6 +489,7 @@ + @@ -522,7 +529,6 @@ - @@ -585,31 +591,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - @@ -690,6 +672,7 @@ + @@ -720,6 +703,7 @@ + diff --git a/libs/SDL3/VisualC/SDL/SDL.vcxproj.filters b/libs/SDL3/VisualC/SDL/SDL.vcxproj.filters index ada79400..d1d24865 100644 --- a/libs/SDL3/VisualC/SDL/SDL.vcxproj.filters +++ b/libs/SDL3/VisualC/SDL/SDL.vcxproj.filters @@ -211,6 +211,12 @@ {00009d5ded166cc6c6680ec771a30000} + + {00004d6806b6238cae0ed62db5440000} + + + {000028b2ea36d7190d13777a4dc70000} + @@ -279,6 +285,9 @@ API Headers + + API Headers + API Headers @@ -438,6 +447,12 @@ filesystem + + file + + + file + main @@ -501,9 +516,6 @@ dynapi - - events - events @@ -678,6 +690,9 @@ video\yuv2rgb + + video\windows + video\windows @@ -923,6 +938,9 @@ gpu + + + @@ -935,6 +953,9 @@ camera + + dialog + dialog @@ -944,6 +965,15 @@ filesystem\windows + + file\generic + + + file + + + file\windows + main\generic @@ -1079,81 +1109,9 @@ joystick - - libm - - - libm - - - libm - - - libm - - - libm - - - libm - - - libm - - - libm - - - libm - - - libm - - - libm - - - libm - - - libm - - - libm - - - libm - - - libm - - - libm - - - libm - - - libm - - - libm - - - libm - libm - - libm - - - libm - - - libm - loadso\windows @@ -1178,9 +1136,6 @@ audio\dummy - - audio\wasapi - audio\wasapi @@ -1277,6 +1232,9 @@ time\windows + + video + video @@ -1349,6 +1307,9 @@ video\dummy + + video\windows + video\windows @@ -1499,15 +1460,6 @@ render\direct3d11 - - render\gpu - - - render\gpu - - - render\gpu - render\opengl @@ -1550,12 +1502,6 @@ power\windows - - process - - - process\windows - render\direct3d12 @@ -1601,15 +1547,20 @@ gpu - - gpu - gpu gpu + + + + + + + + diff --git a/libs/SDL3/VisualC/VisualC/examples/game/01-snake/01-snake.vcxproj b/libs/SDL3/VisualC/VisualC/examples/game/01-snake/01-snake.vcxproj index 9bdfd52e..7da99a23 100644 --- a/libs/SDL3/VisualC/VisualC/examples/game/01-snake/01-snake.vcxproj +++ b/libs/SDL3/VisualC/VisualC/examples/game/01-snake/01-snake.vcxproj @@ -6,8 +6,8 @@ - - + + \ No newline at end of file diff --git a/libs/SDL3/VisualC/examples/game/01-snake/01-snake.vcxproj b/libs/SDL3/VisualC/examples/demo/01-snake/01-snake.vcxproj similarity index 79% rename from libs/SDL3/VisualC/examples/game/01-snake/01-snake.vcxproj rename to libs/SDL3/VisualC/examples/demo/01-snake/01-snake.vcxproj index d9c0d862..85128b40 100644 --- a/libs/SDL3/VisualC/examples/game/01-snake/01-snake.vcxproj +++ b/libs/SDL3/VisualC/examples/demo/01-snake/01-snake.vcxproj @@ -6,8 +6,8 @@ - - + + \ No newline at end of file diff --git a/libs/SDL3/VisualC/examples/game/02-woodeneye-008/02-woodeneye-008.vcxproj b/libs/SDL3/VisualC/examples/demo/02-woodeneye-008/02-woodeneye-008.vcxproj similarity index 80% rename from libs/SDL3/VisualC/examples/game/02-woodeneye-008/02-woodeneye-008.vcxproj rename to libs/SDL3/VisualC/examples/demo/02-woodeneye-008/02-woodeneye-008.vcxproj index da893126..08f1da99 100644 --- a/libs/SDL3/VisualC/examples/game/02-woodeneye-008/02-woodeneye-008.vcxproj +++ b/libs/SDL3/VisualC/examples/demo/02-woodeneye-008/02-woodeneye-008.vcxproj @@ -6,8 +6,8 @@ - - + + \ No newline at end of file diff --git a/libs/SDL3/VisualC/examples/game/03-infinite-monkeys/03-infinite-monkeys.vcxproj b/libs/SDL3/VisualC/examples/demo/03-infinite-monkeys/03-infinite-monkeys.vcxproj similarity index 80% rename from libs/SDL3/VisualC/examples/game/03-infinite-monkeys/03-infinite-monkeys.vcxproj rename to libs/SDL3/VisualC/examples/demo/03-infinite-monkeys/03-infinite-monkeys.vcxproj index 58e3c817..a08c5e4a 100644 --- a/libs/SDL3/VisualC/examples/game/03-infinite-monkeys/03-infinite-monkeys.vcxproj +++ b/libs/SDL3/VisualC/examples/demo/03-infinite-monkeys/03-infinite-monkeys.vcxproj @@ -6,8 +6,8 @@ - - + + \ No newline at end of file diff --git a/libs/SDL3/Xcode/SDL/Info-Framework.plist b/libs/SDL3/Xcode/SDL/Info-Framework.plist index 8e9f72f4..23c3d509 100644 --- a/libs/SDL3/Xcode/SDL/Info-Framework.plist +++ b/libs/SDL3/Xcode/SDL/Info-Framework.plist @@ -19,10 +19,10 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 3.1.6 + 3.1.7 CFBundleSignature SDLX CFBundleVersion - 3.1.6 + 3.1.7 diff --git a/libs/SDL3/Xcode/SDL/SDL.xcodeproj/project.pbxproj b/libs/SDL3/Xcode/SDL/SDL.xcodeproj/project.pbxproj index 30701079..08dfd070 100644 --- a/libs/SDL3/Xcode/SDL/SDL.xcodeproj/project.pbxproj +++ b/libs/SDL3/Xcode/SDL/SDL.xcodeproj/project.pbxproj @@ -40,10 +40,13 @@ 000040E76FDC6AE48CBF0000 /* SDL_hashtable.c in Sources */ = {isa = PBXBuildFile; fileRef = 000078E1881E857EBB6C0000 /* SDL_hashtable.c */; }; 0000481D255AF155B42C0000 /* SDL_sysfsops.c in Sources */ = {isa = PBXBuildFile; fileRef = 0000F4E6AA3EF99DA3C80000 /* SDL_sysfsops.c */; }; 0000494CC93F3E624D3C0000 /* SDL_systime.c in Sources */ = {isa = PBXBuildFile; fileRef = 00003F472C51CE7DF6160000 /* SDL_systime.c */; }; + 00004D0B73767647AD550000 /* SDL_asyncio_generic.c in Sources */ = {isa = PBXBuildFile; fileRef = 0000FB02CDE4BE34A87E0000 /* SDL_asyncio_generic.c */; }; 000080903BC03006F24E0000 /* SDL_filesystem.c in Sources */ = {isa = PBXBuildFile; fileRef = 00002B010DB1A70931C20000 /* SDL_filesystem.c */; }; 000095FA1BDE436CF3AF0000 /* SDL_time.c in Sources */ = {isa = PBXBuildFile; fileRef = 0000641A9BAC11AB3FBE0000 /* SDL_time.c */; }; 000098E9DAA43EF6FF7F0000 /* SDL_camera.c in Sources */ = {isa = PBXBuildFile; fileRef = 0000035D38C3899C7EFD0000 /* SDL_camera.c */; }; + 0000A03C0F32C43816F40000 /* SDL_asyncio_windows_ioring.c in Sources */ = {isa = PBXBuildFile; fileRef = 000030DD21496B5C0F210000 /* SDL_asyncio_windows_ioring.c */; }; 0000A4DA2F45A31DC4F00000 /* SDL_sysmain_callbacks.m in Sources */ = {isa = PBXBuildFile; fileRef = 0000BB287BA0A0178C1A0000 /* SDL_sysmain_callbacks.m */; }; + 0000AEB9AE90228CA2D60000 /* SDL_asyncio.c in Sources */ = {isa = PBXBuildFile; fileRef = 00003928A612EC33D42C0000 /* SDL_asyncio.c */; }; 0000D5B526B85DE7AB1C0000 /* SDL_cocoapen.m in Sources */ = {isa = PBXBuildFile; fileRef = 0000CCA310B73A7B59910000 /* SDL_cocoapen.m */; }; 007317A40858DECD00B2BC32 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0073179D0858DECD00B2BC32 /* Cocoa.framework */; platformFilters = (macos, ); }; 007317A60858DECD00B2BC32 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0073179F0858DECD00B2BC32 /* IOKit.framework */; platformFilters = (ios, maccatalyst, macos, ); }; @@ -300,28 +303,6 @@ A7D8BA7923E2514400DCD162 /* SDL_glfuncs.h in Headers */ = {isa = PBXBuildFile; fileRef = A7D8A90E23E2514000DCD162 /* SDL_glfuncs.h */; }; A7D8BA7F23E2514400DCD162 /* SDL_render_gl.c in Sources */ = {isa = PBXBuildFile; fileRef = A7D8A90F23E2514000DCD162 /* SDL_render_gl.c */; }; A7D8BA8523E2514400DCD162 /* SDL_shaders_gl.c in Sources */ = {isa = PBXBuildFile; fileRef = A7D8A91023E2514000DCD162 /* SDL_shaders_gl.c */; }; - A7D8BA8B23E2514400DCD162 /* s_sin.c in Sources */ = {isa = PBXBuildFile; fileRef = A7D8A91223E2514000DCD162 /* s_sin.c */; }; - A7D8BA9123E2514400DCD162 /* s_cos.c in Sources */ = {isa = PBXBuildFile; fileRef = A7D8A91323E2514000DCD162 /* s_cos.c */; }; - A7D8BA9723E2514400DCD162 /* s_copysign.c in Sources */ = {isa = PBXBuildFile; fileRef = A7D8A91423E2514000DCD162 /* s_copysign.c */; }; - A7D8BA9D23E2514400DCD162 /* s_fabs.c in Sources */ = {isa = PBXBuildFile; fileRef = A7D8A91523E2514000DCD162 /* s_fabs.c */; }; - A7D8BAA323E2514400DCD162 /* k_rem_pio2.c in Sources */ = {isa = PBXBuildFile; fileRef = A7D8A91623E2514000DCD162 /* k_rem_pio2.c */; }; - A7D8BAA923E2514400DCD162 /* k_sin.c in Sources */ = {isa = PBXBuildFile; fileRef = A7D8A91723E2514000DCD162 /* k_sin.c */; }; - A7D8BAAF23E2514400DCD162 /* s_atan.c in Sources */ = {isa = PBXBuildFile; fileRef = A7D8A91823E2514000DCD162 /* s_atan.c */; }; - A7D8BAB523E2514400DCD162 /* k_cos.c in Sources */ = {isa = PBXBuildFile; fileRef = A7D8A91923E2514000DCD162 /* k_cos.c */; }; - A7D8BABB23E2514400DCD162 /* s_scalbn.c in Sources */ = {isa = PBXBuildFile; fileRef = A7D8A91A23E2514000DCD162 /* s_scalbn.c */; }; - A7D8BAC123E2514500DCD162 /* math_private.h in Headers */ = {isa = PBXBuildFile; fileRef = A7D8A91B23E2514000DCD162 /* math_private.h */; }; - A7D8BAC723E2514500DCD162 /* e_pow.c in Sources */ = {isa = PBXBuildFile; fileRef = A7D8A91C23E2514000DCD162 /* e_pow.c */; }; - A7D8BACD23E2514500DCD162 /* e_atan2.c in Sources */ = {isa = PBXBuildFile; fileRef = A7D8A91D23E2514000DCD162 /* e_atan2.c */; }; - A7D8BAD323E2514500DCD162 /* s_tan.c in Sources */ = {isa = PBXBuildFile; fileRef = A7D8A91E23E2514000DCD162 /* s_tan.c */; }; - A7D8BAD923E2514500DCD162 /* e_rem_pio2.c in Sources */ = {isa = PBXBuildFile; fileRef = A7D8A91F23E2514000DCD162 /* e_rem_pio2.c */; }; - A7D8BADF23E2514500DCD162 /* e_fmod.c in Sources */ = {isa = PBXBuildFile; fileRef = A7D8A92023E2514000DCD162 /* e_fmod.c */; }; - A7D8BAE523E2514500DCD162 /* e_exp.c in Sources */ = {isa = PBXBuildFile; fileRef = A7D8A92123E2514000DCD162 /* e_exp.c */; }; - A7D8BAEB23E2514500DCD162 /* e_log10.c in Sources */ = {isa = PBXBuildFile; fileRef = A7D8A92223E2514000DCD162 /* e_log10.c */; }; - A7D8BAF123E2514500DCD162 /* e_log.c in Sources */ = {isa = PBXBuildFile; fileRef = A7D8A92323E2514000DCD162 /* e_log.c */; }; - A7D8BAF723E2514500DCD162 /* e_sqrt.c in Sources */ = {isa = PBXBuildFile; fileRef = A7D8A92423E2514000DCD162 /* e_sqrt.c */; }; - A7D8BAFD23E2514500DCD162 /* s_floor.c in Sources */ = {isa = PBXBuildFile; fileRef = A7D8A92523E2514000DCD162 /* s_floor.c */; }; - A7D8BB0323E2514500DCD162 /* math_libm.h in Headers */ = {isa = PBXBuildFile; fileRef = A7D8A92623E2514000DCD162 /* math_libm.h */; }; - A7D8BB0923E2514500DCD162 /* k_tan.c in Sources */ = {isa = PBXBuildFile; fileRef = A7D8A92723E2514000DCD162 /* k_tan.c */; }; A7D8BB1523E2514500DCD162 /* SDL_mouse.c in Sources */ = {isa = PBXBuildFile; fileRef = A7D8A92A23E2514000DCD162 /* SDL_mouse.c */; }; A7D8BB1B23E2514500DCD162 /* SDL_mouse_c.h in Headers */ = {isa = PBXBuildFile; fileRef = A7D8A92B23E2514000DCD162 /* SDL_mouse_c.h */; }; A7D8BB2123E2514500DCD162 /* scancodes_windows.h in Headers */ = {isa = PBXBuildFile; fileRef = A7D8A92C23E2514000DCD162 /* scancodes_windows.h */; }; @@ -346,8 +327,6 @@ A7D8BBA523E2514500DCD162 /* SDL_events_c.h in Headers */ = {isa = PBXBuildFile; fileRef = A7D8A94223E2514000DCD162 /* SDL_events_c.h */; }; A7D8BBAB23E2514500DCD162 /* SDL_windowevents_c.h in Headers */ = {isa = PBXBuildFile; fileRef = A7D8A94323E2514000DCD162 /* SDL_windowevents_c.h */; }; A7D8BBB123E2514500DCD162 /* SDL_assert.c in Sources */ = {isa = PBXBuildFile; fileRef = A7D8A94423E2514000DCD162 /* SDL_assert.c */; }; - A7D8BBC523E2561500DCD162 /* SDL_steamcontroller.c in Sources */ = {isa = PBXBuildFile; fileRef = A7D8A7A723E2513E00DCD162 /* SDL_steamcontroller.c */; }; - A7D8BBC723E2561500DCD162 /* SDL_steamcontroller.h in Headers */ = {isa = PBXBuildFile; fileRef = A7D8A7A523E2513E00DCD162 /* SDL_steamcontroller.h */; }; A7D8BBD223E2574800DCD162 /* SDL_uikitappdelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = A7D8A62F23E2513D00DCD162 /* SDL_uikitappdelegate.h */; }; A7D8BBD323E2574800DCD162 /* SDL_uikitappdelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = A7D8A61E23E2513D00DCD162 /* SDL_uikitappdelegate.m */; }; A7D8BBD423E2574800DCD162 /* SDL_uikitclipboard.h in Headers */ = {isa = PBXBuildFile; fileRef = A7D8A62123E2513D00DCD162 /* SDL_uikitclipboard.h */; }; @@ -407,7 +386,10 @@ F32DDAD12AB795A30041EAA5 /* SDL_audioqueue.c in Sources */ = {isa = PBXBuildFile; fileRef = F32DDACB2AB795A30041EAA5 /* SDL_audioqueue.c */; }; F32DDAD32AB795A30041EAA5 /* SDL_audioqueue.h in Headers */ = {isa = PBXBuildFile; fileRef = F32DDACD2AB795A30041EAA5 /* SDL_audioqueue.h */; }; F32DDAD42AB795A30041EAA5 /* SDL_audioresample.c in Sources */ = {isa = PBXBuildFile; fileRef = F32DDACE2AB795A30041EAA5 /* SDL_audioresample.c */; }; + F338A1182D1B37D8007CDFDF /* SDL_tray.m in Sources */ = {isa = PBXBuildFile; fileRef = F338A1172D1B37D8007CDFDF /* SDL_tray.m */; }; + F338A11A2D1B37E4007CDFDF /* SDL_tray.c in Sources */ = {isa = PBXBuildFile; fileRef = F338A1192D1B37E4007CDFDF /* SDL_tray.c */; }; F34B9895291DEFF500AAC96E /* SDL_hidapi_steam.c in Sources */ = {isa = PBXBuildFile; fileRef = A75FDAAC23E2795C00529352 /* SDL_hidapi_steam.c */; }; + F35B79632D03A7B900C6D591 /* SDL_asyncio.h in Headers */ = {isa = PBXBuildFile; fileRef = 00004945A946DF5B1AED0000 /* SDL_asyncio.h */; settings = {ATTRIBUTES = (Public, ); }; }; F362B9192B3349E200D30B94 /* controller_list.h in Headers */ = {isa = PBXBuildFile; fileRef = F362B9152B3349E200D30B94 /* controller_list.h */; }; F362B91A2B3349E200D30B94 /* SDL_gamepad_c.h in Headers */ = {isa = PBXBuildFile; fileRef = F362B9162B3349E200D30B94 /* SDL_gamepad_c.h */; }; F362B91B2B3349E200D30B94 /* SDL_steam_virtual_gamepad.h in Headers */ = {isa = PBXBuildFile; fileRef = F362B9172B3349E200D30B94 /* SDL_steam_virtual_gamepad.h */; }; @@ -471,11 +453,9 @@ F3E5A6EB2AD5E0E600293D83 /* SDL_properties.c in Sources */ = {isa = PBXBuildFile; fileRef = F3E5A6EA2AD5E0E600293D83 /* SDL_properties.c */; }; F3E5A6ED2AD5E10800293D83 /* SDL_properties.h in Headers */ = {isa = PBXBuildFile; fileRef = F3E5A6EC2AD5E10800293D83 /* SDL_properties.h */; settings = {ATTRIBUTES = (Public, ); }; }; F3F07D5A269640160074468B /* SDL_hidapi_luna.c in Sources */ = {isa = PBXBuildFile; fileRef = F3F07D59269640160074468B /* SDL_hidapi_luna.c */; }; - F3F528CB2C29E1C300E6CC26 /* s_isnanf.c in Sources */ = {isa = PBXBuildFile; fileRef = F3F528C62C29E1C300E6CC26 /* s_isnanf.c */; }; - F3F528CC2C29E1C300E6CC26 /* s_isinf.c in Sources */ = {isa = PBXBuildFile; fileRef = F3F528C72C29E1C300E6CC26 /* s_isinf.c */; }; - F3F528CD2C29E1C300E6CC26 /* s_isnan.c in Sources */ = {isa = PBXBuildFile; fileRef = F3F528C82C29E1C300E6CC26 /* s_isnan.c */; }; - F3F528CE2C29E1C300E6CC26 /* s_modf.c in Sources */ = {isa = PBXBuildFile; fileRef = F3F528C92C29E1C300E6CC26 /* s_modf.c */; }; - F3F528CF2C29E1C300E6CC26 /* s_isinff.c in Sources */ = {isa = PBXBuildFile; fileRef = F3F528CA2C29E1C300E6CC26 /* s_isinff.c */; }; + F3F15D7F2D011912007AE210 /* SDL_dialog.c in Sources */ = {isa = PBXBuildFile; fileRef = F3F15D7D2D011912007AE210 /* SDL_dialog.c */; }; + F3F15D802D011912007AE210 /* SDL_dialog_utils.h in Headers */ = {isa = PBXBuildFile; fileRef = F3F15D7E2D011912007AE210 /* SDL_dialog_utils.h */; }; + F3F15D812D011912007AE210 /* SDL_dialog.h in Headers */ = {isa = PBXBuildFile; fileRef = F3F15D7C2D011912007AE210 /* SDL_dialog.h */; }; F3F7D8ED2933074E00816151 /* SDL_audio.h in Headers */ = {isa = PBXBuildFile; fileRef = F3F7D8AA2933074900816151 /* SDL_audio.h */; settings = {ATTRIBUTES = (Public, ); }; }; F3F7D8F12933074E00816151 /* SDL_platform.h in Headers */ = {isa = PBXBuildFile; fileRef = F3F7D8AB2933074900816151 /* SDL_platform.h */; settings = {ATTRIBUTES = (Public, ); }; }; F3F7D8F52933074E00816151 /* SDL_stdinc.h in Headers */ = {isa = PBXBuildFile; fileRef = F3F7D8AC2933074900816151 /* SDL_stdinc.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -576,8 +556,12 @@ 0000035D38C3899C7EFD0000 /* SDL_camera.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_camera.c; sourceTree = ""; }; 00002B010DB1A70931C20000 /* SDL_filesystem.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_filesystem.c; sourceTree = ""; }; 00002F2F5496FA184A0F0000 /* SDL_cocoapen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_cocoapen.h; sourceTree = ""; }; + 000030DD21496B5C0F210000 /* SDL_asyncio_windows_ioring.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_asyncio_windows_ioring.c; sourceTree = ""; }; 00003260407E1002EAC10000 /* SDL_main_callbacks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_main_callbacks.h; sourceTree = ""; }; + 00003928A612EC33D42C0000 /* SDL_asyncio.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_asyncio.c; sourceTree = ""; }; 00003F472C51CE7DF6160000 /* SDL_systime.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_systime.c; sourceTree = ""; }; + 00004945A946DF5B1AED0000 /* SDL_asyncio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDL_asyncio.h; path = SDL3/SDL_asyncio.h; sourceTree = ""; }; + 0000585B2CAB450B40540000 /* SDL_sysasyncio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_sysasyncio.h; sourceTree = ""; }; 00005BD74B46358B33A20000 /* SDL_camera_dummy.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_camera_dummy.c; sourceTree = ""; }; 00005D3EB902478835E20000 /* SDL_syscamera.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_syscamera.h; sourceTree = ""; }; 0000641A9BAC11AB3FBE0000 /* SDL_time.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_time.c; sourceTree = ""; }; @@ -585,12 +569,14 @@ 000084ED0A56E3ED52F90000 /* SDL_camera.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDL_camera.h; path = SDL3/SDL_camera.h; sourceTree = ""; }; 00008B79BF08CBCEAC460000 /* SDL_camera_coremedia.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDL_camera_coremedia.m; sourceTree = ""; }; 00009003C7148E1126CA0000 /* SDL_camera_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_camera_c.h; sourceTree = ""; }; + 0000919399B1A908267F0000 /* SDL_asyncio_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_asyncio_c.h; sourceTree = ""; }; 00009366FB9FBBD54C390000 /* SDL_main_callbacks.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_main_callbacks.c; sourceTree = ""; }; 0000B6ADCD88CAD6610F0000 /* SDL_hashtable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_hashtable.h; sourceTree = ""; }; 0000BB287BA0A0178C1A0000 /* SDL_sysmain_callbacks.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDL_sysmain_callbacks.m; sourceTree = ""; }; 0000CCA310B73A7B59910000 /* SDL_cocoapen.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDL_cocoapen.m; sourceTree = ""; }; 0000F4E6AA3EF99DA3C80000 /* SDL_sysfsops.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_sysfsops.c; sourceTree = ""; }; 0000F6C6A072ED4E3D660000 /* SDL_dialog_utils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_dialog_utils.c; sourceTree = ""; }; + 0000FB02CDE4BE34A87E0000 /* SDL_asyncio_generic.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_asyncio_generic.c; sourceTree = ""; }; 0073179D0858DECD00B2BC32 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; 0073179F0858DECD00B2BC32 /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = System/Library/Frameworks/IOKit.framework; sourceTree = SDKROOT; }; 007317C10858E15000B2BC32 /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = System/Library/Frameworks/Carbon.framework; sourceTree = SDKROOT; }; @@ -798,8 +784,6 @@ A7D8A78823E2513E00DCD162 /* SDL_sysmutex_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_sysmutex_c.h; sourceTree = ""; }; A7D8A79E23E2513E00DCD162 /* SDL_gamepad_db.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_gamepad_db.h; sourceTree = ""; }; A7D8A7A023E2513E00DCD162 /* SDL_sysjoystick.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_sysjoystick.c; sourceTree = ""; }; - A7D8A7A523E2513E00DCD162 /* SDL_steamcontroller.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_steamcontroller.h; sourceTree = ""; }; - A7D8A7A723E2513E00DCD162 /* SDL_steamcontroller.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_steamcontroller.c; sourceTree = ""; }; A7D8A7A923E2513E00DCD162 /* SDL_joystick.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_joystick.c; sourceTree = ""; }; A7D8A7AD23E2513E00DCD162 /* SDL_gamepad.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_gamepad.c; sourceTree = ""; }; A7D8A7C223E2513E00DCD162 /* SDL_hidapi_xbox360.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_hidapi_xbox360.c; sourceTree = ""; }; @@ -885,28 +869,6 @@ A7D8A90E23E2514000DCD162 /* SDL_glfuncs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_glfuncs.h; sourceTree = ""; }; A7D8A90F23E2514000DCD162 /* SDL_render_gl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_render_gl.c; sourceTree = ""; }; A7D8A91023E2514000DCD162 /* SDL_shaders_gl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_shaders_gl.c; sourceTree = ""; }; - A7D8A91223E2514000DCD162 /* s_sin.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = s_sin.c; sourceTree = ""; }; - A7D8A91323E2514000DCD162 /* s_cos.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = s_cos.c; sourceTree = ""; }; - A7D8A91423E2514000DCD162 /* s_copysign.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = s_copysign.c; sourceTree = ""; }; - A7D8A91523E2514000DCD162 /* s_fabs.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = s_fabs.c; sourceTree = ""; }; - A7D8A91623E2514000DCD162 /* k_rem_pio2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = k_rem_pio2.c; sourceTree = ""; }; - A7D8A91723E2514000DCD162 /* k_sin.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = k_sin.c; sourceTree = ""; }; - A7D8A91823E2514000DCD162 /* s_atan.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = s_atan.c; sourceTree = ""; }; - A7D8A91923E2514000DCD162 /* k_cos.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = k_cos.c; sourceTree = ""; }; - A7D8A91A23E2514000DCD162 /* s_scalbn.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = s_scalbn.c; sourceTree = ""; }; - A7D8A91B23E2514000DCD162 /* math_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = math_private.h; sourceTree = ""; }; - A7D8A91C23E2514000DCD162 /* e_pow.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = e_pow.c; sourceTree = ""; }; - A7D8A91D23E2514000DCD162 /* e_atan2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = e_atan2.c; sourceTree = ""; }; - A7D8A91E23E2514000DCD162 /* s_tan.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = s_tan.c; sourceTree = ""; }; - A7D8A91F23E2514000DCD162 /* e_rem_pio2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = e_rem_pio2.c; sourceTree = ""; }; - A7D8A92023E2514000DCD162 /* e_fmod.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = e_fmod.c; sourceTree = ""; }; - A7D8A92123E2514000DCD162 /* e_exp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = e_exp.c; sourceTree = ""; }; - A7D8A92223E2514000DCD162 /* e_log10.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = e_log10.c; sourceTree = ""; }; - A7D8A92323E2514000DCD162 /* e_log.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = e_log.c; sourceTree = ""; }; - A7D8A92423E2514000DCD162 /* e_sqrt.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = e_sqrt.c; sourceTree = ""; }; - A7D8A92523E2514000DCD162 /* s_floor.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = s_floor.c; sourceTree = ""; }; - A7D8A92623E2514000DCD162 /* math_libm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = math_libm.h; sourceTree = ""; }; - A7D8A92723E2514000DCD162 /* k_tan.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = k_tan.c; sourceTree = ""; }; A7D8A92A23E2514000DCD162 /* SDL_mouse.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_mouse.c; sourceTree = ""; }; A7D8A92B23E2514000DCD162 /* SDL_mouse_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_mouse_c.h; sourceTree = ""; }; A7D8A92C23E2514000DCD162 /* scancodes_windows.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = scancodes_windows.h; sourceTree = ""; }; @@ -967,6 +929,8 @@ F32DDACB2AB795A30041EAA5 /* SDL_audioqueue.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_audioqueue.c; sourceTree = ""; }; F32DDACD2AB795A30041EAA5 /* SDL_audioqueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_audioqueue.h; sourceTree = ""; }; F32DDACE2AB795A30041EAA5 /* SDL_audioresample.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_audioresample.c; sourceTree = ""; }; + F338A1172D1B37D8007CDFDF /* SDL_tray.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDL_tray.m; sourceTree = ""; }; + F338A1192D1B37E4007CDFDF /* SDL_tray.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = SDL_tray.c; sourceTree = ""; }; F362B9152B3349E200D30B94 /* controller_list.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = controller_list.h; sourceTree = ""; }; F362B9162B3349E200D30B94 /* SDL_gamepad_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_gamepad_c.h; sourceTree = ""; }; F362B9172B3349E200D30B94 /* SDL_steam_virtual_gamepad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_steam_virtual_gamepad.h; sourceTree = ""; }; @@ -1044,11 +1008,9 @@ F3E5A6EA2AD5E0E600293D83 /* SDL_properties.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_properties.c; sourceTree = ""; }; F3E5A6EC2AD5E10800293D83 /* SDL_properties.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDL_properties.h; path = SDL3/SDL_properties.h; sourceTree = ""; }; F3F07D59269640160074468B /* SDL_hidapi_luna.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_hidapi_luna.c; sourceTree = ""; }; - F3F528C62C29E1C300E6CC26 /* s_isnanf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = s_isnanf.c; sourceTree = ""; }; - F3F528C72C29E1C300E6CC26 /* s_isinf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = s_isinf.c; sourceTree = ""; }; - F3F528C82C29E1C300E6CC26 /* s_isnan.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = s_isnan.c; sourceTree = ""; }; - F3F528C92C29E1C300E6CC26 /* s_modf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = s_modf.c; sourceTree = ""; }; - F3F528CA2C29E1C300E6CC26 /* s_isinff.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = s_isinff.c; sourceTree = ""; }; + F3F15D7C2D011912007AE210 /* SDL_dialog.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDL_dialog.h; sourceTree = ""; }; + F3F15D7D2D011912007AE210 /* SDL_dialog.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = SDL_dialog.c; sourceTree = ""; }; + F3F15D7E2D011912007AE210 /* SDL_dialog_utils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDL_dialog_utils.h; sourceTree = ""; }; F3F7BE3B2CBD79D200C984AF /* config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = config.xcconfig; sourceTree = ""; }; F3F7D8AA2933074900816151 /* SDL_audio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDL_audio.h; path = SDL3/SDL_audio.h; sourceTree = ""; }; F3F7D8AB2933074900816151 /* SDL_platform.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDL_platform.h; path = SDL3/SDL_platform.h; sourceTree = ""; }; @@ -1158,6 +1120,14 @@ path = unix; sourceTree = ""; }; + 000013C0F2EADC24ADC10000 /* generic */ = { + isa = PBXGroup; + children = ( + 0000FB02CDE4BE34A87E0000 /* SDL_asyncio_generic.c */, + ); + path = generic; + sourceTree = ""; + }; 000023E01FD84242AF850000 /* dummy */ = { isa = PBXGroup; children = ( @@ -1186,6 +1156,14 @@ path = posix; sourceTree = ""; }; + 000064F9A2AAE947C1CD0000 /* windows */ = { + isa = PBXGroup; + children = ( + 000030DD21496B5C0F210000 /* SDL_asyncio_windows_ioring.c */, + ); + path = windows; + sourceTree = ""; + }; 000082EF09C89B62BD840000 /* main */ = { isa = PBXGroup; children = ( @@ -1227,7 +1205,9 @@ 0153844A006D81B07F000001 /* Public Headers */ = { isa = PBXGroup; children = ( + F3F7D8CF2933074C00816151 /* SDL.h */, F3F7D8E02933074D00816151 /* SDL_assert.h */, + 00004945A946DF5B1AED0000 /* SDL_asyncio.h */, F3F7D8B92933074A00816151 /* SDL_atomic.h */, F3F7D8AA2933074900816151 /* SDL_audio.h */, F3F7D8E72933074E00816151 /* SDL_begin_code.h */, @@ -1259,26 +1239,26 @@ F3F7D8D92933074C00816151 /* SDL_loadso.h */, F3F7D8C42933074B00816151 /* SDL_locale.h */, F3F7D8B72933074A00816151 /* SDL_log.h */, - F3B38CCA296E2E52005DA6D3 /* SDL_main_impl.h */, F3F7D8B02933074900816151 /* SDL_main.h */, + F3B38CCA296E2E52005DA6D3 /* SDL_main_impl.h */, F3F7D8B62933074A00816151 /* SDL_messagebox.h */, F3F7D8D22933074C00816151 /* SDL_metal.h */, F3F7D8D52933074C00816151 /* SDL_misc.h */, F3F7D8DA2933074D00816151 /* SDL_mouse.h */, F3F7D8E62933074E00816151 /* SDL_mutex.h */, F3B38CCD296E2E52005DA6D3 /* SDL_oldnames.h */, - F3F7D8C02933074A00816151 /* SDL_opengl_glext.h */, F3F7D8E12933074D00816151 /* SDL_opengl.h */, + F3F7D8C02933074A00816151 /* SDL_opengl_glext.h */, F3F7D8C62933074B00816151 /* SDL_opengles.h */, + F3F7D8C72933074B00816151 /* SDL_opengles2.h */, F3F7D8AE2933074900816151 /* SDL_opengles2_gl2.h */, F3F7D8BD2933074A00816151 /* SDL_opengles2_gl2ext.h */, F3F7D8C92933074B00816151 /* SDL_opengles2_gl2platform.h */, F3F7D8B12933074900816151 /* SDL_opengles2_khrplatform.h */, - F3F7D8C72933074B00816151 /* SDL_opengles2.h */, 63134A212A7902CF0021E9A6 /* SDL_pen.h */, F3F7D8B52933074A00816151 /* SDL_pixels.h */, - F3B38CCB296E2E52005DA6D3 /* SDL_platform_defines.h */, F3F7D8AB2933074900816151 /* SDL_platform.h */, + F3B38CCB296E2E52005DA6D3 /* SDL_platform_defines.h */, F3F7D8DB2933074D00816151 /* SDL_power.h */, F3B439472C93595900792030 /* SDL_process.h */, F3E5A6EC2AD5E10800293D83 /* SDL_properties.h */, @@ -1298,7 +1278,6 @@ F3F7D8E42933074D00816151 /* SDL_version.h */, F3F7D8C52933074B00816151 /* SDL_video.h */, F3F7D8D42933074C00816151 /* SDL_vulkan.h */, - F3F7D8CF2933074C00816151 /* SDL.h */, ); name = "Public Headers"; path = ../../include; @@ -1349,7 +1328,6 @@ A7D8A5C223E2513D00DCD162 /* haptic */, A7D8A80923E2513F00DCD162 /* hidapi */, A7D8A79D23E2513E00DCD162 /* joystick */, - A7D8A91123E2514000DCD162 /* libm */, A7D8A85D23E2513F00DCD162 /* loadso */, 566E26CB246274AE00718109 /* locale */, 000082EF09C89B62BD840000 /* main */, @@ -1363,25 +1341,26 @@ A7D8A77623E2513E00DCD162 /* thread */, 0000F5E7419220E3A8AB0000 /* time */, A7D8A5DE23E2513D00DCD162 /* timer */, + F338A1142D1B3735007CDFDF /* tray */, A7D8A5EB23E2513D00DCD162 /* video */, - A7D8A7F523E2513F00DCD162 /* SDL_assert_c.h */, - A7D8A94423E2514000DCD162 /* SDL_assert.c */, - A7D8A57523E2513D00DCD162 /* SDL_error_c.h */, - A7D8A8BF23E2513F00DCD162 /* SDL_error.c */, - F382071C284F362F004DD584 /* SDL_guid.c */, - 000078E1881E857EBB6C0000 /* SDL_hashtable.c */, - 0000B6ADCD88CAD6610F0000 /* SDL_hashtable.h */, - A7D8A8D123E2514000DCD162 /* SDL_hints_c.h */, - A7D8A5AB23E2513D00DCD162 /* SDL_hints.c */, - A7D8A58323E2513D00DCD162 /* SDL_internal.h */, - A1BB8B6127F6CF320057CFA8 /* SDL_list.c */, - A1BB8B6227F6CF330057CFA8 /* SDL_list.h */, - F386F6E42884663E001840AA /* SDL_log_c.h */, - A7D8A5DD23E2513D00DCD162 /* SDL_log.c */, - F3E5A6EA2AD5E0E600293D83 /* SDL_properties.c */, - F386F6E52884663E001840AA /* SDL_utils_c.h */, - F386F6E62884663E001840AA /* SDL_utils.c */, A7D8A57123E2513D00DCD162 /* SDL.c */, + A7D8A94423E2514000DCD162 /* SDL_assert.c */, + A7D8A7F523E2513F00DCD162 /* SDL_assert_c.h */, + A7D8A8BF23E2513F00DCD162 /* SDL_error.c */, + A7D8A57523E2513D00DCD162 /* SDL_error_c.h */, + F382071C284F362F004DD584 /* SDL_guid.c */, + 0000B6ADCD88CAD6610F0000 /* SDL_hashtable.h */, + 000078E1881E857EBB6C0000 /* SDL_hashtable.c */, + A7D8A5AB23E2513D00DCD162 /* SDL_hints.c */, + A7D8A8D123E2514000DCD162 /* SDL_hints_c.h */, + A7D8A58323E2513D00DCD162 /* SDL_internal.h */, + A1BB8B6227F6CF330057CFA8 /* SDL_list.h */, + A1BB8B6127F6CF320057CFA8 /* SDL_list.c */, + A7D8A5DD23E2513D00DCD162 /* SDL_log.c */, + F386F6E42884663E001840AA /* SDL_log_c.h */, + F3E5A6EA2AD5E0E600293D83 /* SDL_properties.c */, + F386F6E62884663E001840AA /* SDL_utils.c */, + F386F6E52884663E001840AA /* SDL_utils_c.h */, ); name = "Library Source"; path = ../../src; @@ -1857,7 +1836,6 @@ A7D8A7CC23E2513E00DCD162 /* darwin */, A7D8A79F23E2513E00DCD162 /* dummy */, A7D8A7BE23E2513E00DCD162 /* hidapi */, - A7D8A7A123E2513E00DCD162 /* steam */, 75E09157241EA924004729E1 /* virtual */, F362B9152B3349E200D30B94 /* controller_list.h */, F3820712284F3609004DD584 /* controller_type.c */, @@ -1883,15 +1861,6 @@ path = dummy; sourceTree = ""; }; - A7D8A7A123E2513E00DCD162 /* steam */ = { - isa = PBXGroup; - children = ( - A7D8A7A723E2513E00DCD162 /* SDL_steamcontroller.c */, - A7D8A7A523E2513E00DCD162 /* SDL_steamcontroller.h */, - ); - path = steam; - sourceTree = ""; - }; A7D8A7AA23E2513E00DCD162 /* apple */ = { isa = PBXGroup; children = ( @@ -1942,6 +1911,11 @@ isa = PBXGroup; children = ( A7D8A7DB23E2513F00DCD162 /* SDL_iostream.c */, + 00003928A612EC33D42C0000 /* SDL_asyncio.c */, + 0000919399B1A908267F0000 /* SDL_asyncio_c.h */, + 0000585B2CAB450B40540000 /* SDL_sysasyncio.h */, + 000013C0F2EADC24ADC10000 /* generic */, + 000064F9A2AAE947C1CD0000 /* windows */, ); path = file; sourceTree = ""; @@ -2199,40 +2173,6 @@ path = opengl; sourceTree = ""; }; - A7D8A91123E2514000DCD162 /* libm */ = { - isa = PBXGroup; - children = ( - A7D8A91D23E2514000DCD162 /* e_atan2.c */, - A7D8A92123E2514000DCD162 /* e_exp.c */, - A7D8A92023E2514000DCD162 /* e_fmod.c */, - A7D8A92323E2514000DCD162 /* e_log.c */, - A7D8A92223E2514000DCD162 /* e_log10.c */, - A7D8A91C23E2514000DCD162 /* e_pow.c */, - A7D8A91F23E2514000DCD162 /* e_rem_pio2.c */, - A7D8A92423E2514000DCD162 /* e_sqrt.c */, - A7D8A91923E2514000DCD162 /* k_cos.c */, - A7D8A91623E2514000DCD162 /* k_rem_pio2.c */, - A7D8A91723E2514000DCD162 /* k_sin.c */, - A7D8A92723E2514000DCD162 /* k_tan.c */, - A7D8A92623E2514000DCD162 /* math_libm.h */, - A7D8A91B23E2514000DCD162 /* math_private.h */, - A7D8A91823E2514000DCD162 /* s_atan.c */, - A7D8A91423E2514000DCD162 /* s_copysign.c */, - A7D8A91323E2514000DCD162 /* s_cos.c */, - A7D8A91523E2514000DCD162 /* s_fabs.c */, - A7D8A92523E2514000DCD162 /* s_floor.c */, - F3F528C72C29E1C300E6CC26 /* s_isinf.c */, - F3F528CA2C29E1C300E6CC26 /* s_isinff.c */, - F3F528C82C29E1C300E6CC26 /* s_isnan.c */, - F3F528C62C29E1C300E6CC26 /* s_isnanf.c */, - F3F528C92C29E1C300E6CC26 /* s_modf.c */, - A7D8A91A23E2514000DCD162 /* s_scalbn.c */, - A7D8A91223E2514000DCD162 /* s_sin.c */, - A7D8A91E23E2514000DCD162 /* s_tan.c */, - ); - path = libm; - sourceTree = ""; - }; A7D8A92923E2514000DCD162 /* events */ = { isa = PBXGroup; children = ( @@ -2333,6 +2273,31 @@ path = gpu; sourceTree = ""; }; + F338A1142D1B3735007CDFDF /* tray */ = { + isa = PBXGroup; + children = ( + F338A1162D1B378D007CDFDF /* cocoa */, + F338A1152D1B3786007CDFDF /* dummy */, + ); + path = tray; + sourceTree = ""; + }; + F338A1152D1B3786007CDFDF /* dummy */ = { + isa = PBXGroup; + children = ( + F338A1192D1B37E4007CDFDF /* SDL_tray.c */, + ); + path = dummy; + sourceTree = ""; + }; + F338A1162D1B378D007CDFDF /* cocoa */ = { + isa = PBXGroup; + children = ( + F338A1172D1B37D8007CDFDF /* SDL_tray.m */, + ); + path = cocoa; + sourceTree = ""; + }; F36C7ACF294B9F5E004D61C3 /* core */ = { isa = PBXGroup; children = ( @@ -2346,6 +2311,9 @@ children = ( F37E18552BA50ED50098C111 /* cocoa */, F37E18562BA50F2A0098C111 /* dummy */, + F3F15D7C2D011912007AE210 /* SDL_dialog.h */, + F3F15D7D2D011912007AE210 /* SDL_dialog.c */, + F3F15D7E2D011912007AE210 /* SDL_dialog_utils.h */, 0000F6C6A072ED4E3D660000 /* SDL_dialog_utils.c */, ); path = dialog; @@ -2491,6 +2459,8 @@ A7D8B8A223E2514400DCD162 /* SDL_diskaudio.h in Headers */, F3B439482C93595900792030 /* SDL_process.h in Headers */, A7D8BB3F23E2514500DCD162 /* SDL_displayevents_c.h in Headers */, + F3F15D802D011912007AE210 /* SDL_dialog_utils.h in Headers */, + F3F15D812D011912007AE210 /* SDL_dialog.h in Headers */, A7D8BA1923E2514400DCD162 /* SDL_draw.h in Headers */, F3C2CB222C5DDDB2004D7998 /* SDL_categories_c.h in Headers */, E479118E2BA9555500CE3B7F /* SDL_sysstorage.h in Headers */, @@ -2560,6 +2530,7 @@ F31A92C828D4CB39003BFD6A /* SDL_offscreenopengles.h in Headers */, A7D8AB6D23E2514100DCD162 /* SDL_offscreenvideo.h in Headers */, A7D8AB8523E2514100DCD162 /* SDL_offscreenwindow.h in Headers */, + F35B79632D03A7B900C6D591 /* SDL_asyncio.h in Headers */, F37E18642BAA40670098C111 /* SDL_time_c.h in Headers */, F3B38CDB296E2E52005DA6D3 /* SDL_oldnames.h in Headers */, F3F7D9C92933074E00816151 /* SDL_opengl.h in Headers */, @@ -2601,7 +2572,6 @@ F310138F2C1F2CB700FBE946 /* SDL_sysstdlib.h in Headers */, F3F7D8F52933074E00816151 /* SDL_stdinc.h in Headers */, F362B91B2B3349E200D30B94 /* SDL_steam_virtual_gamepad.h in Headers */, - A7D8BBC723E2561500DCD162 /* SDL_steamcontroller.h in Headers */, F3F7D9312933074E00816151 /* SDL_surface.h in Headers */, A7D8B85A23E2514400DCD162 /* SDL_sysaudio.h in Headers */, A7D8AAD423E2514100DCD162 /* SDL_syshaptic.h in Headers */, @@ -2665,8 +2635,6 @@ A7D8B23023E2514200DCD162 /* gl2platform.h in Headers */, A75FDB5823E39E6100529352 /* hidapi.h in Headers */, A7D8B23623E2514200DCD162 /* khrplatform.h in Headers */, - A7D8BB0323E2514500DCD162 /* math_libm.h in Headers */, - A7D8BAC123E2514500DCD162 /* math_private.h in Headers */, A7D8BB5123E2514500DCD162 /* scancodes_darwin.h in Headers */, A7D8BB5D23E2514500DCD162 /* scancodes_linux.h in Headers */, A7D8BB2123E2514500DCD162 /* scancodes_windows.h in Headers */, @@ -2825,7 +2793,6 @@ A7D8B9E323E2514400DCD162 /* SDL_drawline.c in Sources */, A7D8AE7C23E2514100DCD162 /* SDL_yuv.c in Sources */, A7D8B62F23E2514300DCD162 /* SDL_sysfilesystem.m in Sources */, - A7D8BAC723E2514500DCD162 /* e_pow.c in Sources */, A7D8B41C23E2514300DCD162 /* SDL_systls.c in Sources */, 9846B07C287A9020000C35C8 /* SDL_hidapi_shield.c in Sources */, F31013C72C24E98200FBE946 /* SDL_keymap.c in Sources */, @@ -2837,7 +2804,6 @@ F36C34322C0F876500991150 /* SDL_offscreenvulkan.c in Sources */, A7D8A95123E2514000DCD162 /* SDL_spinlock.c in Sources */, F34B9895291DEFF500AAC96E /* SDL_hidapi_steam.c in Sources */, - A7D8BAAF23E2514400DCD162 /* s_atan.c in Sources */, A7D8B75223E2514300DCD162 /* SDL_sysloadso.c in Sources */, A7D8BBE123E2574800DCD162 /* SDL_uikitopenglview.m in Sources */, A79745702B2E9D39009D224A /* SDL_hidapi_steamdeck.c in Sources */, @@ -2848,6 +2814,7 @@ A7D8B86623E2514400DCD162 /* SDL_audiocvt.c in Sources */, A7D8B9F523E2514400DCD162 /* SDL_rotate.c in Sources */, A7D8BBE323E2574800DCD162 /* SDL_uikitvideo.m in Sources */, + F338A1182D1B37D8007CDFDF /* SDL_tray.m in Sources */, 5616CA4E252BB2A6005D5928 /* SDL_sysurl.m in Sources */, F3B439562C937DAB00792030 /* SDL_process.c in Sources */, A7D8A97523E2514000DCD162 /* SDL_coremotionsensor.m in Sources */, @@ -2856,9 +2823,7 @@ F31A92D228D4CB39003BFD6A /* SDL_offscreenopengles.c in Sources */, A1626A3E2617006A003F1973 /* SDL_triangle.c in Sources */, A7D8B3F223E2514300DCD162 /* SDL_thread.c in Sources */, - F3F528CF2C29E1C300E6CC26 /* s_isinff.c in Sources */, A7D8B55D23E2514300DCD162 /* SDL_hidapi_xbox360w.c in Sources */, - F3F528CB2C29E1C300E6CC26 /* s_isnanf.c in Sources */, A7D8A95723E2514000DCD162 /* SDL_atomic.c in Sources */, A75FDBCE23EA380300529352 /* SDL_hidapi_rumble.c in Sources */, E4F257952C81903800FCEAFC /* SDL_gpu_vulkan.c in Sources */, @@ -2869,7 +2834,6 @@ F37E18582BA50F3B0098C111 /* SDL_cocoadialog.m in Sources */, A7D8B43423E2514300DCD162 /* SDL_systhread.c in Sources */, A7D8BB3323E2514500DCD162 /* SDL_windowevents.c in Sources */, - A7D8BABB23E2514400DCD162 /* s_scalbn.c in Sources */, F3973FAB28A59BDD00B84553 /* SDL_crc16.c in Sources */, A7D8AB2B23E2514100DCD162 /* SDL_timer.c in Sources */, E4F257962C81903800FCEAFC /* SDL_gpu.c in Sources */, @@ -2889,6 +2853,7 @@ A7D8BA3723E2514400DCD162 /* SDL_d3dmath.c in Sources */, F3A9AE9C2C8A13C100AAC390 /* SDL_pipeline_gpu.c in Sources */, 75E0915A241EA924004729E1 /* SDL_virtualjoystick.c in Sources */, + F338A11A2D1B37E4007CDFDF /* SDL_tray.c in Sources */, A7D8ABEB23E2514100DCD162 /* SDL_nullvideo.c in Sources */, F3990E072A78833C000D8759 /* hid.m in Sources */, A7D8AB6723E2514100DCD162 /* SDL_offscreenevents.c in Sources */, @@ -2899,18 +2864,13 @@ A7D8BBE523E2574800DCD162 /* SDL_uikitview.m in Sources */, A7D8BBE923E2574800DCD162 /* SDL_uikitvulkan.m in Sources */, A7D8ABCD23E2514100DCD162 /* SDL_blit_slow.c in Sources */, - A7D8BA9723E2514400DCD162 /* s_copysign.c in Sources */, F3984CD025BCC92900374F43 /* SDL_hidapi_stadia.c in Sources */, A7D8AAB623E2514100DCD162 /* SDL_haptic.c in Sources */, E4F257922C81903800FCEAFC /* Metal_Blit.metal in Sources */, A7D8AF2423E2514100DCD162 /* SDL_cocoametalview.m in Sources */, A7D8B86023E2514400DCD162 /* SDL_audiotypecvt.c in Sources */, - A7D8BBC523E2561500DCD162 /* SDL_steamcontroller.c in Sources */, A7D8AD3223E2514100DCD162 /* SDL_blit_N.c in Sources */, A7D8BB7B23E2514500DCD162 /* SDL_dropevents.c in Sources */, - A7D8BACD23E2514500DCD162 /* e_atan2.c in Sources */, - A7D8BA8B23E2514400DCD162 /* s_sin.c in Sources */, - F3F528CE2C29E1C300E6CC26 /* s_modf.c in Sources */, A7D8BBEB23E2574800DCD162 /* SDL_uikitwindow.m in Sources */, F3B439532C935C2C00792030 /* SDL_posixprocess.c in Sources */, F395BF6525633B2400942BFF /* SDL_crc32.c in Sources */, @@ -2918,7 +2878,6 @@ A7D8AED623E2514100DCD162 /* SDL_cocoakeyboard.m in Sources */, A7D8AB1623E2514100DCD162 /* SDL_dynapi.c in Sources */, A7D8BA8523E2514400DCD162 /* SDL_shaders_gl.c in Sources */, - A7D8BAF123E2514500DCD162 /* e_log.c in Sources */, A7D8AED023E2514100DCD162 /* SDL_cocoamessagebox.m in Sources */, F376F6552559B4E300CFC0BC /* SDL_hidapi.c in Sources */, A7D8BA2B23E2514400DCD162 /* SDL_blendfillrect.c in Sources */, @@ -2928,14 +2887,13 @@ A7D8B8E423E2514400DCD162 /* SDL_error.c in Sources */, A7D8AD6823E2514100DCD162 /* SDL_blit.c in Sources */, A7D8B5BD23E2514300DCD162 /* SDL_iostream.c in Sources */, - A7D8BA9123E2514400DCD162 /* s_cos.c in Sources */, A7D8B9D123E2514400DCD162 /* SDL_yuv_sw.c in Sources */, A7D8B76A23E2514300DCD162 /* SDL_wave.c in Sources */, 5616CA4C252BB2A6005D5928 /* SDL_url.c in Sources */, - A7D8BAD323E2514500DCD162 /* s_tan.c in Sources */, F316ABDB2B5CA721002EF551 /* SDL_memmove.c in Sources */, A7D8AA6523E2514000DCD162 /* SDL_hints.c in Sources */, A7D8B53F23E2514300DCD162 /* SDL_hidapi_ps4.c in Sources */, + F3F15D7F2D011912007AE210 /* SDL_dialog.c in Sources */, F362B91C2B3349E200D30B94 /* SDL_steam_virtual_gamepad.c in Sources */, A7D8AD6E23E2514100DCD162 /* SDL_pixels.c in Sources */, A7D8B75E23E2514300DCD162 /* SDL_sysloadso.c in Sources */, @@ -2943,24 +2901,18 @@ A7D8B5F323E2514300DCD162 /* SDL_syspower.c in Sources */, A7D8B95023E2514400DCD162 /* SDL_iconv.c in Sources */, F3E5A6EB2AD5E0E600293D83 /* SDL_properties.c in Sources */, - A7D8BA9D23E2514400DCD162 /* s_fabs.c in Sources */, - F3F528CC2C29E1C300E6CC26 /* s_isinf.c in Sources */, F395C1B12569C6A000942BFF /* SDL_mfijoystick.m in Sources */, A7D8B99223E2514400DCD162 /* SDL_shaders_metal.metal in Sources */, F3990DF52A787C10000D8759 /* SDL_sysurl.m in Sources */, F316ABD92B5C3185002EF551 /* SDL_memcpy.c in Sources */, A7D8B97A23E2514400DCD162 /* SDL_render.c in Sources */, A7D8ABD323E2514100DCD162 /* SDL_stretch.c in Sources */, - A7D8BAFD23E2514500DCD162 /* s_floor.c in Sources */, A7D8AC3923E2514100DCD162 /* SDL_blit_copy.c in Sources */, - A7D8BADF23E2514500DCD162 /* e_fmod.c in Sources */, A7D8B5CF23E2514300DCD162 /* SDL_syspower.m in Sources */, - A7D8BAEB23E2514500DCD162 /* e_log10.c in Sources */, F3B439512C935C2400792030 /* SDL_dummyprocess.c in Sources */, A7D8B76423E2514300DCD162 /* SDL_mixer.c in Sources */, A7D8BB5723E2514500DCD162 /* SDL_events.c in Sources */, A7D8ADE623E2514100DCD162 /* SDL_blit_0.c in Sources */, - A7D8BB0923E2514500DCD162 /* k_tan.c in Sources */, A7D8B8A823E2514400DCD162 /* SDL_diskaudio.c in Sources */, 56A2373329F9C113003CCA5F /* SDL_sysrwlock.c in Sources */, F3A9AE9A2C8A13C100AAC390 /* SDL_shaders_gpu.c in Sources */, @@ -2980,7 +2932,6 @@ A7D8B95C23E2514400DCD162 /* SDL_string.c in Sources */, A7D8BA7F23E2514400DCD162 /* SDL_render_gl.c in Sources */, A7D8AE9423E2514100DCD162 /* SDL_cocoamodes.m in Sources */, - A7D8BAA323E2514400DCD162 /* k_rem_pio2.c in Sources */, A7D8B95623E2514400DCD162 /* SDL_getenv.c in Sources */, A7D8B56323E2514300DCD162 /* SDL_hidapi_gamecube.c in Sources */, A7D8B4DC23E2514300DCD162 /* SDL_joystick.c in Sources */, @@ -3000,7 +2951,6 @@ A7D8BB7523E2514500DCD162 /* SDL_clipboardevents.c in Sources */, E4F798202AD8D87F00669F54 /* SDL_video_unsupported.c in Sources */, A1BB8B6327F6CF330057CFA8 /* SDL_list.c in Sources */, - A7D8BAB523E2514400DCD162 /* k_cos.c in Sources */, A7D8B54523E2514300DCD162 /* SDL_hidapijoystick.c in Sources */, A7D8B97423E2514400DCD162 /* SDL_malloc.c in Sources */, A7D8B8C623E2514400DCD162 /* SDL_audio.c in Sources */, @@ -3010,14 +2960,12 @@ A7D8AB8B23E2514100DCD162 /* SDL_offscreenvideo.c in Sources */, A7D8B42E23E2514300DCD162 /* SDL_syscond.c in Sources */, A7D8AADA23E2514100DCD162 /* SDL_syshaptic.c in Sources */, - A7D8BAE523E2514500DCD162 /* e_exp.c in Sources */, F3FD042F2C9B755700824C4C /* SDL_hidapi_steam_hori.c in Sources */, A7D8BB8123E2514500DCD162 /* SDL_quit.c in Sources */, F3FA5A232B59ACE000FEAD97 /* yuv_rgb_lsx.c in Sources */, A7D8AEA623E2514100DCD162 /* SDL_cocoawindow.m in Sources */, A7D8B43A23E2514300DCD162 /* SDL_sysmutex.c in Sources */, A7D8AAB023E2514100DCD162 /* SDL_syshaptic.c in Sources */, - F3F528CD2C29E1C300E6CC26 /* s_isnan.c in Sources */, F3F07D5A269640160074468B /* SDL_hidapi_luna.c in Sources */, A7D8BBD523E2574800DCD162 /* SDL_uikitclipboard.m in Sources */, F386F6F92884663E001840AA /* SDL_utils.c in Sources */, @@ -3027,18 +2975,15 @@ A7D8B14023E2514200DCD162 /* SDL_blit_1.c in Sources */, A7D8BBDB23E2574800DCD162 /* SDL_uikitmetalview.m in Sources */, A7D8BB1523E2514500DCD162 /* SDL_mouse.c in Sources */, - A7D8BAD923E2514500DCD162 /* e_rem_pio2.c in Sources */, F395C19C2569C68F00942BFF /* SDL_iokitjoystick.c in Sources */, A7D8B4B223E2514300DCD162 /* SDL_sysjoystick.c in Sources */, A7D8B3E023E2514300DCD162 /* SDL_cpuinfo.c in Sources */, A7D8A99323E2514000DCD162 /* SDL_sensor.c in Sources */, - A7D8BAA923E2514400DCD162 /* k_sin.c in Sources */, A7D8AB4923E2514100DCD162 /* SDL_systimer.c in Sources */, F37E185A2BA50F450098C111 /* SDL_dummydialog.c in Sources */, A7D8BA2523E2514400DCD162 /* SDL_drawpoint.c in Sources */, F3681E802B7AA6240002C6FD /* SDL_cocoashape.m in Sources */, F388C95528B5F6F700661ECF /* SDL_hidapi_ps3.c in Sources */, - A7D8BAF723E2514500DCD162 /* e_sqrt.c in Sources */, F36C7AD1294BA009004D61C3 /* SDL_runapp.c in Sources */, A7D8AEAC23E2514100DCD162 /* SDL_cocoavideo.m in Sources */, A7D8A94B23E2514000DCD162 /* SDL.c in Sources */, @@ -3060,6 +3005,9 @@ 0000140640E77F73F1DF0000 /* SDL_dialog_utils.c in Sources */, 0000D5B526B85DE7AB1C0000 /* SDL_cocoapen.m in Sources */, 6312C66D2B42341400A7BB00 /* SDL_murmur3.c in Sources */, + 0000AEB9AE90228CA2D60000 /* SDL_asyncio.c in Sources */, + 00004D0B73767647AD550000 /* SDL_asyncio_generic.c in Sources */, + 0000A03C0F32C43816F40000 /* SDL_asyncio_windows_ioring.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3081,8 +3029,8 @@ CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; DEPLOYMENT_POSTPROCESSING = YES; - DYLIB_COMPATIBILITY_VERSION = 107.0.0; - DYLIB_CURRENT_VERSION = 107.0.0; + DYLIB_COMPATIBILITY_VERSION = 108.0.0; + DYLIB_CURRENT_VERSION = 108.0.0; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_ALTIVEC_EXTENSIONS = YES; @@ -3114,7 +3062,7 @@ "@loader_path/Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.11; - MARKETING_VERSION = 3.1.6; + MARKETING_VERSION = 3.1.7; OTHER_LDFLAGS = "$(CONFIG_FRAMEWORK_LDFLAGS)"; PRODUCT_BUNDLE_IDENTIFIER = org.libsdl.SDL3; PRODUCT_NAME = SDL3; @@ -3142,8 +3090,8 @@ ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; - DYLIB_COMPATIBILITY_VERSION = 107.0.0; - DYLIB_CURRENT_VERSION = 107.0.0; + DYLIB_COMPATIBILITY_VERSION = 108.0.0; + DYLIB_CURRENT_VERSION = 108.0.0; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; @@ -3175,7 +3123,7 @@ "@loader_path/Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.11; - MARKETING_VERSION = 3.1.6; + MARKETING_VERSION = 3.1.7; ONLY_ACTIVE_ARCH = YES; OTHER_LDFLAGS = "$(CONFIG_FRAMEWORK_LDFLAGS)"; PRODUCT_BUNDLE_IDENTIFIER = org.libsdl.SDL3; diff --git a/libs/SDL3/Xcode/SDL/pkg-support/SDL.info b/libs/SDL3/Xcode/SDL/pkg-support/SDL.info index 08b21172..38315815 100644 --- a/libs/SDL3/Xcode/SDL/pkg-support/SDL.info +++ b/libs/SDL3/Xcode/SDL/pkg-support/SDL.info @@ -1,4 +1,4 @@ -Title SDL 3.1.6 +Title SDL 3.1.7 Version 1 Description SDL Library for macOS (http://www.libsdl.org) DefaultLocation /Library/Frameworks diff --git a/libs/SDL3/Xcode/SDL/pkg-support/share/cmake/SDL3/SDL3Config.cmake b/libs/SDL3/Xcode/SDL/pkg-support/share/cmake/SDL3/SDL3Config.cmake index 9d29aae9..f1585814 100644 --- a/libs/SDL3/Xcode/SDL/pkg-support/share/cmake/SDL3/SDL3Config.cmake +++ b/libs/SDL3/Xcode/SDL/pkg-support/share/cmake/SDL3/SDL3Config.cmake @@ -112,7 +112,7 @@ if(NOT TARGET SDL3::SDL3-shared) set_target_properties(SDL3::SDL3-shared PROPERTIES FRAMEWORK "TRUE" - IMPORTED_LOCATION "${_sdl3_framework_path}/SDL3" + IMPORTED_LOCATION "${_sdl3_framework_path}" INTERFACE_LINK_LIBRARIES "SDL3::Headers" ) endif() diff --git a/libs/SDL3/android-project/app/src/main/java/org/libsdl/app/HIDDeviceBLESteamController.java b/libs/SDL3/android-project/app/src/main/java/org/libsdl/app/HIDDeviceBLESteamController.java index a7b85d0c..d2dc0d23 100644 --- a/libs/SDL3/android-project/app/src/main/java/org/libsdl/app/HIDDeviceBLESteamController.java +++ b/libs/SDL3/android-project/app/src/main/java/org/libsdl/app/HIDDeviceBLESteamController.java @@ -470,7 +470,7 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe // Only register controller with the native side once it has been fully configured if (!isRegistered()) { Log.v(TAG, "Registering Steam Controller with ID: " + getId()); - mManager.HIDDeviceConnected(getId(), getIdentifier(), getVendorId(), getProductId(), getSerialNumber(), getVersion(), getManufacturerName(), getProductName(), 0, 0, 0, 0); + mManager.HIDDeviceConnected(getId(), getIdentifier(), getVendorId(), getProductId(), getSerialNumber(), getVersion(), getManufacturerName(), getProductName(), 0, 0, 0, 0, true); setRegistered(); } } diff --git a/libs/SDL3/android-project/app/src/main/java/org/libsdl/app/HIDDeviceManager.java b/libs/SDL3/android-project/app/src/main/java/org/libsdl/app/HIDDeviceManager.java index fe791432..37d80ca0 100644 --- a/libs/SDL3/android-project/app/src/main/java/org/libsdl/app/HIDDeviceManager.java +++ b/libs/SDL3/android-project/app/src/main/java/org/libsdl/app/HIDDeviceManager.java @@ -355,7 +355,7 @@ public class HIDDeviceManager { HIDDeviceUSB device = new HIDDeviceUSB(this, usbDevice, interface_index); int id = device.getId(); mDevicesById.put(id, device); - HIDDeviceConnected(id, device.getIdentifier(), device.getVendorId(), device.getProductId(), device.getSerialNumber(), device.getVersion(), device.getManufacturerName(), device.getProductName(), usbInterface.getId(), usbInterface.getInterfaceClass(), usbInterface.getInterfaceSubclass(), usbInterface.getInterfaceProtocol()); + HIDDeviceConnected(id, device.getIdentifier(), device.getVendorId(), device.getProductId(), device.getSerialNumber(), device.getVersion(), device.getManufacturerName(), device.getProductName(), usbInterface.getId(), usbInterface.getInterfaceClass(), usbInterface.getInterfaceSubclass(), usbInterface.getInterfaceProtocol(), false); } } } @@ -679,7 +679,7 @@ public class HIDDeviceManager { private native void HIDDeviceRegisterCallback(); private native void HIDDeviceReleaseCallback(); - native void HIDDeviceConnected(int deviceID, String identifier, int vendorId, int productId, String serial_number, int release_number, String manufacturer_string, String product_string, int interface_number, int interface_class, int interface_subclass, int interface_protocol); + native void HIDDeviceConnected(int deviceID, String identifier, int vendorId, int productId, String serial_number, int release_number, String manufacturer_string, String product_string, int interface_number, int interface_class, int interface_subclass, int interface_protocol, boolean bBluetooth); native void HIDDeviceOpenPending(int deviceID); native void HIDDeviceOpenResult(int deviceID, boolean opened); native void HIDDeviceDisconnected(int deviceID); diff --git a/libs/SDL3/android-project/app/src/main/java/org/libsdl/app/HIDDeviceUSB.java b/libs/SDL3/android-project/app/src/main/java/org/libsdl/app/HIDDeviceUSB.java index 5b5d201f..27414386 100644 --- a/libs/SDL3/android-project/app/src/main/java/org/libsdl/app/HIDDeviceUSB.java +++ b/libs/SDL3/android-project/app/src/main/java/org/libsdl/app/HIDDeviceUSB.java @@ -154,6 +154,11 @@ class HIDDeviceUSB implements HIDDevice { @Override public int writeReport(byte[] report, boolean feature) { + if (mConnection == null) { + Log.w(TAG, "writeReport() called with no device connection"); + return -1; + } + if (feature) { int res = -1; int offset = 0; @@ -201,6 +206,11 @@ class HIDDeviceUSB implements HIDDevice { boolean skipped_report_id = false; byte report_number = report[0]; + if (mConnection == null) { + Log.w(TAG, "readReport() called with no device connection"); + return false; + } + if (report_number == 0x0) { /* Offset the return buffer by 1, so that the report ID will remain in byte 0. */ diff --git a/libs/SDL3/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java b/libs/SDL3/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java index 23681998..8666885a 100644 --- a/libs/SDL3/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java +++ b/libs/SDL3/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java @@ -60,7 +60,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 = 1; - private static final int SDL_MICRO_VERSION = 6; + private static final int SDL_MICRO_VERSION = 7; /* // Display InputType.SOURCE/CLASS of events and devices // diff --git a/libs/SDL3/build-scripts/SDL_migration.cocci b/libs/SDL3/build-scripts/SDL_migration.cocci index 7a36ec85..27a549fd 100644 --- a/libs/SDL3/build-scripts/SDL_migration.cocci +++ b/libs/SDL3/build-scripts/SDL_migration.cocci @@ -1081,7 +1081,7 @@ typedef SDL_GameControllerButton, SDL_GamepadButton; @@ @@ - SDL_GameControllerFromInstanceID -+ SDL_GetGamepadFromInstanceID ++ SDL_GetGamepadFromID (...) @@ @@ @@ -1318,7 +1318,7 @@ typedef SDL_GameControllerButton, SDL_GamepadButton; @@ @@ - SDL_JoystickFromInstanceID -+ SDL_GetJoystickFromInstanceID ++ SDL_GetJoystickFromID (...) @@ @@ @@ -1398,7 +1398,7 @@ typedef SDL_GameControllerButton, SDL_GamepadButton; @@ @@ - SDL_JoystickInstanceID -+ SDL_GetJoystickInstanceID ++ SDL_GetJoystickID (...) @@ @@ @@ -1807,7 +1807,7 @@ expression e2; @@ @@ - SDL_SensorFromInstanceID -+ SDL_GetSensorFromInstanceID ++ SDL_GetSensorFromID (...) @@ @@ @@ -1817,7 +1817,7 @@ expression e2; @@ @@ - SDL_SensorGetInstanceID -+ SDL_GetSensorInstanceID ++ SDL_GetSensorID (...) @@ @@ diff --git a/libs/SDL3/build-scripts/build-release.py b/libs/SDL3/build-scripts/build-release.py index 3295c1f8..2002039e 100644 --- a/libs/SDL3/build-scripts/build-release.py +++ b/libs/SDL3/build-scripts/build-release.py @@ -328,6 +328,10 @@ def configure_text(text: str, context: dict[str, str]) -> str: return text +def configure_text_list(text_list: list[str], context: dict[str, str]) -> list[str]: + return [configure_text(text=e, context=context) for e in text_list] + + class ArchiveFileTree: def __init__(self): self._tree: dict[str, NodeInArchive] = {} @@ -343,7 +347,7 @@ class ArchiveFileTree: added_files = dict() def calculate_symlink_target(s: NodeInArchive) -> str: - dest_dir = os.path.dirname(s.path) + dest_dir = os.path.dirname(s.arcpath) if dest_dir: dest_dir += "/" target = dest_dir + s.symtarget @@ -357,12 +361,15 @@ class ArchiveFileTree: # Add files in first pass for arcpath, node in self._tree.items(): + assert node is not None, f"{arcpath} -> node" if node.data is not None: archiver.add_file_data(arcpath=arc_join(archive_base, arcpath), data=node.data, time=node.time, mode=node.mode) - added_files[node.path] = node + assert node.arcpath is not None, f"{node=}" + added_files[node.arcpath] = node elif node.path is not None: archiver.add_file_path(arcpath=arc_join(archive_base, arcpath), path=node.path) - added_files[node.path] = node + assert node.arcpath is not None, f"{node=}" + added_files[node.arcpath] = node elif node.symtarget is not None: remaining_symlinks.add(node) elif node.directory: @@ -370,6 +377,8 @@ class ArchiveFileTree: else: raise ValueError(f"Invalid Archive Node: {repr(node)}") + assert None not in added_files + # Resolve symlinks in second pass: zipfile does not support symlinks, so add files to zip archive while True: if not remaining_symlinks: @@ -380,18 +389,18 @@ class ArchiveFileTree: symlink_files_for_zip = {} symlink_target_path = calculate_symlink_target(symlink) if symlink_target_path in added_files: - symlink_files_for_zip[symlink.path] = added_files[symlink_target_path] + symlink_files_for_zip[symlink.arcpath] = added_files[symlink_target_path] else: symlink_target_path_slash = symlink_target_path + "/" for added_file in added_files: if added_file.startswith(symlink_target_path_slash): - path_in_symlink = symlink.path + "/" + added_file.removeprefix(symlink_target_path_slash) + path_in_symlink = symlink.arcpath + "/" + added_file.removeprefix(symlink_target_path_slash) symlink_files_for_zip[path_in_symlink] = added_files[added_file] if symlink_files_for_zip: symlinks_this_time.add(symlink) extra_added_files.update(symlink_files_for_zip) files_for_zip = [{"arcpath": f"{archive_base}/{sym_path}", "data": sym_info.data, "mode": sym_info.mode} for sym_path, sym_info in symlink_files_for_zip.items()] - archiver.add_symlink(arcpath=f"{archive_base}/{symlink.path}", target=symlink.symtarget, time=symlink.time, files_for_zip=files_for_zip) + archiver.add_symlink(arcpath=f"{archive_base}/{symlink.arcpath}", target=symlink.symtarget, time=symlink.time, files_for_zip=files_for_zip) # if not symlinks_this_time: # logger.info("files added: %r", set(path for path in added_files.keys())) assert symlinks_this_time, f"No targets found for symlinks: {remaining_symlinks}" @@ -545,6 +554,7 @@ class Releaser: "PROJECT_VERSION": self.version, "PROJECT_COMMIT": self.commit, "PROJECT_REVISION": self.revision, + "PROJECT_ROOT": str(self.root), } if extra_context: ctx.update(extra_context) @@ -741,15 +751,20 @@ class Releaser: install_path = build_parent_dir / f"install-{triplet}" shutil.rmtree(install_path, ignore_errors=True) build_path.mkdir(parents=True, exist_ok=True) + context = self.get_context({ + "ARCH": arch, + "DEP_PREFIX": str(mingw_deps_path / triplet), + }) + extra_args = configure_text_list(text_list=self.release_info["mingw"]["autotools"]["args"], context=context) + with self.section_printer.group(f"Configuring MinGW {triplet} (autotools)"): - extra_args = [arg.replace("@DEP_PREFIX@", str(mingw_deps_path / triplet)) for arg in self.release_info["mingw"]["autotools"]["args"]] assert "@" not in " ".join(extra_args), f"@ should not be present in extra arguments ({extra_args})" self.executer.run([ self.root / "configure", f"--prefix={install_path}", - f"--includedir={install_path}/include", - f"--libdir={install_path}/lib", - f"--bindir={install_path}/bin", + f"--includedir=${{prefix}}/include", + f"--libdir=${{prefix}}/lib", + f"--bindir=${{prefix}}/bin", f"--host={triplet}", f"--build=x86_64-none-linux-gnu", ] + extra_args, cwd=build_path, env=new_env) @@ -757,7 +772,13 @@ class Releaser: self.executer.run(["make", f"-j{self.cpu_count}"], cwd=build_path, env=new_env) with self.section_printer.group(f"Install MinGW {triplet} (autotools)"): self.executer.run(["make", "install"], cwd=build_path, env=new_env) - archive_file_tree.add_directory_tree(arc_dir=arc_join(arc_root, triplet), path=install_path) + archive_file_tree.add_directory_tree(arc_dir=arc_join(arc_root, triplet), path=install_path, time=self.arc_time) + + print("Recording arch-dependent extra files for MinGW development archive ...") + extra_context = { + "TRIPLET": ARCH_TO_TRIPLET[arch], + } + archive_file_tree.add_file_mapping(arc_dir=arc_root, file_mapping=self.release_info["mingw"]["autotools"].get("files", {}), file_mapping_root=self.root, context=self.get_context(extra_context=extra_context), time=self.arc_time) if "cmake" in self.release_info["mingw"]: assert self.release_info["mingw"]["cmake"]["shared-static"] in ("args", "both") @@ -770,6 +791,12 @@ class Releaser: assert arch not in mingw_archs mingw_archs.add(arch) + context = self.get_context({ + "ARCH": arch, + "DEP_PREFIX": str(mingw_deps_path / triplet), + }) + extra_args = configure_text_list(text_list=self.release_info["mingw"]["cmake"]["args"], context=context) + build_path = build_parent_dir / f"build-{triplet}" install_path = build_parent_dir / f"install-{triplet}" shutil.rmtree(install_path, ignore_errors=True) @@ -780,7 +807,6 @@ class Releaser: args_for_shared_static = (["-DBUILD_SHARED_LIBS=ON"], ["-DBUILD_SHARED_LIBS=OFF"]) for arg_for_shared_static in args_for_shared_static: with self.section_printer.group(f"Configuring MinGW {triplet} (CMake)"): - extra_args = [arg.replace("@DEP_PREFIX@", str(mingw_deps_path / triplet)) for arg in self.release_info["mingw"]["cmake"]["args"]] assert "@" not in " ".join(extra_args), f"@ should not be present in extra arguments ({extra_args})" self.executer.run([ f"cmake", @@ -803,6 +829,13 @@ class Releaser: self.executer.run(["cmake", "--install", str(build_path)], cwd=build_path, env=new_env) archive_file_tree.add_directory_tree(arc_dir=arc_join(arc_root, triplet), path=install_path, time=self.arc_time) + print("Recording arch-dependent extra files for MinGW development archive ...") + extra_context = { + "TRIPLET": ARCH_TO_TRIPLET[arch], + } + archive_file_tree.add_file_mapping(arc_dir=arc_root, file_mapping=self.release_info["mingw"]["cmake"].get("files", {}), file_mapping_root=self.root, context=self.get_context(extra_context=extra_context), time=self.arc_time) + print("... done") + print("Recording extra files for MinGW development archive ...") archive_file_tree.add_file_mapping(arc_dir=arc_root, file_mapping=self.release_info["mingw"].get("files", {}), file_mapping_root=self.root, context=self.get_context(), time=self.arc_time) print("... done") @@ -1231,6 +1264,10 @@ class Releaser: zip_path = self.dist_path / f"{self.project}-devel-{self.version}-VC.zip" arc_root = f"{self.project}-{self.version}" + def copy_files_devel(ctx): + archive_file_tree.add_file_mapping(arc_dir=arc_root, file_mapping=self.release_info["msvc"]["files-devel"], file_mapping_root=self.root, context=ctx, time=self.arc_time) + + logger.info("Collecting files...") archive_file_tree = ArchiveFileTree() if "msbuild" in self.release_info["msvc"]: @@ -1238,12 +1275,13 @@ class Releaser: arch_platform = self._arch_to_vs_platform(arch=arch) platform_context = self.get_context(arch_platform.extra_context()) archive_file_tree.add_file_mapping(arc_dir=arc_root, file_mapping=self.release_info["msvc"]["msbuild"]["files-devel"], file_mapping_root=self.root, context=platform_context, time=self.arc_time) + copy_files_devel(ctx=platform_context) if "cmake" in self.release_info["msvc"]: for arch in self.release_info["msvc"]["cmake"]["archs"]: arch_platform = self._arch_to_vs_platform(arch=arch) platform_context = self.get_context(arch_platform.extra_context()) archive_file_tree.add_file_mapping(arc_dir=arc_root, file_mapping=self.release_info["msvc"]["cmake"]["files-devel"], file_mapping_root=self._arch_platform_to_install_path(arch_platform), context=platform_context, time=self.arc_time) - archive_file_tree.add_file_mapping(arc_dir=arc_root, file_mapping=self.release_info["msvc"]["files-devel"], file_mapping_root=self.root, context=self.get_context(), time=self.arc_time) + copy_files_devel(ctx=platform_context) with Archiver(zip_path=zip_path) as archiver: archive_file_tree.add_to_archiver(archive_base="", archiver=archiver) diff --git a/libs/SDL3/build-scripts/build-web-examples.pl b/libs/SDL3/build-scripts/build-web-examples.pl index 0053295e..211682e1 100644 --- a/libs/SDL3/build-scripts/build-web-examples.pl +++ b/libs/SDL3/build-scripts/build-web-examples.pl @@ -71,6 +71,62 @@ sub build_latest { } } +sub get_category_description { + my $category = shift; + my $retval = ucfirst($category); + + if (open(my $fh, '<', "$examples_dir/$category/description.txt")) { + $retval = <$fh>; + chomp($retval); + close($fh); + } + + return $retval; +} + +sub get_categories { + my @categories = (); + + if (open(my $fh, '<', "$examples_dir/categories.txt")) { + while (<$fh>) { + chomp; + s/\A\s+//; + s/\s+\Z//; + next if $_ eq ''; + next if /\A\#/; + push @categories, $_; + } + close($fh); + } else { + opendir(my $dh, $examples_dir) or die("Couldn't opendir '$examples_dir': $!\n"); + foreach my $dir (sort readdir $dh) { + next if ($dir eq '.') || ($dir eq '..'); # obviously skip current and parent entries. + next if not -d "$examples_dir/$dir"; # only care about subdirectories. + push @categories, $dir; + } + closedir($dh); + } + + return @categories; +} + +sub get_examples_for_category { + my $category = shift; + + my @examples = (); + + opendir(my $dh, "$examples_dir/$category") or die("Couldn't opendir '$examples_dir/$category': $!\n"); + foreach my $dir (sort readdir $dh) { + next if ($dir eq '.') || ($dir eq '..'); # obviously skip current and parent entries. + next if not -d "$examples_dir/$category/$dir"; # only care about subdirectories. + + push @examples, $dir; + } + closedir($dh); + + return @examples; +} + sub handle_example_dir { my $category = shift; my $example = shift; @@ -99,25 +155,43 @@ sub handle_example_dir { $basefname = "$category-$basefname"; my $jsfname = "$basefname.js"; my $wasmfname = "$basefname.wasm"; + my $thumbnailfname = 'thumbnail.png'; + my $onmouseoverfname = 'onmouseover.webp'; my $jssrc = "$compile_dir/examples/$jsfname"; my $wasmsrc = "$compile_dir/examples/$wasmfname"; + my $thumbnailsrc = "$examples_dir/$category/$example/$thumbnailfname"; + my $onmouseoversrc = "$examples_dir/$category/$example/$onmouseoverfname"; my $jsdst = "$dst/$jsfname"; my $wasmdst = "$dst/$wasmfname"; + my $thumbnaildst = "$dst/$thumbnailfname"; + my $onmouseoverdst = "$dst/$onmouseoverfname"; my $description = ''; + my $has_paragraph = 0; if (open(my $readmetxth, '<', "$examples_dir/$category/$example/README.txt")) { while (<$readmetxth>) { chomp; s/\A\s+//; s/\s+\Z//; - $description .= "$_
"; + if (($_ eq '') && ($description ne '')) { + $has_paragraph = 1; + } else { + if ($has_paragraph) { + $description .= "\n
\n
\n"; + $has_paragraph = 0; + } + $description .= "$_ "; + } } close($readmetxth); + $description =~ s/\s+\Z//; } do_mkdir($dst); do_copy($jssrc, $jsdst); do_copy($wasmsrc, $wasmdst); + do_copy($thumbnailsrc, $thumbnaildst) if ( -f $thumbnailsrc ); + do_copy($onmouseoversrc, $onmouseoverdst) if ( -f $onmouseoversrc ); my $highlight_cmd = "highlight '--outdir=$dst' --style-outfile=highlight.css --fragment --enclose-pre --stdout --syntax=c '--plug-in=$examples_dir/highlight-plugin.lua'"; print("$highlight_cmd\n"); @@ -146,16 +220,27 @@ sub handle_example_dir { waitpid($pid, 0); + my $other_examples_html = "
    "; + foreach my $example (get_examples_for_category($category)) { + $other_examples_html .= "
  • $category/$example
  • "; + } + $other_examples_html .= "
"; + + my $category_description = get_category_description($category); + my $preview_image = get_example_thumbnail($project, $category, $example); my $html = ''; open my $htmltemplate, '<', "$examples_dir/template.html" or die("Couldn't open '$examples_dir/template.html': $!\n"); while (<$htmltemplate>) { s/\@project_name\@/$project/g; s/\@category_name\@/$category/g; + s/\@category_description\@/$category_description/g; s/\@example_name\@/$example/g; s/\@javascript_file\@/$jsfname/g; s/\@htmlified_source_code\@/$htmlified_source_code/g; s/\@description\@/$description/g; + s/\@preview_image\@/$preview_image/g; + s/\@other_examples_html\@/$other_examples_html/g; $html .= $_; } close($htmltemplate); @@ -165,11 +250,61 @@ sub handle_example_dir { close($htmloutput); } +sub get_example_thumbnail { + my $project = shift; + my $category = shift; + my $example = shift; + + if ( -f "$examples_dir/$category/$example/thumbnail.png" ) { + return "/$project/$category/$example/thumbnail.png"; + } elsif ( -f "$examples_dir/$category/thumbnail.png" ) { + return "/$project/$category/thumbnail.png"; + } + + return "/$project/thumbnail.png"; +} + +sub generate_example_thumbnail { + my $project = shift; + my $category = shift; + my $example = shift; + + my $example_no_num = "$example"; + $example_no_num =~ s/\A\d+\-//; + + my $example_image_url = get_example_thumbnail($project, $category, $example); + + my $example_mouseover_html = ''; + if ( -f "$examples_dir/$category/$example/onmouseover.webp" ) { + $example_mouseover_html = "onmouseover=\"this.src='/$project/$category/$example/onmouseover.webp'\" onmouseout=\"this.src='$example_image_url';\""; + } elsif ( -f "$examples_dir/$category/onmouseover.webp" ) { + $example_mouseover_html = "onmouseover=\"this.src='/$project/$category/onmouseover.webp'\" onmouseout=\"this.src='$example_image_url';\""; + } + + return " + +
+ +
$example_no_num
+
+
" + ; +} + +sub generate_example_thumbnails_for_category { + my $project = shift; + my $category = shift; + my @examples = get_examples_for_category($category); + my $retval = ''; + foreach my $example (@examples) { + $retval .= generate_example_thumbnail($project, $category, $example); + } + return $retval; +} + sub handle_category_dir { my $category = shift; - # !!! FIXME: this needs to generate a preview page for all the examples things in the category. - print("Category $category ...\n"); do_mkdir("$output_dir/$category"); @@ -183,6 +318,36 @@ sub handle_category_dir { } closedir($dh); + + my $examples_list_html = generate_example_thumbnails_for_category($project, $category); + + my $dst = "$output_dir/$category"; + + do_copy("$examples_dir/$category/thumbnail.png", "$dst/thumbnail.png") if ( -f "$examples_dir/$category/thumbnail.png" ); + do_copy("$examples_dir/$category/onmouseover.webp", "$dst/onmouseover.webp") if ( -f "$examples_dir/$category/onmouseover.webp" ); + + my $category_description = get_category_description($category); + my $preview_image = "/$project/thumbnail.png"; + if ( -f "$examples_dir/$category/thumbnail.png" ) { + $preview_image = "/$project/$category/thumbnail.png"; + } + + # write category page + my $html = ''; + open my $htmltemplate, '<', "$examples_dir/template-category.html" or die("Couldn't open '$examples_dir/template-category.html': $!\n"); + while (<$htmltemplate>) { + s/\@project_name\@/$project/g; + s/\@category_name\@/$category/g; + s/\@category_description\@/$category_description/g; + s/\@examples_list_html\@/$examples_list_html/g; + s/\@preview_image\@/$preview_image/g; + $html .= $_; + } + close($htmltemplate); + + open my $htmloutput, '>', "$dst/index.html" or die("Couldn't open '$dst/index.html': $!\n"); + print $htmloutput $html; + close($htmloutput); } @@ -210,6 +375,9 @@ do_mkdir($output_dir); build_latest(); +do_copy("$examples_dir/template.css", "$output_dir/examples.css"); +do_copy("$examples_dir/template-placeholder.png", "$output_dir/thumbnail.png"); + opendir(my $dh, $examples_dir) or die("Couldn't opendir '$examples_dir': $!\n"); while (readdir($dh)) { @@ -221,6 +389,32 @@ while (readdir($dh)) { closedir($dh); +# write homepage +my $homepage_list_html = ""; +foreach my $category (get_categories()) { + my $category_description = get_category_description($category); + $homepage_list_html .= "

$category_description

"; + $homepage_list_html .= "
"; + $homepage_list_html .= generate_example_thumbnails_for_category($project, $category); + $homepage_list_html .= "
"; +} + +my $preview_image = "/$project/thumbnail.png"; + +my $dst = "$output_dir/"; +my $html = ''; +open my $htmltemplate, '<', "$examples_dir/template-homepage.html" or die("Couldn't open '$examples_dir/template-category.html': $!\n"); +while (<$htmltemplate>) { + s/\@project_name\@/$project/g; + s/\@homepage_list_html\@/$homepage_list_html/g; + s/\@preview_image\@/$preview_image/g; + $html .= $_; +} +close($htmltemplate); + +open my $htmloutput, '>', "$dst/index.html" or die("Couldn't open '$dst/index.html': $!\n"); +print $htmloutput $html; +close($htmloutput); + print("All examples built successfully!\n"); exit(0); # success! - diff --git a/libs/SDL3/build-scripts/create-release.py b/libs/SDL3/build-scripts/create-release.py index 22144499..14916fa8 100644 --- a/libs/SDL3/build-scripts/create-release.py +++ b/libs/SDL3/build-scripts/create-release.py @@ -23,18 +23,20 @@ def determine_remote() -> str: def main(): default_remote = determine_remote() - current_commit = subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=ROOT, text=True).strip() - parser = argparse.ArgumentParser(allow_abbrev=False) parser.add_argument("--ref", required=True, help=f"Name of branch or tag containing release.yml") parser.add_argument("--remote", "-R", default=default_remote, help=f"Remote repo (default={default_remote})") - parser.add_argument("--commit", default=current_commit, help=f"Commit (default={current_commit})") + parser.add_argument("--commit", help=f"Input 'commit' of release.yml (default is the hash of the ref)") args = parser.parse_args() + if args.commit is None: + args.commit = subprocess.check_output(["git", "rev-parse", args.ref], cwd=ROOT, text=True).strip() + print(f"Running release.yml workflow:") - print(f" commit = {args.commit}") print(f" remote = {args.remote}") + print(f" ref = {args.ref}") + print(f" commit = {args.commit}") subprocess.check_call(["gh", "-R", args.remote, "workflow", "run", "release.yml", "--ref", args.ref, "-f", f"commit={args.commit}"], cwd=ROOT) diff --git a/libs/SDL3/build-scripts/fnsince.pl b/libs/SDL3/build-scripts/fnsince.pl index 20cbb30b..8e8ec48e 100644 --- a/libs/SDL3/build-scripts/fnsince.pl +++ b/libs/SDL3/build-scripts/fnsince.pl @@ -90,11 +90,15 @@ foreach my $release (@releases) { my $tag = $fulltags{$release}; my $blobname = "$tag:src/dynapi/SDL_dynapi_overrides.h"; - if ($release =~ /\A3\.[01]\.\d+/) { # make everything up to the first SDL3 prerelease look like 3.1.3 (ABI lock version). + if ($release =~ /\A3\.(0\.\d+|1\.[0123])/) { # make everything up to the first SDL3 prerelease look like 3.1.3 (ABI lock version). $release = '3.1.3'; } - else { $release = '3.2.0'; } # !!! FIXME: REMOVE ME WHEN 3.2.0 SHIPS! + # !!! FIXME: REMOVE ME WHEN 3.2.0 SHIPS! + elsif (not $release =~ /\A3\.1\.\d+/) { # a couple of releases after the initial 3.1.3, let them through. + $release = '3.2.0'; + } + # !!! FIXME: REMOVE ME WHEN 3.2.0 SHIPS! open(PIPEFH, '-|', "git show '$blobname'") or die "Failed to read git blob '$blobname': $!\n"; while () { diff --git a/libs/SDL3/build-scripts/pkg-support/android/cmake/SDL3Config.cmake b/libs/SDL3/build-scripts/pkg-support/android/cmake/SDL3Config.cmake index a41275dc..5b92a456 100644 --- a/libs/SDL3/build-scripts/pkg-support/android/cmake/SDL3Config.cmake +++ b/libs/SDL3/build-scripts/pkg-support/android/cmake/SDL3Config.cmake @@ -1,7 +1,7 @@ # SDL CMake configuration file: # This file is meant to be placed in lib/cmake/SDL3 subfolder of a reconstructed Android SDL3 SDK -cmake_minimum_required(VERSION 3.0...3.5) +cmake_minimum_required(VERSION 3.0...3.28) include(FeatureSummary) set_package_properties(SDL3 PROPERTIES diff --git a/libs/SDL3/build-scripts/pkg-support/msvc/cmake/SDL3Config.cmake.in b/libs/SDL3/build-scripts/pkg-support/msvc/cmake/SDL3Config.cmake.in index 5463b498..6387376e 100644 --- a/libs/SDL3/build-scripts/pkg-support/msvc/cmake/SDL3Config.cmake.in +++ b/libs/SDL3/build-scripts/pkg-support/msvc/cmake/SDL3Config.cmake.in @@ -1,7 +1,7 @@ # @<@PROJECT_NAME@>@ CMake configuration file: # This file is meant to be placed in a cmake subfolder of @<@PROJECT_NAME@>@-devel-@<@PROJECT_VERSION@>@-VC.zip -cmake_minimum_required(VERSION 3.0...3.5) +cmake_minimum_required(VERSION 3.0...3.28) include(FeatureSummary) set_package_properties(SDL3 PROPERTIES diff --git a/libs/SDL3/build-scripts/rename_macros.py b/libs/SDL3/build-scripts/rename_macros.py index ba386cd4..978120cc 100644 --- a/libs/SDL3/build-scripts/rename_macros.py +++ b/libs/SDL3/build-scripts/rename_macros.py @@ -113,7 +113,6 @@ RENAMED_MACROS = { "__LINUX__": "SDL_PLATFORM_LINUX", "__OS2__": "SDL_PLATFORM_OS2", # "__ANDROID__": "SDL_PLATFORM_ANDROID, - "__NGAGE__": "SDL_PLATFORM_NGAGE", "__APPLE__": "SDL_PLATFORM_APPLE", "__TVOS__": "SDL_PLATFORM_TVOS", "__IPHONEOS__": "SDL_PLATFORM_IOS", diff --git a/libs/SDL3/build-scripts/wikiheaders.pl b/libs/SDL3/build-scripts/wikiheaders.pl index 510ee397..2ea852f7 100644 --- a/libs/SDL3/build-scripts/wikiheaders.pl +++ b/libs/SDL3/build-scripts/wikiheaders.pl @@ -51,6 +51,12 @@ my $wikipreamble = undef; my $wikiheaderfiletext = 'Defined in %fname%'; my $manpageheaderfiletext = 'Defined in %fname%'; my $headercategoryeval = undef; +my $quickrefenabled = 0; +my @quickrefcategoryorder; +my $quickreftitle = undef; +my $quickrefurl = undef; +my $quickrefdesc = undef; +my $quickrefmacroregex = undef; my $changeformat = undef; my $manpath = undef; my $gitrev = undef; @@ -122,6 +128,12 @@ if (defined $optionsfname) { $wikiheaderfiletext = $val, next if $key eq 'wikiheaderfiletext'; $manpageheaderfiletext = $val, next if $key eq 'manpageheaderfiletext'; $headercategoryeval = $val, next if $key eq 'headercategoryeval'; + $quickrefenabled = int($val), next if $key eq 'quickrefenabled'; + @quickrefcategoryorder = split(/,/, $val), next if $key eq 'quickrefcategoryorder'; + $quickreftitle = $val, next if $key eq 'quickreftitle'; + $quickrefurl = $val, next if $key eq 'quickrefurl'; + $quickrefdesc = $val, next if $key eq 'quickrefdesc'; + $quickrefmacroregex = $val, next if $key eq 'quickrefmacroregex'; } } close(OPTIONS); @@ -647,6 +659,7 @@ my %headersymsrettype = (); # $headersymsrettype{"SDL_OpenAudio"} -> string of C my %wikitypes = (); # contains string of wiki page extension, like $wikitypes{"SDL_OpenAudio"} == 'mediawiki' my %wikisyms = (); # contains references to hash of strings, each string being the full contents of a section of a wiki page, like $wikisyms{"SDL_OpenAudio"}{"Remarks"}. my %wikisectionorder = (); # contains references to array, each array item being a key to a wikipage section in the correct order, like $wikisectionorder{"SDL_OpenAudio"}[2] == 'Remarks' +my %quickreffuncorder = (); # contains references to array, each array item being a key to a category with functions in the order they appear in the headers, like $quickreffuncorder{"Audio"}[0] == 'SDL_GetNumAudioDrivers' my %referenceonly = (); # $referenceonly{"Y"} -> symbol name that this symbol is bound to. This makes wiki pages that say "See X" where "X" is a typedef and "Y" is a define attached to it. These pages are generated in the wiki only and do not bridge to the headers or manpages. @@ -720,6 +733,279 @@ sub sanitize_c_typename { return $str; } +my %big_ascii = ( + 'A' => [ "\x{20}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}\x{20}", "\x{2588}\x{2588}\x{2554}\x{2550}\x{2550}\x{2588}\x{2588}\x{2557}", "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2551}", "\x{2588}\x{2588}\x{2554}\x{2550}\x{2550}\x{2588}\x{2588}\x{2551}", "\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{2588}\x{2588}\x{2551}", "\x{255A}\x{2550}\x{255D}\x{20}\x{20}\x{255A}\x{2550}\x{255D}" ], + 'B' => [ "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}\x{20}", "\x{2588}\x{2588}\x{2554}\x{2550}\x{2550}\x{2588}\x{2588}\x{2557}", "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2554}\x{255D}", "\x{2588}\x{2588}\x{2554}\x{2550}\x{2550}\x{2588}\x{2588}\x{2557}", "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2554}\x{255D}", "\x{255A}\x{2550}\x{2550}\x{2550}\x{2550}\x{2550}\x{255D}\x{20}" ], + 'C' => [ "\x{20}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}", "\x{2588}\x{2588}\x{2554}\x{2550}\x{2550}\x{2550}\x{2550}\x{255D}", "\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{20}\x{20}\x{20}", "\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{20}\x{20}\x{20}", "\x{255A}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}", "\x{20}\x{255A}\x{2550}\x{2550}\x{2550}\x{2550}\x{2550}\x{255D}" ], + 'D' => [ "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}\x{20}", "\x{2588}\x{2588}\x{2554}\x{2550}\x{2550}\x{2588}\x{2588}\x{2557}", "\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{2588}\x{2588}\x{2551}", "\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{2588}\x{2588}\x{2551}", "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2554}\x{255D}", "\x{255A}\x{2550}\x{2550}\x{2550}\x{2550}\x{2550}\x{255D}\x{20}" ], + 'E' => [ "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}", "\x{2588}\x{2588}\x{2554}\x{2550}\x{2550}\x{2550}\x{2550}\x{255D}", "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}\x{20}\x{20}", "\x{2588}\x{2588}\x{2554}\x{2550}\x{2550}\x{255D}\x{20}\x{20}", "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}", "\x{255A}\x{2550}\x{2550}\x{2550}\x{2550}\x{2550}\x{2550}\x{255D}" ], + 'F' => [ "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}", "\x{2588}\x{2588}\x{2554}\x{2550}\x{2550}\x{2550}\x{2550}\x{255D}", "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}\x{20}\x{20}", "\x{2588}\x{2588}\x{2554}\x{2550}\x{2550}\x{255D}\x{20}\x{20}", "\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{20}\x{20}\x{20}", "\x{255A}\x{2550}\x{255D}\x{20}\x{20}\x{20}\x{20}\x{20}" ], + 'G' => [ "\x{20}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}\x{20}", "\x{2588}\x{2588}\x{2554}\x{2550}\x{2550}\x{2550}\x{2550}\x{255D}\x{20}", "\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{2588}\x{2588}\x{2588}\x{2557}", "\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2551}", "\x{255A}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2554}\x{255D}", "\x{20}\x{255A}\x{2550}\x{2550}\x{2550}\x{2550}\x{2550}\x{255D}\x{20}" ], + 'H' => [ "\x{2588}\x{2588}\x{2557}\x{20}\x{20}\x{2588}\x{2588}\x{2557}", "\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{2588}\x{2588}\x{2551}", "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2551}", "\x{2588}\x{2588}\x{2554}\x{2550}\x{2550}\x{2588}\x{2588}\x{2551}", "\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{2588}\x{2588}\x{2551}", "\x{255A}\x{2550}\x{255D}\x{20}\x{20}\x{255A}\x{2550}\x{255D}" ], + 'I' => [ "\x{2588}\x{2588}\x{2557}", "\x{2588}\x{2588}\x{2551}", "\x{2588}\x{2588}\x{2551}", "\x{2588}\x{2588}\x{2551}", "\x{2588}\x{2588}\x{2551}", "\x{255A}\x{2550}\x{255D}" ], + 'J' => [ "\x{20}\x{20}\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2557}", "\x{20}\x{20}\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2551}", "\x{20}\x{20}\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2551}", "\x{2588}\x{2588}\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2551}", "\x{255A}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2554}\x{255D}", "\x{20}\x{255A}\x{2550}\x{2550}\x{2550}\x{2550}\x{255D}\x{20}" ], + 'K' => [ "\x{2588}\x{2588}\x{2557}\x{20}\x{20}\x{2588}\x{2588}\x{2557}", "\x{2588}\x{2588}\x{2551}\x{20}\x{2588}\x{2588}\x{2554}\x{255D}", "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2554}\x{255D}\x{20}", "\x{2588}\x{2588}\x{2554}\x{2550}\x{2588}\x{2588}\x{2557}\x{20}", "\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{2588}\x{2588}\x{2557}", "\x{255A}\x{2550}\x{255D}\x{20}\x{20}\x{255A}\x{2550}\x{255D}" ], + 'L' => [ "\x{2588}\x{2588}\x{2557}\x{20}\x{20}\x{20}\x{20}\x{20}", "\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{20}\x{20}\x{20}", "\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{20}\x{20}\x{20}", "\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{20}\x{20}\x{20}", "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}", "\x{255A}\x{2550}\x{2550}\x{2550}\x{2550}\x{2550}\x{2550}\x{255D}" ], + 'M' => [ "\x{2588}\x{2588}\x{2588}\x{2557}\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2588}\x{2557}", "\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}\x{20}\x{2588}\x{2588}\x{2588}\x{2588}\x{2551}", "\x{2588}\x{2588}\x{2554}\x{2588}\x{2588}\x{2588}\x{2588}\x{2554}\x{2588}\x{2588}\x{2551}", "\x{2588}\x{2588}\x{2551}\x{255A}\x{2588}\x{2588}\x{2554}\x{255D}\x{2588}\x{2588}\x{2551}", "\x{2588}\x{2588}\x{2551}\x{20}\x{255A}\x{2550}\x{255D}\x{20}\x{2588}\x{2588}\x{2551}", "\x{255A}\x{2550}\x{255D}\x{20}\x{20}\x{20}\x{20}\x{20}\x{255A}\x{2550}\x{255D}" ], + 'N' => [ "\x{2588}\x{2588}\x{2588}\x{2557}\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2557}", "\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}\x{20}\x{20}\x{2588}\x{2588}\x{2551}", "\x{2588}\x{2588}\x{2554}\x{2588}\x{2588}\x{2557}\x{20}\x{2588}\x{2588}\x{2551}", "\x{2588}\x{2588}\x{2551}\x{255A}\x{2588}\x{2588}\x{2557}\x{2588}\x{2588}\x{2551}", "\x{2588}\x{2588}\x{2551}\x{20}\x{255A}\x{2588}\x{2588}\x{2588}\x{2588}\x{2551}", "\x{255A}\x{2550}\x{255D}\x{20}\x{20}\x{255A}\x{2550}\x{2550}\x{2550}\x{255D}" ], + 'O' => [ "\x{20}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}\x{20}", "\x{2588}\x{2588}\x{2554}\x{2550}\x{2550}\x{2550}\x{2588}\x{2588}\x{2557}", "\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2551}", "\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2551}", "\x{255A}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2554}\x{255D}", "\x{20}\x{255A}\x{2550}\x{2550}\x{2550}\x{2550}\x{2550}\x{255D}\x{20}" ], + 'P' => [ "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}\x{20}", "\x{2588}\x{2588}\x{2554}\x{2550}\x{2550}\x{2588}\x{2588}\x{2557}", "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2554}\x{255D}", "\x{2588}\x{2588}\x{2554}\x{2550}\x{2550}\x{2550}\x{255D}\x{20}", "\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{20}\x{20}\x{20}", "\x{255A}\x{2550}\x{255D}\x{20}\x{20}\x{20}\x{20}\x{20}" ], + 'Q' => [ "\x{20}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}", "\x{2588}\x{2588}\x{2554}\x{2550}\x{2550}\x{2550}\x{2588}\x{2588}\x{2557}", "\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2551}", "\x{2588}\x{2588}\x{2551}\x{2584}\x{2584}\x{20}\x{2588}\x{2588}\x{2551}", "\x{255A}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2554}\x{255D}", "\x{20}\x{255A}\x{2550}\x{2550}\x{2580}\x{2580}\x{2550}\x{255D}\x{20}" ], + 'R' => [ "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}\x{20}", "\x{2588}\x{2588}\x{2554}\x{2550}\x{2550}\x{2588}\x{2588}\x{2557}", "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2554}\x{255D}", "\x{2588}\x{2588}\x{2554}\x{2550}\x{2550}\x{2588}\x{2588}\x{2557}", "\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{2588}\x{2588}\x{2551}", "\x{255A}\x{2550}\x{255D}\x{20}\x{20}\x{255A}\x{2550}\x{255D}" ], + 'S' => [ "\x{20}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}", "\x{2588}\x{2588}\x{2554}\x{2550}\x{2550}\x{2550}\x{2550}\x{255D}", "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}", "\x{255A}\x{2550}\x{2550}\x{2550}\x{2550}\x{2588}\x{2588}\x{2551}", "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2554}\x{255D}", "\x{255A}\x{2550}\x{2550}\x{2550}\x{2550}\x{2550}\x{255D}\x{20}" ], + 'T' => [ "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}", "\x{255A}\x{2550}\x{2550}\x{2588}\x{2588}\x{2554}\x{2550}\x{2550}\x{255D}", "\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{20}", "\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{20}", "\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{20}", "\x{20}\x{20}\x{20}\x{255A}\x{2550}\x{255D}\x{20}\x{20}\x{20}" ], + 'U' => [ "\x{2588}\x{2588}\x{2557}\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2557}", "\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2551}", "\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2551}", "\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2551}", "\x{255A}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2554}\x{255D}", "\x{20}\x{255A}\x{2550}\x{2550}\x{2550}\x{2550}\x{2550}\x{255D}\x{20}" ], + 'V' => [ "\x{2588}\x{2588}\x{2557}\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2557}", "\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2551}", "\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2551}", "\x{255A}\x{2588}\x{2588}\x{2557}\x{20}\x{2588}\x{2588}\x{2554}\x{255D}", "\x{20}\x{255A}\x{2588}\x{2588}\x{2588}\x{2588}\x{2554}\x{255D}\x{20}", "\x{20}\x{20}\x{255A}\x{2550}\x{2550}\x{2550}\x{255D}\x{20}\x{20}" ], + 'W' => [ "\x{2588}\x{2588}\x{2557}\x{20}\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2557}", "\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2551}", "\x{2588}\x{2588}\x{2551}\x{20}\x{2588}\x{2557}\x{20}\x{2588}\x{2588}\x{2551}", "\x{2588}\x{2588}\x{2551}\x{2588}\x{2588}\x{2588}\x{2557}\x{2588}\x{2588}\x{2551}", "\x{255A}\x{2588}\x{2588}\x{2588}\x{2554}\x{2588}\x{2588}\x{2588}\x{2554}\x{255D}", "\x{20}\x{255A}\x{2550}\x{2550}\x{255D}\x{255A}\x{2550}\x{2550}\x{255D}\x{20}" ], + 'X' => [ "\x{2588}\x{2588}\x{2557}\x{20}\x{20}\x{2588}\x{2588}\x{2557}", "\x{255A}\x{2588}\x{2588}\x{2557}\x{2588}\x{2588}\x{2554}\x{255D}", "\x{20}\x{255A}\x{2588}\x{2588}\x{2588}\x{2554}\x{255D}\x{20}", "\x{20}\x{2588}\x{2588}\x{2554}\x{2588}\x{2588}\x{2557}\x{20}", "\x{2588}\x{2588}\x{2554}\x{255D}\x{20}\x{2588}\x{2588}\x{2557}", "\x{255A}\x{2550}\x{255D}\x{20}\x{20}\x{255A}\x{2550}\x{255D}" ], + 'Y' => [ "\x{2588}\x{2588}\x{2557}\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2557}", "\x{255A}\x{2588}\x{2588}\x{2557}\x{20}\x{2588}\x{2588}\x{2554}\x{255D}", "\x{20}\x{255A}\x{2588}\x{2588}\x{2588}\x{2588}\x{2554}\x{255D}\x{20}", "\x{20}\x{20}\x{255A}\x{2588}\x{2588}\x{2554}\x{255D}\x{20}\x{20}", "\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{20}", "\x{20}\x{20}\x{20}\x{255A}\x{2550}\x{255D}\x{20}\x{20}\x{20}" ], + 'Z' => [ "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}", "\x{255A}\x{2550}\x{2550}\x{2588}\x{2588}\x{2588}\x{2554}\x{255D}", "\x{20}\x{20}\x{2588}\x{2588}\x{2588}\x{2554}\x{255D}\x{20}", "\x{20}\x{2588}\x{2588}\x{2588}\x{2554}\x{255D}\x{20}\x{20}", "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}", "\x{255A}\x{2550}\x{2550}\x{2550}\x{2550}\x{2550}\x{2550}\x{255D}" ], + ' ' => [ "\x{20}\x{20}\x{20}\x{20}", "\x{20}\x{20}\x{20}\x{20}", "\x{20}\x{20}\x{20}\x{20}", "\x{20}\x{20}\x{20}\x{20}", "\x{20}\x{20}\x{20}\x{20}", "\x{20}\x{20}\x{20}\x{20}" ], + '.' => [ "\x{20}\x{20}\x{20}", "\x{20}\x{20}\x{20}", "\x{20}\x{20}\x{20}", "\x{20}\x{20}\x{20}", "\x{2588}\x{2588}\x{2557}", "\x{255A}\x{2550}\x{255D}" ], + ',' => [ "\x{20}\x{20}\x{20}", "\x{20}\x{20}\x{20}", "\x{20}\x{20}\x{20}", "\x{20}\x{20}\x{20}", "\x{2584}\x{2588}\x{2557}", "\x{255A}\x{2550}\x{255D}" ], + '/' => [ "\x{20}\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2557}", "\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2554}\x{255D}", "\x{20}\x{20}\x{2588}\x{2588}\x{2554}\x{255D}\x{20}", "\x{20}\x{2588}\x{2588}\x{2554}\x{255D}\x{20}\x{20}", "\x{2588}\x{2588}\x{2554}\x{255D}\x{20}\x{20}\x{20}", "\x{255A}\x{2550}\x{255D}\x{20}\x{20}\x{20}\x{20}" ], + '!' => [ "\x{2588}\x{2588}\x{2557}", "\x{2588}\x{2588}\x{2551}", "\x{2588}\x{2588}\x{2551}", "\x{255A}\x{2550}\x{255D}", "\x{2588}\x{2588}\x{2557}", "\x{255A}\x{2550}\x{255D}" ], + '_' => [ "\x{20}\x{20}\x{20}\x{20}\x{20}\x{20}\x{20}\x{20}", "\x{20}\x{20}\x{20}\x{20}\x{20}\x{20}\x{20}\x{20}", "\x{20}\x{20}\x{20}\x{20}\x{20}\x{20}\x{20}\x{20}", "\x{20}\x{20}\x{20}\x{20}\x{20}\x{20}\x{20}\x{20}", "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}", "\x{255A}\x{2550}\x{2550}\x{2550}\x{2550}\x{2550}\x{2550}\x{255D}" ], + '0' => [ "\x{20}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}\x{20}", "\x{2588}\x{2588}\x{2554}\x{2550}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}", "\x{2588}\x{2588}\x{2551}\x{2588}\x{2588}\x{2554}\x{2588}\x{2588}\x{2551}", "\x{2588}\x{2588}\x{2588}\x{2588}\x{2554}\x{255D}\x{2588}\x{2588}\x{2551}", "\x{255A}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2554}\x{255D}", "\x{20}\x{255A}\x{2550}\x{2550}\x{2550}\x{2550}\x{2550}\x{255D}\x{20}" ], + '1' => [ "\x{20}\x{2588}\x{2588}\x{2557}", "\x{2588}\x{2588}\x{2588}\x{2551}", "\x{255A}\x{2588}\x{2588}\x{2551}", "\x{20}\x{2588}\x{2588}\x{2551}", "\x{20}\x{2588}\x{2588}\x{2551}", "\x{20}\x{255A}\x{2550}\x{255D}" ], + '2' => [ "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}\x{20}", "\x{255A}\x{2550}\x{2550}\x{2550}\x{2550}\x{2588}\x{2588}\x{2557}", "\x{20}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2554}\x{255D}", "\x{2588}\x{2588}\x{2554}\x{2550}\x{2550}\x{2550}\x{255D}\x{20}", "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}", "\x{255A}\x{2550}\x{2550}\x{2550}\x{2550}\x{2550}\x{2550}\x{255D}" ], + '3' => [ "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}\x{20}", "\x{255A}\x{2550}\x{2550}\x{2550}\x{2550}\x{2588}\x{2588}\x{2557}", "\x{20}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2554}\x{255D}", "\x{20}\x{255A}\x{2550}\x{2550}\x{2550}\x{2588}\x{2588}\x{2557}", "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2554}\x{255D}", "\x{255A}\x{2550}\x{2550}\x{2550}\x{2550}\x{2550}\x{255D}\x{20}" ], + '4' => [ "\x{2588}\x{2588}\x{2557}\x{20}\x{20}\x{2588}\x{2588}\x{2557}", "\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{2588}\x{2588}\x{2551}", "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2551}", "\x{255A}\x{2550}\x{2550}\x{2550}\x{2550}\x{2588}\x{2588}\x{2551}", "\x{20}\x{20}\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2551}", "\x{20}\x{20}\x{20}\x{20}\x{20}\x{255A}\x{2550}\x{255D}" ], + '5' => [ "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}", "\x{2588}\x{2588}\x{2554}\x{2550}\x{2550}\x{2550}\x{2550}\x{255D}", "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}", "\x{255A}\x{2550}\x{2550}\x{2550}\x{2550}\x{2588}\x{2588}\x{2551}", "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2551}", "\x{255A}\x{2550}\x{2550}\x{2550}\x{2550}\x{2550}\x{2550}\x{255D}" ], + '6' => [ "\x{20}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}\x{20}", "\x{2588}\x{2588}\x{2554}\x{2550}\x{2550}\x{2550}\x{2550}\x{255D}\x{20}", "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}\x{20}", "\x{2588}\x{2588}\x{2554}\x{2550}\x{2550}\x{2550}\x{2588}\x{2588}\x{2557}", "\x{255A}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2554}\x{255D}", "\x{20}\x{255A}\x{2550}\x{2550}\x{2550}\x{2550}\x{2550}\x{255D}\x{20}" ], + '7' => [ "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}", "\x{255A}\x{2550}\x{2550}\x{2550}\x{2550}\x{2588}\x{2588}\x{2551}", "\x{20}\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2554}\x{255D}", "\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2554}\x{255D}\x{20}", "\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2551}\x{20}\x{20}", "\x{20}\x{20}\x{20}\x{255A}\x{2550}\x{255D}\x{20}\x{20}" ], + '8' => [ "\x{20}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}\x{20}", "\x{2588}\x{2588}\x{2554}\x{2550}\x{2550}\x{2588}\x{2588}\x{2557}", "\x{255A}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2554}\x{255D}", "\x{2588}\x{2588}\x{2554}\x{2550}\x{2550}\x{2588}\x{2588}\x{2557}", "\x{255A}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2554}\x{255D}", "\x{20}\x{255A}\x{2550}\x{2550}\x{2550}\x{2550}\x{255D}\x{20}" ], + '9' => [ "\x{20}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}\x{20}", "\x{2588}\x{2588}\x{2554}\x{2550}\x{2550}\x{2588}\x{2588}\x{2557}", "\x{255A}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2551}", "\x{20}\x{255A}\x{2550}\x{2550}\x{2550}\x{2588}\x{2588}\x{2551}", "\x{20}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2554}\x{255D}", "\x{20}\x{255A}\x{2550}\x{2550}\x{2550}\x{2550}\x{255D}\x{20}" ], +); + +sub print_big_ascii_string { + my $fh = shift; + my $str = shift; + my $comment = shift; + my $lowascii = shift; + $comment = '' if not defined $comment; + $lowascii = 0 if not defined $lowascii; + + my @chars = split //, $str; + my $charcount = scalar(@chars); + + binmode($fh, ":utf8"); + + my $maxrows = $lowascii ? 5 : 6; + + for(my $rownum = 0; $rownum < $maxrows; $rownum++){ + print $fh $comment; + my $charidx = 0; + foreach my $ch (@chars) { + my $rowsref = $big_ascii{uc($ch)}; + die("Don't have a big ascii entry for '$ch'!\n") if not defined $rowsref; + my $row = @$rowsref[$rownum]; + + if ($lowascii) { + my @x = split //, $row; + foreach (@x) { + my $v = ($_ eq "\x{2588}") ? 'X' : ' '; + print $fh $v; + } + } else { + print $fh $row; + } + + $charidx++; + + if ($charidx < $charcount) { + print $fh " "; + } + } + print $fh "\n"; + } +} + +sub generate_quickref { + my $briefsref = shift; + my $path = shift; + my $lowascii = shift; + + # !!! FIXME: this gitrev and majorver/etc stuff is copy/pasted a few times now. + if (!$gitrev) { + $gitrev = `cd "$srcpath" ; git rev-list HEAD~..`; + chomp($gitrev); + } + + # !!! FIXME + open(FH, '<', "$srcpath/$versionfname") or die("Can't open '$srcpath/$versionfname': $!\n"); + my $majorver = 0; + my $minorver = 0; + my $microver = 0; + while () { + chomp; + if (/$versionmajorregex/) { + $majorver = int($1); + } elsif (/$versionminorregex/) { + $minorver = int($1); + } elsif (/$versionmicroregex/) { + $microver = int($1); + } + } + close(FH); + my $fullversion = "$majorver.$minorver.$microver"; + + my $tmppath = "$path.tmp"; + open(my $fh, '>', $tmppath) or die("Can't open '$tmppath': $!\n"); + + if (not @quickrefcategoryorder) { + @quickrefcategoryorder = sort keys %headercategorydocs; + } + + #my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime(time); + #my $datestr = sprintf("%04d-%02d-%02d %02d:%02d:%02d GMT", $year+1900, $mon+1, $mday, $hour, $min, $sec); + + print $fh "\n\n"; + + # Just something to test big_ascii output. + #print_big_ascii_string($fh, "ABCDEFGHIJ", '', $lowascii); + #print_big_ascii_string($fh, "KLMNOPQRST", '', $lowascii); + #print_big_ascii_string($fh, "UVWXYZ0123", '', $lowascii); + #print_big_ascii_string($fh, "456789JT3A", '', $lowascii); + #print_big_ascii_string($fh, "hello, _a.b/c_!!", '', $lowascii); + + # Dan Bechard's work was on an SDL2 cheatsheet: + # https://blog.theprogrammingjunkie.com/post/sdl2-cheatsheet/ + + if ($lowascii) { + print $fh "# QuickReferenceNoUnicode\n\n"; + print $fh "If you want to paste this into a text editor that can handle\n"; + print $fh "fancy Unicode section headers, try using\n"; + print $fh "[QuickReference](QuickReference) instead.\n\n"; + } else { + print $fh "# QuickReference\n\n"; + print $fh "If you want to paste this into a text editor that can't handle\n"; + print $fh "the fancy Unicode section headers, try using\n"; + print $fh "[QuickReferenceNoUnicode](QuickReferenceNoUnicode) instead.\n\n"; + } + + print $fh "```c\n"; + print $fh "// $quickreftitle\n" if defined $quickreftitle; + print $fh "//\n"; + print $fh "// $quickrefurl\n//\n" if defined $quickrefurl; + print $fh "// $quickrefdesc\n" if defined $quickrefdesc; + #print $fh "// When this document was written: $datestr\n"; + print $fh "// Based on $projectshortname version $fullversion\n"; + #print $fh "// git revision $gitrev\n"; + print $fh "//\n"; + print $fh "// This can be useful in an IDE with search and syntax highlighting.\n"; + print $fh "//\n"; + print $fh "// Original idea for this document came from Dan Bechard (thanks!)\n"; + print $fh "// ASCII art generated by: https://patorjk.com/software/taag/#p=display&f=ANSI%20Shadow (with modified 'S' for readability)\n\n"; + + foreach (@quickrefcategoryorder) { + my $cat = $_; + my $maxlen = 0; + my @csigs = (); + my $funcorderref = $quickreffuncorder{$cat}; + next if not defined $funcorderref; + + foreach (@$funcorderref) { + my $sym = $_; + my $csig = ''; + + if ($headersymstype{$sym} == 1) { # function + $csig = "${headersymsrettype{$sym}} $sym"; + my $fnsigparams = $headersymsparaminfo{$sym}; + if (not defined($fnsigparams)) { + $csig .= '(void);'; + } else { + my $sep = '('; + for (my $i = 0; $i < scalar(@$fnsigparams); $i += 2) { + my $paramname = @$fnsigparams[$i]; + my $paramtype = @$fnsigparams[$i+1]; + my $spc = ($paramtype =~ /\*\Z/) ? '' : ' '; + $csig .= "$sep$paramtype$spc$paramname"; + $sep = ', '; + } + $csig .= ");"; + } + } elsif ($headersymstype{$sym} == 2) { # macro + next if defined $quickrefmacroregex && not $sym =~ /$quickrefmacroregex/; + + $csig = (split /\n/, $headerdecls{$sym})[0]; # get the first line from a multiline string. + if (not $csig =~ s/\A(\#define [a-zA-Z0-9_]*\(.*?\))(\s+.*)?\Z/$1/) { + $csig =~ s/\A(\#define [a-zA-Z0-9_]*)(\s+.*)?\Z/$1/; + } + chomp($csig); + } + + my $len = length($csig); + $maxlen = $len if $len > $maxlen; + + push @csigs, $sym; + push @csigs, $csig; + } + + $maxlen += 2; + + next if (not @csigs); + + print $fh "\n"; + print_big_ascii_string($fh, $cat, '// ', $lowascii); + print $fh "\n"; + + while (@csigs) { + my $sym = shift @csigs; + my $csig = shift @csigs; + my $brief = $$briefsref{$sym}; + if (defined $brief) { + $brief = "$brief"; + chomp($brief); + my $thiswikitype = defined $wikitypes{$sym} ? $wikitypes{$sym} : 'md'; # default to MarkDown for new stuff. + $brief = dewikify($thiswikitype, $brief); + my $spaces = ' ' x ($maxlen - length($csig)); + $brief = "$spaces// $brief"; + } else { + $brief = ''; + } + print $fh "$csig$brief\n"; + } + } + + print $fh "```\n\n"; + + close($fh); + +# # Don't overwrite the file if nothing has changed besides the timestamp +# # and git revision. +# my $matches = 1; +# if ( not -f $path ) { +# $matches = 0; # always write if the file hasn't been created yet. +# } else { +# open(my $fh_a, '<', $tmppath) or die("Can't open '$tmppath': $!\n"); +# open(my $fh_b, '<', $path) or die("Can't open '$path': $!\n"); +# while (1) { +# my $a = <$fh_a>; +# my $b = <$fh_b>; +# $matches = 0, last if ((not defined $a) != (not defined $b)); +# last if ((not defined $a) || (not defined $b)); +# if ($a ne $b) { +# next if ($a =~ /\A\/\/ When this document was written:/); +# next if ($a =~ /\A\/\/ git revision /); +# $matches = 0; +# last; +# } +# } +# +# close($fh_a); +# close($fh_b); +# } +# +# if ($matches) { +# unlink($tmppath); # it's the same file except maybe the date/gitrev. Don't overwrite it. +# } else { +# rename($tmppath, $path) or die("Can't rename '$tmppath' to '$path': $!\n"); +# } + rename($tmppath, $path) or die("Can't rename '$tmppath' to '$path': $!\n"); +} + + my $incpath = "$srcpath"; $incpath .= "/$incsubdir" if $incsubdir ne ''; @@ -749,10 +1035,12 @@ while (my $d = readdir(DH)) { } my @contents = (); + my @function_order = (); my $ignoring_lines = 0; my $header_comment = -1; my $saw_category_doxygen = -1; my $lineno = 0; + while () { chomp; $lineno++; @@ -1073,7 +1361,6 @@ while (my $d = readdir(DH)) { } } $decl .= $additional_decl; - } elsif ($symtype == 2) { # a macro if ($decl =~ /\A\s*\#\s*define\s+(.*?)(\(.*?\)|)\s+/) { $sym = $1; @@ -1301,6 +1588,7 @@ while (my $d = readdir(DH)) { $headersymstype{$sym} = $symtype; $headersymsparaminfo{$sym} = \@paraminfo if (scalar(@paraminfo) > 0); $headersymsrettype{$sym} = $rettype if (defined($rettype)); + push @function_order, $sym if ($symtype == 1) || ($symtype == 2); push @contents, join("\n", @templines); push @contents, join("\n", @decllines) if (scalar(@decllines) > 0); } @@ -1309,6 +1597,7 @@ while (my $d = readdir(DH)) { close(FH); $headers{$dent} = \@contents; + $quickreffuncorder{$current_wiki_category} = \@function_order if defined $current_wiki_category; } closedir(DH); @@ -1785,6 +2074,8 @@ if ($copy_direction == 1) { # --copy-to-headers } elsif ($copy_direction == -1) { # --copy-to-wiki + my %briefs = (); # $briefs{'SDL_OpenAudio'} -> the \brief string for the function. + if (defined $changeformat) { $dewikify_mode = $changeformat; $wordwrap_mode = $changeformat; @@ -1856,6 +2147,8 @@ if ($copy_direction == 1) { # --copy-to-headers $sections{'Remarks'} = "$remarks\n" if $remarks ne ''; $sections{'Syntax'} = $syntax; + $briefs{$sym} = $brief; + my %params = (); # have to parse these and build up the wiki tables after, since Markdown needs to know the length of the largest string. :/ my @paramsorder = (); my $fnsigparams = $headersymsparaminfo{$sym}; @@ -2415,6 +2708,11 @@ __EOF__ } } + # Write out quick reference pages... + if ($quickrefenabled) { + generate_quickref(\%briefs, "$wikipath/QuickReference.md", 0); + generate_quickref(\%briefs, "$wikipath/QuickReferenceNoUnicode.md", 1); + } } elsif ($copy_direction == -2) { # --copy-to-manpages # This only takes from the wiki data, since it has sections we omit from the headers, like code examples. diff --git a/libs/SDL3/cmake/android/FindSdlAndroid.cmake b/libs/SDL3/cmake/android/FindSdlAndroid.cmake index 38a2d7a1..851848f7 100644 --- a/libs/SDL3/cmake/android/FindSdlAndroid.cmake +++ b/libs/SDL3/cmake/android/FindSdlAndroid.cmake @@ -61,7 +61,7 @@ This module will set the following variables in your project: #]=======================================================================] -cmake_minimum_required(VERSION 3.7) +cmake_minimum_required(VERSION 3.7...3.28) if(NOT PROJECT_NAME MATCHES "^SDL.*") message(WARNING "This module is internal to SDL and is currently not supported.") diff --git a/libs/SDL3/cmake/android/FindSdlAndroidBuildTools.cmake b/libs/SDL3/cmake/android/FindSdlAndroidBuildTools.cmake index b8825d92..999a268e 100644 --- a/libs/SDL3/cmake/android/FindSdlAndroidBuildTools.cmake +++ b/libs/SDL3/cmake/android/FindSdlAndroidBuildTools.cmake @@ -52,7 +52,7 @@ This module responds to the flags: #]=======================================================================] -cmake_minimum_required(VERSION 3.7) +cmake_minimum_required(VERSION 3.7...3.28) if(NOT PROJECT_NAME MATCHES "^SDL.*") message(WARNING "This module is internal to SDL and is currently not supported.") diff --git a/libs/SDL3/cmake/android/FindSdlAndroidPlatform.cmake b/libs/SDL3/cmake/android/FindSdlAndroidPlatform.cmake index 7b569e3e..fbe53c30 100644 --- a/libs/SDL3/cmake/android/FindSdlAndroidPlatform.cmake +++ b/libs/SDL3/cmake/android/FindSdlAndroidPlatform.cmake @@ -55,7 +55,7 @@ This module responds to the flags: #]=======================================================================] -cmake_minimum_required(VERSION 3.7) +cmake_minimum_required(VERSION 3.7...3.28) if(NOT PROJECT_NAME MATCHES "^SDL.*") message(WARNING "This module is internal to SDL and is currently not supported.") diff --git a/libs/SDL3/cmake/android/SdlAndroidFunctions.cmake b/libs/SDL3/cmake/android/SdlAndroidFunctions.cmake index b7b3aa84..4acce470 100644 --- a/libs/SDL3/cmake/android/SdlAndroidFunctions.cmake +++ b/libs/SDL3/cmake/android/SdlAndroidFunctions.cmake @@ -5,7 +5,7 @@ It is (currently) limited to packaging binaries for a single architecture. #]=======================================================================] -cmake_minimum_required(VERSION 3.7) +cmake_minimum_required(VERSION 3.7...3.28) if(NOT PROJECT_NAME MATCHES "^SDL.*") message(WARNING "This module is internal to SDL and is currently not supported.") diff --git a/libs/SDL3/cmake/android/SdlAndroidScript.cmake b/libs/SDL3/cmake/android/SdlAndroidScript.cmake index dd98f9b4..15dea2d1 100644 --- a/libs/SDL3/cmake/android/SdlAndroidScript.cmake +++ b/libs/SDL3/cmake/android/SdlAndroidScript.cmake @@ -6,7 +6,7 @@ Because #]=======================================================================] -cmake_minimum_required(VERSION 3.16) +cmake_minimum_required(VERSION 3.16...3.28) if(NOT CMAKE_SCRIPT_MODE_FILE) message(FATAL_ERROR "This file can only be used in CMake script mode") diff --git a/libs/SDL3/cmake/sdlchecks.cmake b/libs/SDL3/cmake/sdlchecks.cmake index 4efdf86a..c0bdabc4 100644 --- a/libs/SDL3/cmake/sdlchecks.cmake +++ b/libs/SDL3/cmake/sdlchecks.cmake @@ -307,6 +307,7 @@ macro(CheckX11) find_file(HAVE_XRANDR_H NAMES "X11/extensions/Xrandr.h" HINTS "${X11_INCLUDEDIR}") find_file(HAVE_XFIXES_H_ NAMES "X11/extensions/Xfixes.h" HINTS "${X11_INCLUDEDIR}") find_file(HAVE_XRENDER_H NAMES "X11/extensions/Xrender.h" HINTS "${X11_INCLUDEDIR}") + find_file(HAVE_XSYNC_H NAMES "X11/extensions/sync.h" HINTS "${X11_INCLUDEDIR}") find_file(HAVE_XSS_H NAMES "X11/extensions/scrnsaver.h" HINTS "${X11_INCLUDEDIR}") find_file(HAVE_XSHAPE_H NAMES "X11/extensions/shape.h" HINTS "${X11_INCLUDEDIR}") find_file(HAVE_XDBE_H NAMES "X11/extensions/Xdbe.h" HINTS "${X11_INCLUDEDIR}") @@ -445,6 +446,11 @@ macro(CheckX11) set(HAVE_X11_XFIXES TRUE) endif() + if(SDL_X11_XSYNC AND HAVE_XSYNC_H AND XEXT_LIB) + set(SDL_VIDEO_DRIVER_X11_XSYNC 1) + set(HAVE_X11_XSYNC TRUE) + endif() + if(SDL_X11_XRANDR AND HAVE_XRANDR_H AND XRANDR_LIB) if(HAVE_X11_SHARED) set(SDL_VIDEO_DRIVER_X11_DYNAMIC_XRANDR "\"${XRANDR_LIB_SONAME}\"") diff --git a/libs/SDL3/cmake/sdlcpu.cmake b/libs/SDL3/cmake/sdlcpu.cmake index d63a57f9..0c2ca1f5 100644 --- a/libs/SDL3/cmake/sdlcpu.cmake +++ b/libs/SDL3/cmake/sdlcpu.cmake @@ -61,8 +61,8 @@ const char *arch_${known_arch} = \"INFO<${known_arch}=\" ARCH_${known_arch} \">\ set(src_arch_detect "${src_vars} int main(int argc, char *argv[]) { - (void)argv; int result = 0; + (void)argv; ${src_main} return result; }") diff --git a/libs/SDL3/docs/README-documentation-rules.md b/libs/SDL3/docs/README-documentation-rules.md index ae705378..3151de76 100644 --- a/libs/SDL3/docs/README-documentation-rules.md +++ b/libs/SDL3/docs/README-documentation-rules.md @@ -242,6 +242,15 @@ wikiheaders will complain loudly if you don't do this, and exit with an error message. +## Don't repeat type names in `\param` and `\returns` sections. + +Wikiheaders will explicitly mention the datatype for each parameter and the +return value, linking to the datatype's wikipage. Users reading the headers +can see the types in the function signature right below the documentation +comment. So don't mention the type a second time in the documentation if +possible. It looks cluttered and repetitive to do so. + + ## Code examples go in the wiki. We don't want the headers cluttered up with code examples. These live on the diff --git a/libs/SDL3/docs/README-linux.md b/libs/SDL3/docs/README-linux.md index 473c591e..ce740ea8 100644 --- a/libs/SDL3/docs/README-linux.md +++ b/libs/SDL3/docs/README-linux.md @@ -21,7 +21,7 @@ Ubuntu 18.04, all available features enabled: libxkbcommon-dev libdrm-dev libgbm-dev libgl1-mesa-dev libgles2-mesa-dev \ libegl1-mesa-dev libdbus-1-dev libibus-1.0-dev libudev-dev fcitx-libs-dev -Ubuntu 22.04+ can also add `libpipewire-0.3-dev libwayland-dev libdecor-0-dev` to that command line. +Ubuntu 22.04+ can also add `libpipewire-0.3-dev libwayland-dev libdecor-0-dev liburing-dev` to that command line. Fedora 35, all available features enabled: @@ -34,6 +34,8 @@ Fedora 35, all available features enabled: libdrm-devel mesa-libgbm-devel libusb-devel libdecor-devel \ pipewire-jack-audio-connection-kit-devel \ +Fedora 39+ can also add `liburing-devel` to that command line. + NOTES: - The sndio audio target is unavailable on Fedora (but probably not what you should want to use anyhow). diff --git a/libs/SDL3/docs/README-main-functions.md b/libs/SDL3/docs/README-main-functions.md index e36654fc..75b9e2cc 100644 --- a/libs/SDL3/docs/README-main-functions.md +++ b/libs/SDL3/docs/README-main-functions.md @@ -204,3 +204,33 @@ data, as this pointer will not be provided to your app again. The SDL_AppResult value that terminated the app is provided here, in case it's useful to know if this was a successful or failing run of the app. + + +## Summary and Best Practices + +- **Always Include SDL_main.h in One Source File:** When working with SDL, + remember that SDL_main.h must only be included in one source file in your + project. Including it in multiple files will lead to conflicts and undefined + behavior. + +- **Avoid Redefining main:** If you're using SDL's entry point system (which + renames `main` to `SDL_main`), do not define `main` yourself. SDL takes care + of this for you, and redefining it can cause issues, especially when linking + with SDL libraries. + +- **Using SDL's Callback System:** If you're working with more complex + scenarios, such as requiring more control over your application's flow + (e.g., with games or apps that need extensive event handling), consider + using SDL's callback system. Define the necessary callbacks and SDL will + handle initialization, event processing, and cleanup automatically. + +- **Platform-Specific Considerations:** On platforms like Windows, SDL handles + the platform-specific entry point (like `WinMain`) automatically. This means + you don't need to worry about writing platform-specific entry code when + using SDL. + +- **When to Skip SDL_main.h:** If you do not require SDL's custom entry point + (for example, if you're integrating SDL into an existing application or a + scripting environment), you can omit SDL_main.h. However, this will limit + SDL's ability to abstract away platform-specific entry point details. + diff --git a/libs/SDL3/docs/README-migration.md b/libs/SDL3/docs/README-migration.md index e213fa81..59f3914a 100644 --- a/libs/SDL3/docs/README-migration.md +++ b/libs/SDL3/docs/README-migration.md @@ -179,7 +179,7 @@ SDL_AudioDeviceID now represents both an open audio device's handle (a "logical" Devices are opened by physical device instance ID, and a new logical instance ID is generated by the open operation; This allows any device to be opened multiple times, possibly by unrelated pieces of code. SDL will manage the logical devices to provide a single stream of audio to the physical device behind the scenes. -Devices are not opened by an arbitrary string name anymore, but by device instance ID (or magic numbers to request a reasonable default, like a NULL string in SDL2). In SDL2, the string was used to open both a standard list of system devices, but also allowed for arbitrary devices, such as hostnames of network sound servers. In SDL3, many of the backends that supported arbitrary device names are obsolete and have been removed; of those that remain, arbitrary devices will be opened with a default device ID and an SDL_hint, so specific end-users can set an environment variable to fit their needs and apps don't have to concern themselves with it. +Devices are not opened by an arbitrary string name anymore, but by device instance ID (or magic numbers to request a reasonable default, like a NULL string would do in SDL2). In SDL2, the string was used to open both a standard list of system devices, but also allowed for arbitrary devices, such as hostnames of network sound servers. In SDL3, many of the backends that supported arbitrary device names are obsolete and have been removed; of those that remain, arbitrary devices will be opened with a default device ID and an SDL_hint, so specific end-users can set an environment variable to fit their needs and apps don't have to concern themselves with it. Many functions that would accept a device index and an `iscapture` parameter now just take an SDL_AudioDeviceID, as they are unique across all devices, instead of separate indices into playback and recording device lists. @@ -214,7 +214,7 @@ SDL_QueueAudio(), SDL_DequeueAudio, and SDL_ClearQueuedAudio and SDL_GetQueuedAu APIs that use channel counts used to use a Uint8 for the channel; now they use int. -SDL_AudioSpec has been reduced; now it only holds format, channel, and sample rate. SDL_GetSilenceValueForFormat() can provide the information from the SDL_AudioSpec's `silence` field. The other SDL2 SDL_AudioSpec fields aren't relevant anymore. +SDL_AudioSpec has been reduced; now it only holds format, channel, and sample rate. SDL_GetSilenceValueForFormat() can provide the information from the SDL_AudioSpec's removed `silence` field. SDL3 now manages the removed `samples` field; apps that want more control over device latency and throughput can force a newly-opened device's sample count with the SDL_HINT_AUDIO_DEVICE_SAMPLE_FRAMES hint, but most apps should not risk messing with the defaults. The other SDL2 SDL_AudioSpec fields aren't relevant anymore. SDL_GetAudioDeviceSpec() is removed; use SDL_GetAudioDeviceFormat() instead. @@ -820,12 +820,14 @@ The following hints have been renamed: The following hints have been removed: * SDL_HINT_ACCELEROMETER_AS_JOYSTICK * SDL_HINT_ANDROID_BLOCK_ON_PAUSE_PAUSEAUDIO - the audio will be paused when the application is paused, and SDL_HINT_ANDROID_BLOCK_ON_PAUSE can be used to control that +* SDL_HINT_AUDIO_DEVICE_APP_NAME - replaced by either using the appname param to SDL_SetAppMetadata() or setting SDL_PROP_APP_METADATA_NAME_STRING with SDL_SetAppMetadataProperty() * SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS - gamepad buttons are always positional * SDL_HINT_GRAB_KEYBOARD - use SDL_SetWindowKeyboardGrab() instead * SDL_HINT_IDLE_TIMER_DISABLED - use SDL_DisableScreenSaver() instead * SDL_HINT_IME_INTERNAL_EDITING - replaced with SDL_HINT_IME_IMPLEMENTED_UI * SDL_HINT_IME_SHOW_UI - replaced with SDL_HINT_IME_IMPLEMENTED_UI * SDL_HINT_IME_SUPPORT_EXTENDED_TEXT - the normal text editing event has extended text +* SDL_HINT_MOUSE_RELATIVE_MODE_WARP - relative mode is always implemented at the hardware level or reported as unavailable * SDL_HINT_MOUSE_RELATIVE_SCALING - mouse coordinates are no longer automatically scaled by the SDL renderer * SDL_HINT_PS2_DYNAMIC_VSYNC - use SDL_SetRenderVSync(renderer, -1) instead * SDL_HINT_RENDER_BATCHING - Render batching is always enabled, apps should call SDL_FlushRenderer() before calling into a lower-level graphics API. @@ -848,7 +850,6 @@ The following hints have been removed: * SDL_HINT_WINRT_PRIVACY_POLICY_LABEL - WinRT support was removed in SDL3. * SDL_HINT_WINRT_PRIVACY_POLICY_URL - WinRT support was removed in SDL3. * SDL_HINT_XINPUT_USE_OLD_JOYSTICK_MAPPING -* SDL_HINT_AUDIO_DEVICE_APP_NAME - replaced by either using the appname param to SDL_SetAppMetadata() or setting SDL_PROP_APP_METADATA_NAME_STRING with SDL_SetAppMetadataProperty() The following environment variables have been renamed: * SDL_AUDIODRIVER => SDL_AUDIO_DRIVER @@ -1290,7 +1291,6 @@ The following platform preprocessor macros have been renamed: | `__LINUX__` | `SDL_PLATFORM_LINUX` | | `__MACOSX__` | `SDL_PLATFORM_MACOS` | | `__NETBSD__` | `SDL_PLATFORM_NETBSD` | -| `__NGAGE__` | `SDL_PLATFORM_NGAGE` | | `__OPENBSD__` | `SDL_PLATFORM_OPENBSD` | | `__OS2__` | `SDL_PLATFORM_OS2` | | `__OSF__` | `SDL_PLATFORM_OSF` | @@ -2210,7 +2210,6 @@ The following functions have been renamed: * SDL_SetWindowDisplayMode() => SDL_SetWindowFullscreenMode(), returns bool The following functions have been removed: -* SDL_GetClosestFullscreenDisplayMode() * SDL_GetDisplayDPI() - not reliable across platforms, approximately replaced by multiplying SDL_GetWindowDisplayScale() times 160 on iPhone and Android, and 96 on other platforms. * SDL_GetDisplayMode() * SDL_GetNumDisplayModes() - replaced with SDL_GetFullscreenDisplayModes() diff --git a/libs/SDL3/docs/README-ngage.md b/libs/SDL3/docs/README-ngage.md index 363760b9..84192b01 100644 --- a/libs/SDL3/docs/README-ngage.md +++ b/libs/SDL3/docs/README-ngage.md @@ -1,44 +1,5 @@ -Nokia N-Gage -============ +Support for the Nokia N-Gage has been removed from SDL3 (but will make a +comeback when newer compilers are available for the platform). -SDL port for Symbian S60v1 and v2 with a main focus on the Nokia N-Gage -(Classic and QD) by [Michael Fitzmayer](https://github.com/mupfdev). +SDL2 still supports this platform. -Compiling ---------- - -SDL is part of the [N-Gage SDK.](https://github.com/ngagesdk) project. -The library is included in the -[toolchain](https://github.com/ngagesdk/ngage-toolchain) as a -sub-module. - -A complete example project based on SDL can be found in the GitHub -account of the SDK: [Wordle](https://github.com/ngagesdk/wordle). - -Current level of implementation -------------------------------- - -The video driver currently provides full screen video support with -keyboard input. - -At the moment only the software renderer works. - -Audio is not yet implemented. - -Acknowledgements ----------------- - -Thanks to Hannu Viitala, Kimmo Kinnunen and Markus Mertama for the -valuable insight into Symbian programming. Without the SDL 1.2 port -which was specially developed for CDoom (Doom for the Nokia 9210), this -adaptation would not have been possible. - -I would like to thank my friends -[Razvan](https://twitter.com/bewarerazvan) and [Dan -Whelan](https://danwhelan.ie/), for their continuous support. Without -you and the [N-Gage community](https://discord.gg/dbUzqJ26vs), I would -have lost my patience long ago. - -Last but not least, I would like to thank the development team of -[EKA2L1](https://12z1.com/) (an experimental Symbian OS emulator). Your -patience and support in troubleshooting helped me a lot. diff --git a/libs/SDL3/docs/README-raspberrypi.md b/libs/SDL3/docs/README-raspberrypi.md index 9984ec6d..ba34f47b 100644 --- a/libs/SDL3/docs/README-raspberrypi.md +++ b/libs/SDL3/docs/README-raspberrypi.md @@ -3,27 +3,35 @@ Raspberry Pi Requirements: -Raspbian (other Linux distros may work as well). +Raspberry Pi OS (other Linux distros may work as well). -Features --------- +In modern times, the Raspberry Pi works mostly like any other Linux device: +for video, you can use X11, Wayland, or KMSDRM. For audio, you can use ALSA, +PulseAudio, or PipeWire, etc. OpenGL, OpenGL ES, and Vulkan are known to work. -* Works without X11 -* Hardware accelerated OpenGL ES 2.x -* Sound via ALSA -* Input (mouse/keyboard/joystick) via EVDEV -* Hotplugging of input devices via UDEV +There is a video backend in SDL called "rpi" that uses a deprecated Broadcom +interface (named "dispmanx") to draw directly to the console without X11. +Newer Raspberry Pi OS releases don't support this (and work fine with our +"kmsdrm" backend for the same purposes, a standard Linux interface). Don't +panic if you can't use this backend, or CMake says it can't find libraries it +needs for this. + +SDL has, in past times, worked on the original Raspberry Pi and the RPi 2, but +these devices are no longer targets we actively test; if they broke, please +report bugs or send patches! + +The Raspberry Pi 3 and later (in 32-bit and 64-bit mode) are still known to +work well at the time of this writing. The Raspberry Pi Zero and Zero 2 are +also known to work well. -Raspbian Build Dependencies ---------------------------- +## Documentation Out Of Date -sudo apt-get install libudev-dev libasound2-dev libdbus-1-dev +The rest of this document is likely out of date; a lot has changed in recent +years in both SDL and the Raspberry Pi universe, and this document has not +been updated to reflect those details. Take the rest of this information with +a grain of salt! -You also need the VideoCore binary stuff that ships in /opt/vc for EGL and -OpenGL ES 2.x, it usually comes pre-installed, but in any case: - -sudo apt-get install libraspberrypi0 libraspberrypi-bin libraspberrypi-dev NEON @@ -31,8 +39,8 @@ NEON If your Pi has NEON support, make sure you add -mfpu=neon to your CFLAGS so that SDL will select some otherwise-disabled highly-optimized code. The -original Pi units don't have NEON, the Pi2 probably does, and the Pi3 -definitely does. +original Pi and Pi Zero units don't have NEON; everything from the Pi2/PiZero2 +and later do. Cross compiling from x86 Linux diff --git a/libs/SDL3/docs/README-touch.md b/libs/SDL3/docs/README-touch.md index 97925279..05edc2c8 100644 --- a/libs/SDL3/docs/README-touch.md +++ b/libs/SDL3/docs/README-touch.md @@ -14,7 +14,7 @@ Works out of box. Windows: Unfortunately there is no windows support as of yet. Support for Windows 7 is planned, but we currently have no way to test. If you have a Windows 7 WM_TOUCH supported device, and are willing to help test please contact me at jim.tla+sdl_touch@gmail.com -=========================================================================== + Events =========================================================================== SDL_EVENT_FINGER_DOWN: @@ -39,7 +39,6 @@ Fields: Same as SDL_EVENT_FINGER_DOWN. -=========================================================================== Functions =========================================================================== SDL provides the ability to access the underlying SDL_Finger structures. diff --git a/libs/SDL3/docs/release_checklist.md b/libs/SDL3/docs/release_checklist.md index 7f762944..56fb23d0 100644 --- a/libs/SDL3/docs/release_checklist.md +++ b/libs/SDL3/docs/release_checklist.md @@ -1,16 +1,19 @@ # Release checklist -* Run `build-scripts/create-release.py -R libsdl-org/SDL --ref ` to command - GitHub Actions to start creating release assets. - It's advisable to run this script regularly, and also prior to any release step. - When creating the release assets, `` must be the release tag - This makes sure the revision string baked into the archives is correct. +* Run `build-scripts/create-release.py -R libsdl-org/SDL --ref ` to do + a dry run creating the release assets. Verify that the archives are correct. -* When changing the version, run `build-scripts/update-version.sh X Y Z`, - where `X Y Z` are the major version, minor version, and patch level. So - `3 8 1` means "change the version to 3.8.1". This script does much of the - mechanical work. +* Tag the release, e.g. `git tag release-3.8.0; git push --tags` +* Run `build-scripts/create-release.py -R libsdl-org/SDL --ref ` + to have GitHub Actions create release assets. This makes sure the revision + string baked into the archives is correct. + +* Verify that the source archive REVISION.txt has the correct release tag. + +* Sign the source archives and upload everything to libsdl.org + +* Create a GitHub release and attach the archives you just generated. ## New feature release @@ -22,14 +25,16 @@ * Do the release +* Immediately create a branch for patch releases, e.g. `git branch release-3.EVEN.x` + +* Bump version number from 3.EVEN.0 to 3.(EVEN+1).0 + + * `./build-scripts/update-version.sh 3 EVEN+1 0` + * Update the website file include/header.inc.php to reflect the new version ## New bugfix release -* Check that no new API/ABI was added - - * If it was, do a new feature release (see above) instead - * Bump version number from 3.Y.Z to 3.Y.(Z+1) (Y is even) * `./build-scripts/update-version.sh 3 Y Z+1` @@ -38,14 +43,6 @@ * Update the website file include/header.inc.php to reflect the new version -## After a feature release - -* Create a branch like `release-3.4.x` - -* Bump version number to 3.ODD.0 for next development branch - - * `./build-scripts/update-version.sh 3 ODD 0` - ## New development prerelease * Bump version number from 3.Y.Z to 3.Y.(Z+1) (Y is odd) diff --git a/libs/SDL3/examples/CMakeLists.txt b/libs/SDL3/examples/CMakeLists.txt index dec58ddb..2a13a038 100644 --- a/libs/SDL3/examples/CMakeLists.txt +++ b/libs/SDL3/examples/CMakeLists.txt @@ -110,6 +110,7 @@ macro(add_sdl_example_executable TARGET) target_link_libraries(${TARGET} PRIVATE GL) elseif(EMSCRIPTEN) set_property(TARGET ${TARGET} PROPERTY SUFFIX ".html") + target_link_options(${TARGET} PRIVATE -sALLOW_MEMORY_GROWTH=1) endif() if(OPENGL_FOUND) @@ -138,11 +139,16 @@ add_sdl_example_executable(renderer-debug-text SOURCES renderer/18-debug-text/de add_sdl_example_executable(audio-simple-playback SOURCES audio/01-simple-playback/simple-playback.c) add_sdl_example_executable(audio-simple-playback-callback SOURCES audio/02-simple-playback-callback/simple-playback-callback.c) add_sdl_example_executable(audio-load-wav SOURCES audio/03-load-wav/load-wav.c DATAFILES ${CMAKE_CURRENT_SOURCE_DIR}/../test/sample.wav) +add_sdl_example_executable(audio-multiple-streams SOURCES audio/04-multiple-streams/multiple-streams.c DATAFILES ${CMAKE_CURRENT_SOURCE_DIR}/../test/sample.wav ${CMAKE_CURRENT_SOURCE_DIR}/../test/sword.wav) +add_sdl_example_executable(input-joystick-polling SOURCES input/01-joystick-polling/joystick-polling.c) +add_sdl_example_executable(input-joystick-events SOURCES input/02-joystick-events/joystick-events.c) add_sdl_example_executable(camera-read-and-draw SOURCES camera/01-read-and-draw/read-and-draw.c) add_sdl_example_executable(pen-drawing-lines SOURCES pen/01-drawing-lines/drawing-lines.c) -add_sdl_example_executable(game-snake SOURCES game/01-snake/snake.c) -add_sdl_example_executable(game-woodeneye-008 SOURCES game/02-woodeneye-008/woodeneye-008.c) -add_sdl_example_executable(game-infinite-monkeys SOURCES game/03-infinite-monkeys/infinite-monkeys.c) +add_sdl_example_executable(asyncio-load-bitmaps SOURCES asyncio/01-load-bitmaps/load-bitmaps.c DATAFILES ${CMAKE_CURRENT_SOURCE_DIR}/../test/sample.bmp ${CMAKE_CURRENT_SOURCE_DIR}/../test/gamepad_front.bmp ${CMAKE_CURRENT_SOURCE_DIR}/../test/speaker.bmp ${CMAKE_CURRENT_SOURCE_DIR}/../test/icon2x.bmp) +add_sdl_example_executable(demo-snake SOURCES demo/01-snake/snake.c) +add_sdl_example_executable(demo-woodeneye-008 SOURCES demo/02-woodeneye-008/woodeneye-008.c) +add_sdl_example_executable(demo-infinite-monkeys SOURCES demo/03-infinite-monkeys/infinite-monkeys.c) +add_sdl_example_executable(demo-bytepusher SOURCES demo/04-bytepusher/bytepusher.c) # When you add an example, remember to add the Visual Studio project as well: # - Add a new example in examples/ diff --git a/libs/SDL3/examples/asyncio/01-load-bitmaps/README.txt b/libs/SDL3/examples/asyncio/01-load-bitmaps/README.txt new file mode 100644 index 00000000..e4283d46 --- /dev/null +++ b/libs/SDL3/examples/asyncio/01-load-bitmaps/README.txt @@ -0,0 +1,6 @@ +This example code loads a few bitmap files from disk using the asynchronous +i/o, and then draws it to the window. It uses a task group to watch multiple +reads and deal with them in whatever order they finish. + +Note that for a single tiny file like this, you'd probably not want to bother +with async i/o in real life, but this is just an example of how to do it. diff --git a/libs/SDL3/examples/asyncio/01-load-bitmaps/load-bitmaps.c b/libs/SDL3/examples/asyncio/01-load-bitmaps/load-bitmaps.c new file mode 100644 index 00000000..44c2abf7 --- /dev/null +++ b/libs/SDL3/examples/asyncio/01-load-bitmaps/load-bitmaps.c @@ -0,0 +1,125 @@ +/* + * This example code loads a bitmap with asynchronous i/o and renders it. + * + * This code is public domain. Feel free to use it for any purpose! + */ + +#define SDL_MAIN_USE_CALLBACKS 1 /* use the callbacks instead of main() */ +#include +#include + +/* We will use this renderer to draw into this window every frame. */ +static SDL_Window *window = NULL; +static SDL_Renderer *renderer = NULL; +static SDL_AsyncIOQueue *queue = NULL; + +#define TOTAL_TEXTURES 4 +static const char * const bmps[TOTAL_TEXTURES] = { "sample.bmp", "gamepad_front.bmp", "speaker.bmp", "icon2x.bmp" }; +static SDL_Texture *textures[TOTAL_TEXTURES]; +static const SDL_FRect texture_rects[TOTAL_TEXTURES] = { + { 116, 156, 408, 167 }, + { 20, 200, 96, 60 }, + { 525, 180, 96, 96 }, + { 288, 375, 64, 64 } +}; + +/* This function runs once at startup. */ +SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[]) +{ + int i; + + if (!SDL_Init(SDL_INIT_VIDEO)) { + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Couldn't initialize SDL!", SDL_GetError(), NULL); + return SDL_APP_FAILURE; + } + + if (!SDL_CreateWindowAndRenderer("examples/asyncio/load-bitmaps", 640, 480, 0, &window, &renderer)) { + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Couldn't create window/renderer!", SDL_GetError(), NULL); + return SDL_APP_FAILURE; + } + + queue = SDL_CreateAsyncIOQueue(); + if (!queue) { + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Couldn't create async i/o queue!", SDL_GetError(), NULL); + return SDL_APP_FAILURE; + } + + /* Load some .bmp files asynchronously from wherever the app is being run from, put them in the same queue. */ + for (i = 0; i < SDL_arraysize(bmps); i++) { + char *path = NULL; + SDL_asprintf(&path, "%s%s", SDL_GetBasePath(), bmps[i]); /* allocate a string of the full file path */ + /* you _should) check for failure, but we'll just go on without files here. */ + SDL_LoadFileAsync(path, queue, (void *) bmps[i]); /* attach the filename as app-specific data, so we can see it later. */ + SDL_free(path); + } + + return SDL_APP_CONTINUE; /* carry on with the program! */ +} + +/* This function runs when a new event (mouse input, keypresses, etc) occurs. */ +SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) +{ + if (event->type == SDL_EVENT_QUIT) { + return SDL_APP_SUCCESS; /* end the program, reporting success to the OS. */ + } + + return SDL_APP_CONTINUE; /* carry on with the program! */ +} + +/* This function runs once per frame, and is the heart of the program. */ +SDL_AppResult SDL_AppIterate(void *appstate) +{ + SDL_AsyncIOOutcome outcome; + int i; + + if (SDL_GetAsyncIOResult(queue, &outcome)) { /* a .bmp file load has finished? */ + if (outcome.result == SDL_ASYNCIO_COMPLETE) { + /* this might be _any_ of the bmps; they might finish loading in any order. */ + for (i = 0; i < SDL_arraysize(bmps); i++) { + /* this doesn't need a strcmp because we gave the pointer from this array to SDL_LoadFileAsync */ + if (outcome.userdata == bmps[i]) { + break; + } + } + + if (i < SDL_arraysize(bmps)) { /* (just in case.) */ + SDL_Surface *surface = SDL_LoadBMP_IO(SDL_IOFromConstMem(outcome.buffer, (size_t) outcome.bytes_transferred), true); + if (surface) { /* the renderer is not multithreaded, so create the texture here once the data loads. */ + textures[i] = SDL_CreateTextureFromSurface(renderer, surface); + if (!textures[i]) { + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Couldn't create texture!", SDL_GetError(), NULL); + return SDL_APP_FAILURE; + } + SDL_DestroySurface(surface); + } + } + } + SDL_free(outcome.buffer); + } + + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); + SDL_RenderClear(renderer); + + for (i = 0; i < SDL_arraysize(textures); i++) { + SDL_RenderTexture(renderer, textures[i], NULL, &texture_rects[i]); + } + + SDL_RenderPresent(renderer); + + return SDL_APP_CONTINUE; /* carry on with the program! */ +} + +/* This function runs once at shutdown. */ +void SDL_AppQuit(void *appstate, SDL_AppResult result) +{ + int i; + + SDL_DestroyAsyncIOQueue(queue); + + for (i = 0; i < SDL_arraysize(textures); i++) { + SDL_DestroyTexture(textures[i]); + } + + /* SDL will clean up the window/renderer for us. */ +} + diff --git a/libs/SDL3/examples/asyncio/01-load-bitmaps/thumbnail.png b/libs/SDL3/examples/asyncio/01-load-bitmaps/thumbnail.png new file mode 100644 index 00000000..4f8be2a8 Binary files /dev/null and b/libs/SDL3/examples/asyncio/01-load-bitmaps/thumbnail.png differ diff --git a/libs/SDL3/examples/asyncio/description.txt b/libs/SDL3/examples/asyncio/description.txt new file mode 100644 index 00000000..c3aafe13 --- /dev/null +++ b/libs/SDL3/examples/asyncio/description.txt @@ -0,0 +1 @@ +Asynchronous I/O \ No newline at end of file diff --git a/libs/SDL3/examples/audio/04-multiple-streams/README.txt b/libs/SDL3/examples/audio/04-multiple-streams/README.txt new file mode 100644 index 00000000..9ef2275b --- /dev/null +++ b/libs/SDL3/examples/audio/04-multiple-streams/README.txt @@ -0,0 +1,5 @@ +If you're running this in a web browser, you need to click the window before you'll hear anything! + +This example code loads two .wav files, puts them an audio streams and binds +them for playback, repeating both sounds on loop. This shows several streams +mixing into a single playback device. diff --git a/libs/SDL3/examples/audio/04-multiple-streams/multiple-streams.c b/libs/SDL3/examples/audio/04-multiple-streams/multiple-streams.c new file mode 100644 index 00000000..57f3cd0e --- /dev/null +++ b/libs/SDL3/examples/audio/04-multiple-streams/multiple-streams.c @@ -0,0 +1,135 @@ +/* + * This example code loads two .wav files, puts them an audio streams and + * binds them for playback, repeating both sounds on loop. This shows several + * streams mixing into a single playback device. + * + * This code is public domain. Feel free to use it for any purpose! + */ + +#define SDL_MAIN_USE_CALLBACKS 1 /* use the callbacks instead of main() */ +#include +#include + +/* We will use this renderer to draw into this window every frame. */ +static SDL_Window *window = NULL; +static SDL_Renderer *renderer = NULL; +static SDL_AudioDeviceID audio_device = 0; + +/* things that are playing sound (the audiostream itself, plus the original data, so we can refill to loop. */ +typedef struct Sound { + Uint8 *wav_data; + Uint32 wav_data_len; + SDL_AudioStream *stream; +} Sound; + +static Sound sounds[2]; + +static bool init_sound(const char *fname, Sound *sound) +{ + bool retval = false; + SDL_AudioSpec spec; + char *wav_path = NULL; + + /* Load the .wav files from wherever the app is being run from. */ + SDL_asprintf(&wav_path, "%s%s", SDL_GetBasePath(), fname); /* allocate a string of the full file path */ + if (!SDL_LoadWAV(wav_path, &spec, &sound->wav_data, &sound->wav_data_len)) { + SDL_Log("Couldn't load .wav file: %s", SDL_GetError()); + return false; + } + + /* Create an audio stream. Set the source format to the wav's format (what + we'll input), leave the dest format NULL here (it'll change to what the + device wants once we bind it). */ + sound->stream = SDL_CreateAudioStream(&spec, NULL); + if (!sound->stream) { + SDL_Log("Couldn't create audio stream: %s", SDL_GetError()); + } else if (!SDL_BindAudioStream(audio_device, sound->stream)) { /* once bound, it'll start playing when there is data available! */ + SDL_Log("Failed to bind '%s' stream to device: %s", fname, SDL_GetError()); + } else { + retval = true; /* success! */ + } + + SDL_free(wav_path); /* done with this string. */ + return retval; +} + + +/* This function runs once at startup. */ +SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[]) +{ + + SDL_SetAppMetadata("Example Audio Multiple Streams", "1.0", "com.example.audio-multiple-streams"); + + if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO)) { + SDL_Log("Couldn't initialize SDL: %s", SDL_GetError()); + return SDL_APP_FAILURE; + } + + if (!SDL_CreateWindowAndRenderer("examples/audio/multiple-streams", 640, 480, 0, &window, &renderer)) { + SDL_Log("Couldn't create window/renderer: %s", SDL_GetError()); + return SDL_APP_FAILURE; + } + + /* open the default audio device in whatever format it prefers; our audio streams will adjust to it. */ + audio_device = SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, NULL); + if (audio_device == 0) { + SDL_Log("Couldn't open audio device: %s", SDL_GetError()); + return SDL_APP_FAILURE; + } + + if (!init_sound("sample.wav", &sounds[0])) { + return SDL_APP_FAILURE; + } else if (!init_sound("sword.wav", &sounds[1])) { + return SDL_APP_FAILURE; + } + + return SDL_APP_CONTINUE; /* carry on with the program! */ +} + +/* This function runs when a new event (mouse input, keypresses, etc) occurs. */ +SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) +{ + if (event->type == SDL_EVENT_QUIT) { + return SDL_APP_SUCCESS; /* end the program, reporting success to the OS. */ + } + return SDL_APP_CONTINUE; /* carry on with the program! */ +} + +/* This function runs once per frame, and is the heart of the program. */ +SDL_AppResult SDL_AppIterate(void *appstate) +{ + int i; + + for (i = 0; i < SDL_arraysize(sounds); i++) { + /* If less than a full copy of the audio is queued for playback, put another copy in there. + This is overkill, but easy when lots of RAM is cheap. One could be more careful and + queue less at a time, as long as the stream doesn't run dry. */ + if (SDL_GetAudioStreamAvailable(sounds[i].stream) < ((int) sounds[i].wav_data_len)) { + SDL_PutAudioStreamData(sounds[i].stream, sounds[i].wav_data, (int) sounds[i].wav_data_len); + } + } + + /* just blank the screen. */ + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); + SDL_RenderClear(renderer); + SDL_RenderPresent(renderer); + + return SDL_APP_CONTINUE; /* carry on with the program! */ +} + +/* This function runs once at shutdown. */ +void SDL_AppQuit(void *appstate, SDL_AppResult result) +{ + int i; + + SDL_CloseAudioDevice(audio_device); + + for (i = 0; i < SDL_arraysize(sounds); i++) { + if (sounds[i].stream) { + SDL_DestroyAudioStream(sounds[i].stream); + } + SDL_free(sounds[i].wav_data); + } + + /* SDL will clean up the window/renderer for us. */ +} diff --git a/libs/SDL3/examples/audio/onmouseover.webp b/libs/SDL3/examples/audio/onmouseover.webp new file mode 100644 index 00000000..1f3ba4b2 Binary files /dev/null and b/libs/SDL3/examples/audio/onmouseover.webp differ diff --git a/libs/SDL3/examples/audio/thumbnail.png b/libs/SDL3/examples/audio/thumbnail.png new file mode 100644 index 00000000..10bc6c83 Binary files /dev/null and b/libs/SDL3/examples/audio/thumbnail.png differ diff --git a/libs/SDL3/examples/camera/01-read-and-draw/onmouseover.webp b/libs/SDL3/examples/camera/01-read-and-draw/onmouseover.webp new file mode 100644 index 00000000..1d414e66 Binary files /dev/null and b/libs/SDL3/examples/camera/01-read-and-draw/onmouseover.webp differ diff --git a/libs/SDL3/examples/camera/01-read-and-draw/thumbnail.png b/libs/SDL3/examples/camera/01-read-and-draw/thumbnail.png new file mode 100644 index 00000000..98fe4e07 Binary files /dev/null and b/libs/SDL3/examples/camera/01-read-and-draw/thumbnail.png differ diff --git a/libs/SDL3/examples/categories.txt b/libs/SDL3/examples/categories.txt new file mode 100644 index 00000000..8a9b5ecb --- /dev/null +++ b/libs/SDL3/examples/categories.txt @@ -0,0 +1,13 @@ +# Blank lines and lines that start with '#' in this file are ignored. + +# Categories, by directory name, go in here, in the order they should be +# listed on the main page. If this file is missing, it'll assume any +# subdirectory is a category and sort them alphabetically. + +renderer +input +audio +camera +asyncio +pen +demo diff --git a/libs/SDL3/examples/game/01-snake/README.txt b/libs/SDL3/examples/demo/01-snake/README.txt similarity index 100% rename from libs/SDL3/examples/game/01-snake/README.txt rename to libs/SDL3/examples/demo/01-snake/README.txt diff --git a/libs/SDL3/examples/demo/01-snake/onmouseover.webp b/libs/SDL3/examples/demo/01-snake/onmouseover.webp new file mode 100644 index 00000000..1757202c Binary files /dev/null and b/libs/SDL3/examples/demo/01-snake/onmouseover.webp differ diff --git a/libs/SDL3/examples/game/01-snake/snake.c b/libs/SDL3/examples/demo/01-snake/snake.c similarity index 98% rename from libs/SDL3/examples/game/01-snake/snake.c rename to libs/SDL3/examples/demo/01-snake/snake.c index 6df07a1f..08e62c03 100644 --- a/libs/SDL3/examples/game/01-snake/snake.c +++ b/libs/SDL3/examples/demo/01-snake/snake.c @@ -1,6 +1,6 @@ /* * Logic implementation of the Snake game. It is designed to efficiently - * represent in memory the state of the game. + * represent the state of the game in memory. * * This code is public domain. Feel free to use it for any purpose! */ @@ -316,7 +316,7 @@ SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[]) *appstate = as; - if (!SDL_CreateWindowAndRenderer("examples/game/snake", SDL_WINDOW_WIDTH, SDL_WINDOW_HEIGHT, 0, &as->window, &as->renderer)) { + if (!SDL_CreateWindowAndRenderer("examples/demo/snake", SDL_WINDOW_WIDTH, SDL_WINDOW_HEIGHT, 0, &as->window, &as->renderer)) { return SDL_APP_FAILURE; } diff --git a/libs/SDL3/examples/demo/01-snake/thumbnail.png b/libs/SDL3/examples/demo/01-snake/thumbnail.png new file mode 100644 index 00000000..f0e27c56 Binary files /dev/null and b/libs/SDL3/examples/demo/01-snake/thumbnail.png differ diff --git a/libs/SDL3/examples/game/02-woodeneye-008/README.txt b/libs/SDL3/examples/demo/02-woodeneye-008/README.txt similarity index 100% rename from libs/SDL3/examples/game/02-woodeneye-008/README.txt rename to libs/SDL3/examples/demo/02-woodeneye-008/README.txt diff --git a/libs/SDL3/examples/demo/02-woodeneye-008/onmouseover.webp b/libs/SDL3/examples/demo/02-woodeneye-008/onmouseover.webp new file mode 100644 index 00000000..2e7f44f9 Binary files /dev/null and b/libs/SDL3/examples/demo/02-woodeneye-008/onmouseover.webp differ diff --git a/libs/SDL3/examples/demo/02-woodeneye-008/thumbnail.png b/libs/SDL3/examples/demo/02-woodeneye-008/thumbnail.png new file mode 100644 index 00000000..c8d1efac Binary files /dev/null and b/libs/SDL3/examples/demo/02-woodeneye-008/thumbnail.png differ diff --git a/libs/SDL3/examples/game/02-woodeneye-008/woodeneye-008.c b/libs/SDL3/examples/demo/02-woodeneye-008/woodeneye-008.c similarity index 99% rename from libs/SDL3/examples/game/02-woodeneye-008/woodeneye-008.c rename to libs/SDL3/examples/demo/02-woodeneye-008/woodeneye-008.c index bb3c4d88..3ad0b45b 100644 --- a/libs/SDL3/examples/game/02-woodeneye-008/woodeneye-008.c +++ b/libs/SDL3/examples/demo/02-woodeneye-008/woodeneye-008.c @@ -347,7 +347,7 @@ SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[]) if (!SDL_Init(SDL_INIT_VIDEO)) { return SDL_APP_FAILURE; } - if (!SDL_CreateWindowAndRenderer("examples/game/woodeneye-008", 640, 480, 0, &as->window, &as->renderer)) { + if (!SDL_CreateWindowAndRenderer("examples/demo/woodeneye-008", 640, 480, 0, &as->window, &as->renderer)) { return SDL_APP_FAILURE; } diff --git a/libs/SDL3/examples/game/03-infinite-monkeys/README.txt b/libs/SDL3/examples/demo/03-infinite-monkeys/README.txt similarity index 100% rename from libs/SDL3/examples/game/03-infinite-monkeys/README.txt rename to libs/SDL3/examples/demo/03-infinite-monkeys/README.txt diff --git a/libs/SDL3/examples/game/03-infinite-monkeys/infinite-monkeys.c b/libs/SDL3/examples/demo/03-infinite-monkeys/infinite-monkeys.c similarity index 98% rename from libs/SDL3/examples/game/03-infinite-monkeys/infinite-monkeys.c rename to libs/SDL3/examples/demo/03-infinite-monkeys/infinite-monkeys.c index 99baf271..a8bfad1b 100644 --- a/libs/SDL3/examples/game/03-infinite-monkeys/infinite-monkeys.c +++ b/libs/SDL3/examples/demo/03-infinite-monkeys/infinite-monkeys.c @@ -139,7 +139,7 @@ SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[]) return SDL_APP_FAILURE; } - if (!SDL_CreateWindowAndRenderer("examples/game/03-infinite-monkeys", 640, 480, 0, &window, &renderer)) { + if (!SDL_CreateWindowAndRenderer("examples/demo/infinite-monkeys", 640, 480, 0, &window, &renderer)) { SDL_Log("Couldn't create window/renderer: %s", SDL_GetError()); return SDL_APP_FAILURE; } diff --git a/libs/SDL3/examples/demo/03-infinite-monkeys/onmouseover.webp b/libs/SDL3/examples/demo/03-infinite-monkeys/onmouseover.webp new file mode 100644 index 00000000..f5229747 Binary files /dev/null and b/libs/SDL3/examples/demo/03-infinite-monkeys/onmouseover.webp differ diff --git a/libs/SDL3/examples/demo/03-infinite-monkeys/thumbnail.png b/libs/SDL3/examples/demo/03-infinite-monkeys/thumbnail.png new file mode 100644 index 00000000..418390be Binary files /dev/null and b/libs/SDL3/examples/demo/03-infinite-monkeys/thumbnail.png differ diff --git a/libs/SDL3/examples/demo/04-bytepusher/README.txt b/libs/SDL3/examples/demo/04-bytepusher/README.txt new file mode 100644 index 00000000..dbee4ce3 --- /dev/null +++ b/libs/SDL3/examples/demo/04-bytepusher/README.txt @@ -0,0 +1,4 @@ +An implementation of the BytePusher VM + +For example programs and more information about BytePusher, see +https://esolangs.org/wiki/BytePusher diff --git a/libs/SDL3/examples/demo/04-bytepusher/bytepusher.c b/libs/SDL3/examples/demo/04-bytepusher/bytepusher.c new file mode 100644 index 00000000..ae862908 --- /dev/null +++ b/libs/SDL3/examples/demo/04-bytepusher/bytepusher.c @@ -0,0 +1,416 @@ +/* + * An implementation of the BytePusher VM. + * + * For example programs and more information about BytePusher, see + * https://esolangs.org/wiki/BytePusher + * + * This code is public domain. Feel free to use it for any purpose! + */ + +#define SDL_MAIN_USE_CALLBACKS +#include +#include +#include + +#define SCREEN_W 256 +#define SCREEN_H 256 +#define RAM_SIZE 0x1000000 +#define FRAMES_PER_SECOND 60 +#define SAMPLES_PER_FRAME 256 +#define NS_PER_SECOND (Uint64)SDL_NS_PER_SECOND +#define MAX_AUDIO_LATENCY_FRAMES 5 + +#define IO_KEYBOARD 0 +#define IO_PC 2 +#define IO_SCREEN_PAGE 5 +#define IO_AUDIO_BANK 6 + +typedef struct { + Uint8 ram[RAM_SIZE + 8]; + Uint8 screenbuf[SCREEN_W * SCREEN_H]; + Uint64 last_tick; + Uint64 tick_acc; + SDL_Window* window; + SDL_Renderer* renderer; + SDL_Surface* screen; + SDL_Texture* screentex; + SDL_Texture* rendertarget; /* we need this render target for text to look good */ + SDL_AudioStream* audiostream; + char status[SCREEN_W / 8]; + int status_ticks; + Uint16 keystate; + bool display_help; + bool positional_input; +} BytePusher; + +static const struct { + const char *key; + const char *value; +} extended_metadata[] = { + { SDL_PROP_APP_METADATA_URL_STRING, "https://examples.libsdl.org/SDL3/game/04-bytepusher/" }, + { SDL_PROP_APP_METADATA_CREATOR_STRING, "SDL team" }, + { SDL_PROP_APP_METADATA_COPYRIGHT_STRING, "Placed in the public domain" }, + { SDL_PROP_APP_METADATA_TYPE_STRING, "game" } +}; + +static inline Uint16 read_u16(const BytePusher* vm, Uint32 addr) { + const Uint8* ptr = &vm->ram[addr]; + return ((Uint16)ptr[0] << 8) | ((Uint16)ptr[1]); +} + +static inline Uint32 read_u24(const BytePusher* vm, Uint32 addr) { + const Uint8* ptr = &vm->ram[addr]; + return ((Uint32)ptr[0] << 16) | ((Uint32)ptr[1] << 8) | ((Uint32)ptr[2]); +} + +static void set_status(BytePusher* vm, const char* fmt, ...) { + va_list args; + va_start(args, fmt); + SDL_vsnprintf(vm->status, sizeof(vm->status), fmt, args); + va_end(args); + vm->status[sizeof(vm->status) - 1] = 0; + vm->status_ticks = FRAMES_PER_SECOND * 3; +} + +static bool load(BytePusher* vm, SDL_IOStream* stream, bool closeio) { + size_t bytes_read = 0; + bool ok = true; + + SDL_memset(vm->ram, 0, RAM_SIZE); + + if (!stream) { + return false; + } + + while (bytes_read < RAM_SIZE) { + size_t read = SDL_ReadIO(stream, &vm->ram[bytes_read], RAM_SIZE - bytes_read); + bytes_read += read; + if (read == 0) { + ok = SDL_GetIOStatus(stream) == SDL_IO_STATUS_EOF; + break; + } + } + if (closeio) { + SDL_CloseIO(stream); + } + + SDL_ClearAudioStream(vm->audiostream); + + vm->display_help = !ok; + return ok; +} + +static const char* filename(const char* path) { + size_t i = SDL_strlen(path) + 1; + while (i > 0) { + i -= 1; + if (path[i] == '/' || path[i] == '\\') { + return path + i + 1; + } + } + return path; +} + +static bool load_file(BytePusher* vm, const char* path) { + if (load(vm, SDL_IOFromFile(path, "rb"), true)) { + set_status(vm, "loaded %s", filename(path)); + return true; + } else { + set_status(vm, "load failed: %s", filename(path)); + return false; + } +} + +static void print(BytePusher* vm, int x, int y, const char* str) { + SDL_SetRenderDrawColor(vm->renderer, 0, 0, 0, SDL_ALPHA_OPAQUE); + SDL_RenderDebugText(vm->renderer, (float)(x + 1), (float)(y + 1), str); + SDL_SetRenderDrawColor(vm->renderer, 0xff, 0xff, 0xff, SDL_ALPHA_OPAQUE); + SDL_RenderDebugText(vm->renderer, (float)x, (float)y, str); + SDL_SetRenderDrawColor(vm->renderer, 0, 0, 0, SDL_ALPHA_OPAQUE); +} + +SDL_AppResult SDL_AppInit(void** appstate, int argc, char* argv[]) { + BytePusher* vm; + SDL_Palette* palette; + SDL_Rect usable_bounds; + SDL_AudioSpec audiospec = { SDL_AUDIO_S8, 1, SAMPLES_PER_FRAME * FRAMES_PER_SECOND }; + SDL_DisplayID primary_display; + SDL_PropertiesID texprops; + int zoom = 2; + int i; + Uint8 r, g, b; + (void)argc; + (void)argv; + + if (!SDL_SetAppMetadata("SDL 3 BytePusher", "1.0", "com.example.SDL3BytePusher")) { + return SDL_APP_FAILURE; + } + + for (i = 0; i < (int)SDL_arraysize(extended_metadata); i++) { + if (!SDL_SetAppMetadataProperty(extended_metadata[i].key, extended_metadata[i].value)) { + return SDL_APP_FAILURE; + } + } + + if (!SDL_Init(SDL_INIT_AUDIO | SDL_INIT_VIDEO)) { + return SDL_APP_FAILURE; + } + + if (!(vm = SDL_calloc(1, sizeof(*vm)))) { + return SDL_APP_FAILURE; + } + *(BytePusher**)appstate = vm; + + vm->display_help = true; + + primary_display = SDL_GetPrimaryDisplay(); + if (SDL_GetDisplayUsableBounds(primary_display, &usable_bounds)) { + int zoom_w = (usable_bounds.w - usable_bounds.x) * 2 / 3 / SCREEN_W; + int zoom_h = (usable_bounds.h - usable_bounds.y) * 2 / 3 / SCREEN_H; + zoom = zoom_w < zoom_h ? zoom_w : zoom_h; + if (zoom < 1) { + zoom = 1; + } + } + + if (!SDL_CreateWindowAndRenderer("SDL 3 BytePusher", + SCREEN_W * zoom, SCREEN_H * zoom, SDL_WINDOW_RESIZABLE, + &vm->window, &vm->renderer + )) { + return SDL_APP_FAILURE; + } + + if (!SDL_SetRenderLogicalPresentation( + vm->renderer, SCREEN_W, SCREEN_H, SDL_LOGICAL_PRESENTATION_INTEGER_SCALE + )) { + return SDL_APP_FAILURE; + } + + if (!(vm->screen = SDL_CreateSurfaceFrom( + SCREEN_W, SCREEN_H, SDL_PIXELFORMAT_INDEX8, vm->screenbuf, SCREEN_W + ))) { + return SDL_APP_FAILURE; + } + + if (!(palette = SDL_CreateSurfacePalette(vm->screen))) { + return SDL_APP_FAILURE; + } + i = 0; + for (r = 0; r < 6; ++r) { + for (g = 0; g < 6; ++g) { + for (b = 0; b < 6; ++b, ++i) { + SDL_Color color = { r * 0x33, g * 0x33, b * 0x33, SDL_ALPHA_OPAQUE }; + palette->colors[i] = color; + } + } + } + for (; i < 256; ++i) { + SDL_Color color = { 0, 0, 0, SDL_ALPHA_OPAQUE }; + palette->colors[i] = color; + } + + texprops = SDL_CreateProperties(); + SDL_SetNumberProperty(texprops, SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER, SDL_TEXTUREACCESS_STREAMING); + SDL_SetNumberProperty(texprops, SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER, SCREEN_W); + SDL_SetNumberProperty(texprops, SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER, SCREEN_H); + vm->screentex = SDL_CreateTextureWithProperties(vm->renderer, texprops); + SDL_SetNumberProperty(texprops, SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER, SDL_TEXTUREACCESS_TARGET); + vm->rendertarget = SDL_CreateTextureWithProperties(vm->renderer, texprops); + SDL_DestroyProperties(texprops); + if (!vm->screentex || !vm->rendertarget) { + return SDL_APP_FAILURE; + } + SDL_SetTextureScaleMode(vm->screentex, SDL_SCALEMODE_NEAREST); + SDL_SetTextureScaleMode(vm->rendertarget, SDL_SCALEMODE_NEAREST); + + if (!(vm->audiostream = SDL_OpenAudioDeviceStream( + SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &audiospec, NULL, NULL + ))) { + return SDL_APP_FAILURE; + } + SDL_SetAudioStreamGain(vm->audiostream, 0.1f); /* examples are loud! */ + SDL_ResumeAudioStreamDevice(vm->audiostream); + + set_status(vm, "renderer: %s", SDL_GetRendererName(vm->renderer)); + + vm->last_tick = SDL_GetTicksNS(); + vm->tick_acc = NS_PER_SECOND; + + return SDL_APP_CONTINUE; +} + +SDL_AppResult SDL_AppIterate(void* appstate) { + BytePusher* vm = (BytePusher*)appstate; + + Uint64 tick = SDL_GetTicksNS(); + Uint64 delta = tick - vm->last_tick; + bool updated, skip_audio; + + vm->last_tick = tick; + + vm->tick_acc += delta * FRAMES_PER_SECOND; + updated = vm->tick_acc >= NS_PER_SECOND; + skip_audio = vm->tick_acc >= MAX_AUDIO_LATENCY_FRAMES * NS_PER_SECOND; + + if (skip_audio) { + // don't let audio fall too far behind + SDL_ClearAudioStream(vm->audiostream); + } + + while (vm->tick_acc >= NS_PER_SECOND) { + Uint32 pc; + int i; + + vm->tick_acc -= NS_PER_SECOND; + + vm->ram[IO_KEYBOARD] = (Uint8)(vm->keystate >> 8); + vm->ram[IO_KEYBOARD + 1] = (Uint8)(vm->keystate); + + pc = read_u24(vm, IO_PC); + for (i = 0; i < SCREEN_W * SCREEN_H; ++i) { + Uint32 src = read_u24(vm, pc); + Uint32 dst = read_u24(vm, pc + 3); + vm->ram[dst] = vm->ram[src]; + pc = read_u24(vm, pc + 6); + } + + if (!skip_audio || vm->tick_acc < NS_PER_SECOND) { + SDL_PutAudioStreamData( + vm->audiostream, + &vm->ram[(Uint32)read_u16(vm, IO_AUDIO_BANK) << 8], + SAMPLES_PER_FRAME + ); + } + } + + if (updated) { + SDL_Surface *tex; + + SDL_SetRenderTarget(vm->renderer, vm->rendertarget); + + if (!SDL_LockTextureToSurface(vm->screentex, NULL, &tex)) { + return SDL_APP_FAILURE; + } + vm->screen->pixels = &vm->ram[(Uint32)vm->ram[IO_SCREEN_PAGE] << 16]; + SDL_BlitSurface(vm->screen, NULL, tex, NULL); + SDL_UnlockTexture(vm->screentex); + + SDL_RenderTexture(vm->renderer, vm->screentex, NULL, NULL); + } + + if (vm->display_help) { + print(vm, 4, 4, "Drop a BytePusher file in this"); + print(vm, 8, 12, "window to load and run it!"); + print(vm, 4, 28, "Press ENTER to switch between"); + print(vm, 8, 36, "positional and symbolic input."); + } + + if (vm->status_ticks > 0) { + vm->status_ticks -= 1; + print(vm, 4, SCREEN_H - 12, vm->status); + } + + SDL_SetRenderTarget(vm->renderer, NULL); + SDL_RenderClear(vm->renderer); + SDL_RenderTexture(vm->renderer, vm->rendertarget, NULL, NULL); + SDL_RenderPresent(vm->renderer); + + return SDL_APP_CONTINUE; +} + +static Uint16 keycode_mask(SDL_Keycode key) { + int index; + if (key >= SDLK_0 && key <= SDLK_9) { + index = key - SDLK_0; + } else if (key >= SDLK_A && key <= SDLK_F) { + index = key - SDLK_A + 10; + } else { + return 0; + } + return (Uint16)1 << index; +} + +static Uint16 scancode_mask(SDL_Scancode scancode) { + int index; + switch (scancode) { + case SDL_SCANCODE_1: index = 0x1; break; + case SDL_SCANCODE_2: index = 0x2; break; + case SDL_SCANCODE_3: index = 0x3; break; + case SDL_SCANCODE_4: index = 0xc; break; + case SDL_SCANCODE_Q: index = 0x4; break; + case SDL_SCANCODE_W: index = 0x5; break; + case SDL_SCANCODE_E: index = 0x6; break; + case SDL_SCANCODE_R: index = 0xd; break; + case SDL_SCANCODE_A: index = 0x7; break; + case SDL_SCANCODE_S: index = 0x8; break; + case SDL_SCANCODE_D: index = 0x9; break; + case SDL_SCANCODE_F: index = 0xe; break; + case SDL_SCANCODE_Z: index = 0xa; break; + case SDL_SCANCODE_X: index = 0x0; break; + case SDL_SCANCODE_C: index = 0xb; break; + case SDL_SCANCODE_V: index = 0xf; break; + default: return 0; + } + return (Uint16)1 << index; +} + +SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event) { + BytePusher* vm = (BytePusher*)appstate; + + switch (event->type) { + case SDL_EVENT_QUIT: + return SDL_APP_SUCCESS; + + case SDL_EVENT_DROP_FILE: + load_file(vm, event->drop.data); + break; + + case SDL_EVENT_KEY_DOWN: +#ifndef __EMSCRIPTEN__ + if (event->key.key == SDLK_ESCAPE) { + return SDL_APP_SUCCESS; + } +#endif + if (event->key.key == SDLK_RETURN) { + vm->positional_input = !vm->positional_input; + vm->keystate = 0; + if (vm->positional_input) { + set_status(vm, "switched to positional input"); + } else { + set_status(vm, "switched to symbolic input"); + } + } + if (vm->positional_input) { + vm->keystate |= scancode_mask(event->key.scancode); + } else { + vm->keystate |= keycode_mask(event->key.key); + } + break; + + case SDL_EVENT_KEY_UP: + if (vm->positional_input) { + vm->keystate &= ~scancode_mask(event->key.scancode); + } else { + vm->keystate &= ~keycode_mask(event->key.key); + } + break; + } + + return SDL_APP_CONTINUE; +} + +void SDL_AppQuit(void* appstate, SDL_AppResult result) { + if (result == SDL_APP_FAILURE) { + SDL_Log("Error: %s", SDL_GetError()); + } + if (appstate) { + BytePusher* vm = (BytePusher*)appstate; + SDL_DestroyAudioStream(vm->audiostream); + SDL_DestroyTexture(vm->rendertarget); + SDL_DestroyTexture(vm->screentex); + SDL_DestroySurface(vm->screen); + SDL_DestroyRenderer(vm->renderer); + SDL_DestroyWindow(vm->window); + SDL_free(vm); + } +} diff --git a/libs/SDL3/examples/demo/04-bytepusher/onmouseover.webp b/libs/SDL3/examples/demo/04-bytepusher/onmouseover.webp new file mode 100644 index 00000000..b99e7ddc Binary files /dev/null and b/libs/SDL3/examples/demo/04-bytepusher/onmouseover.webp differ diff --git a/libs/SDL3/examples/demo/04-bytepusher/thumbnail.png b/libs/SDL3/examples/demo/04-bytepusher/thumbnail.png new file mode 100644 index 00000000..891aa8f5 Binary files /dev/null and b/libs/SDL3/examples/demo/04-bytepusher/thumbnail.png differ diff --git a/libs/SDL3/examples/demo/description.txt b/libs/SDL3/examples/demo/description.txt new file mode 100644 index 00000000..77bcbb7c --- /dev/null +++ b/libs/SDL3/examples/demo/description.txt @@ -0,0 +1 @@ +Full game and app demos \ No newline at end of file diff --git a/libs/SDL3/examples/input/01-joystick-polling/README.txt b/libs/SDL3/examples/input/01-joystick-polling/README.txt new file mode 100644 index 00000000..89e2bee0 --- /dev/null +++ b/libs/SDL3/examples/input/01-joystick-polling/README.txt @@ -0,0 +1,2 @@ +This example code looks for the current joystick state once per frame, +and draws a visual representation of it. diff --git a/libs/SDL3/examples/input/01-joystick-polling/joystick-polling.c b/libs/SDL3/examples/input/01-joystick-polling/joystick-polling.c new file mode 100644 index 00000000..8f7a3156 --- /dev/null +++ b/libs/SDL3/examples/input/01-joystick-polling/joystick-polling.c @@ -0,0 +1,193 @@ +/* + * This example code looks for the current joystick state once per frame, + * and draws a visual representation of it. + * + * This code is public domain. Feel free to use it for any purpose! + */ + +/* Joysticks are low-level interfaces: there's something with a bunch of + buttons, axes and hats, in no understood order or position. This is + a flexible interface, but you'll need to build some sort of configuration + UI to let people tell you what button, etc, does what. On top of this + interface, SDL offers the "gamepad" API, which works with lots of devices, + and knows how to map arbitrary buttons and such to look like an + Xbox/PlayStation/etc gamepad. This is easier, and better, for many games, + but isn't necessarily a good fit for complex apps and hardware. A flight + simulator, a realistic racing game, etc, might want this interface instead + of gamepads. */ + +/* SDL can handle multiple joysticks, but for simplicity, this program only + deals with the first stick it sees. */ + +#define SDL_MAIN_USE_CALLBACKS 1 /* use the callbacks instead of main() */ +#include +#include + +/* We will use this renderer to draw into this window every frame. */ +static SDL_Window *window = NULL; +static SDL_Renderer *renderer = NULL; +static SDL_Joystick *joystick = NULL; +static SDL_Color colors[64]; + +/* This function runs once at startup. */ +SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[]) +{ + int i; + + SDL_SetAppMetadata("Example Input Joystick Polling", "1.0", "com.example.input-joystick-polling"); + + if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK)) { + SDL_Log("Couldn't initialize SDL: %s", SDL_GetError()); + return SDL_APP_FAILURE; + } + + if (!SDL_CreateWindowAndRenderer("examples/input/joystick-polling", 640, 480, 0, &window, &renderer)) { + SDL_Log("Couldn't create window/renderer: %s", SDL_GetError()); + return SDL_APP_FAILURE; + } + + for (i = 0; i < SDL_arraysize(colors); i++) { + colors[i].r = SDL_rand(255); + colors[i].g = SDL_rand(255); + colors[i].b = SDL_rand(255); + colors[i].a = 255; + } + + return SDL_APP_CONTINUE; /* carry on with the program! */ +} + +/* This function runs when a new event (mouse input, keypresses, etc) occurs. */ +SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) +{ + if (event->type == SDL_EVENT_QUIT) { + return SDL_APP_SUCCESS; /* end the program, reporting success to the OS. */ + } else if (event->type == SDL_EVENT_JOYSTICK_ADDED) { + /* this event is sent for each hotplugged stick, but also each already-connected joystick during SDL_Init(). */ + if (joystick == NULL) { /* we don't have a stick yet and one was added, open it! */ + joystick = SDL_OpenJoystick(event->jdevice.which); + if (!joystick) { + SDL_Log("Failed to open joystick ID %u: %s", (unsigned int) event->jdevice.which, SDL_GetError()); + } + } + } else if (event->type == SDL_EVENT_JOYSTICK_REMOVED) { + if (joystick && (SDL_GetJoystickID(joystick) == event->jdevice.which)) { + SDL_CloseJoystick(joystick); /* our joystick was unplugged. */ + joystick = NULL; + } + } + return SDL_APP_CONTINUE; /* carry on with the program! */ +} + +/* This function runs once per frame, and is the heart of the program. */ +SDL_AppResult SDL_AppIterate(void *appstate) +{ + int winw = 640, winh = 480; + const char *text = "Plug in a joystick, please."; + float x, y; + int i; + + if (joystick) { /* we have a stick opened? */ + text = SDL_GetJoystickName(joystick); + } + + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); + SDL_RenderClear(renderer); + SDL_GetWindowSize(window, &winw, &winh); + + /* note that you can get input as events, instead of polling, which is + better since it won't miss button presses if the system is lagging, + but often times checking the current state per-frame is good enough, + and maybe better if you'd rather _drop_ inputs due to lag. */ + + if (joystick) { /* we have a stick opened? */ + const float size = 30.0f; + int total; + + /* draw axes as bars going across middle of screen. We don't know if it's an X or Y or whatever axis, so we can't do more than this. */ + total = SDL_GetNumJoystickAxes(joystick); + y = (float) ((winh - (total * size)) / 2); + x = ((float) winw) / 2.0f; + for (i = 0; i < total; i++) { + const SDL_Color *color = &colors[i % SDL_arraysize(colors)]; + const float val = (((float) SDL_GetJoystickAxis(joystick, i)) / 32767.0f); /* make it -1.0f to 1.0f */ + const float dx = x + (val * x); + const SDL_FRect dst = { dx, y, x - SDL_fabsf(dx), size }; + SDL_SetRenderDrawColor(renderer, color->r, color->g, color->b, color->a); + SDL_RenderFillRect(renderer, &dst); + y += size; + } + + /* draw buttons as blocks across top of window. We only know the button numbers, but not where they are on the device. */ + total = SDL_GetNumJoystickButtons(joystick); + x = (float) ((winw - (total * size)) / 2); + for (i = 0; i < total; i++) { + const SDL_Color *color = &colors[i % SDL_arraysize(colors)]; + const SDL_FRect dst = { x, 0.0f, size, size }; + if (SDL_GetJoystickButton(joystick, i)) { + SDL_SetRenderDrawColor(renderer, color->r, color->g, color->b, color->a); + } else { + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); + } + SDL_RenderFillRect(renderer, &dst); + SDL_SetRenderDrawColor(renderer, 255, 255, 255, color->a); + SDL_RenderRect(renderer, &dst); /* outline it */ + x += size; + } + + /* draw hats across the bottom of the screen. */ + total = SDL_GetNumJoystickHats(joystick); + x = ((float) ((winw - (total * (size * 2.0f))) / 2.0f)) + (size / 2.0f); + y = ((float) winh) - size; + for (i = 0; i < total; i++) { + const SDL_Color *color = &colors[i % SDL_arraysize(colors)]; + const float thirdsize = size / 3.0f; + const SDL_FRect cross[] = { { x, y + thirdsize, size, thirdsize }, { x + thirdsize, y, thirdsize, size } }; + const Uint8 hat = SDL_GetJoystickHat(joystick, i); + + SDL_SetRenderDrawColor(renderer, 90, 90, 90, 255); + SDL_RenderFillRects(renderer, cross, SDL_arraysize(cross)); + + SDL_SetRenderDrawColor(renderer, color->r, color->g, color->b, color->a); + + if (hat & SDL_HAT_UP) { + const SDL_FRect dst = { x + thirdsize, y, thirdsize, thirdsize }; + SDL_RenderFillRect(renderer, &dst); + } + + if (hat & SDL_HAT_RIGHT) { + const SDL_FRect dst = { x + (thirdsize * 2), y + thirdsize, thirdsize, thirdsize }; + SDL_RenderFillRect(renderer, &dst); + } + + if (hat & SDL_HAT_DOWN) { + const SDL_FRect dst = { x + thirdsize, y + (thirdsize * 2), thirdsize, thirdsize }; + SDL_RenderFillRect(renderer, &dst); + } + + if (hat & SDL_HAT_LEFT) { + const SDL_FRect dst = { x, y + thirdsize, thirdsize, thirdsize }; + SDL_RenderFillRect(renderer, &dst); + } + + x += size * 2; + } + } + + x = (((float) winw) - (SDL_strlen(text) * SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE)) / 2.0f; + y = (((float) winh) - SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE) / 2.0f; + SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); + SDL_RenderDebugText(renderer, x, y, text); + SDL_RenderPresent(renderer); + + return SDL_APP_CONTINUE; /* carry on with the program! */ +} + +/* This function runs once at shutdown. */ +void SDL_AppQuit(void *appstate, SDL_AppResult result) +{ + if (joystick) { + SDL_CloseJoystick(joystick); + } + + /* SDL will clean up the window/renderer for us. */ +} diff --git a/libs/SDL3/examples/input/01-joystick-polling/onmouseover.webp b/libs/SDL3/examples/input/01-joystick-polling/onmouseover.webp new file mode 100644 index 00000000..484539c3 Binary files /dev/null and b/libs/SDL3/examples/input/01-joystick-polling/onmouseover.webp differ diff --git a/libs/SDL3/examples/input/01-joystick-polling/thumbnail.png b/libs/SDL3/examples/input/01-joystick-polling/thumbnail.png new file mode 100644 index 00000000..4faebbad Binary files /dev/null and b/libs/SDL3/examples/input/01-joystick-polling/thumbnail.png differ diff --git a/libs/SDL3/examples/input/02-joystick-events/README.txt b/libs/SDL3/examples/input/02-joystick-events/README.txt new file mode 100644 index 00000000..d87ac0a0 --- /dev/null +++ b/libs/SDL3/examples/input/02-joystick-events/README.txt @@ -0,0 +1,2 @@ +This example code looks for joystick input in the event handler, and +reports any changes as a flood of info. diff --git a/libs/SDL3/examples/input/02-joystick-events/joystick-events.c b/libs/SDL3/examples/input/02-joystick-events/joystick-events.c new file mode 100644 index 00000000..cc01d84a --- /dev/null +++ b/libs/SDL3/examples/input/02-joystick-events/joystick-events.c @@ -0,0 +1,232 @@ +/* + * This example code looks for joystick input in the event handler, and + * reports any changes as a flood of info. + * + * This code is public domain. Feel free to use it for any purpose! + */ + +/* Joysticks are low-level interfaces: there's something with a bunch of + buttons, axes and hats, in no understood order or position. This is + a flexible interface, but you'll need to build some sort of configuration + UI to let people tell you what button, etc, does what. On top of this + interface, SDL offers the "gamepad" API, which works with lots of devices, + and knows how to map arbitrary buttons and such to look like an + Xbox/PlayStation/etc gamepad. This is easier, and better, for many games, + but isn't necessarily a good fit for complex apps and hardware. A flight + simulator, a realistic racing game, etc, might want this interface instead + of gamepads. */ + +#define SDL_MAIN_USE_CALLBACKS 1 /* use the callbacks instead of main() */ +#include +#include + +/* We will use this renderer to draw into this window every frame. */ +static SDL_Window *window = NULL; +static SDL_Renderer *renderer = NULL; +static SDL_Color colors[64]; + +#define MOTION_EVENT_COOLDOWN 40 + +typedef struct EventMessage +{ + char *str; + SDL_Color color; + Uint64 start_ticks; + struct EventMessage *next; +} EventMessage; + +static EventMessage messages; +static EventMessage *messages_tail = &messages; + +static const char *hat_state_string(Uint8 state) +{ + switch (state) { + case SDL_HAT_CENTERED: return "CENTERED"; + case SDL_HAT_UP: return "UP"; + case SDL_HAT_RIGHT: return "RIGHT"; + case SDL_HAT_DOWN: return "DOWN"; + case SDL_HAT_LEFT: return "LEFT"; + case SDL_HAT_RIGHTUP: return "RIGHT+UP"; + case SDL_HAT_RIGHTDOWN: return "RIGHT+DOWN"; + case SDL_HAT_LEFTUP: return "LEFT+UP"; + case SDL_HAT_LEFTDOWN: return "LEFT+DOWN"; + default: break; + } + return "UNKNOWN"; +} + +static const char *battery_state_string(SDL_PowerState state) +{ + switch (state) { + case SDL_POWERSTATE_ERROR: return "ERROR"; + case SDL_POWERSTATE_UNKNOWN: return "UNKNOWN"; + case SDL_POWERSTATE_ON_BATTERY: return "ON BATTERY"; + case SDL_POWERSTATE_NO_BATTERY: return "NO BATTERY"; + case SDL_POWERSTATE_CHARGING: return "CHARGING"; + case SDL_POWERSTATE_CHARGED: return "CHARGED"; + default: break; + } + return "UNKNOWN"; +} + +static void add_message(SDL_JoystickID jid, const char *fmt, ...) +{ + const SDL_Color *color = &colors[((size_t) jid) % SDL_arraysize(colors)]; + EventMessage *msg = NULL; + char *str = NULL; + va_list ap; + + msg = (EventMessage *) SDL_calloc(1, sizeof (*msg)); + if (!msg) { + return; // oh well. + } + + va_start(ap, fmt); + SDL_vasprintf(&str, fmt, ap); + va_end(ap); + if (!str) { + SDL_free(msg); + return; // oh well. + } + + msg->str = str; + SDL_copyp(&msg->color, color); + msg->start_ticks = SDL_GetTicks(); + msg->next = NULL; + + messages_tail->next = msg; + messages_tail = msg; +} + + +/* This function runs once at startup. */ +SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[]) +{ + int i; + + SDL_SetAppMetadata("Example Input Joystick Events", "1.0", "com.example.input-joystick-events"); + + if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK)) { + SDL_Log("Couldn't initialize SDL: %s", SDL_GetError()); + return SDL_APP_FAILURE; + } + + if (!SDL_CreateWindowAndRenderer("examples/input/joystick-events", 640, 480, 0, &window, &renderer)) { + SDL_Log("Couldn't create window/renderer: %s", SDL_GetError()); + return SDL_APP_FAILURE; + } + + colors[0].r = colors[0].g = colors[0].b = colors[0].a = 255; + for (i = 1; i < SDL_arraysize(colors); i++) { + colors[i].r = SDL_rand(255); + colors[i].g = SDL_rand(255); + colors[i].b = SDL_rand(255); + colors[i].a = 255; + } + + add_message(0, "Please plug in a joystick."); + + return SDL_APP_CONTINUE; /* carry on with the program! */ +} + +/* This function runs when a new event (mouse input, keypresses, etc) occurs. */ +SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) +{ + if (event->type == SDL_EVENT_QUIT) { + return SDL_APP_SUCCESS; /* end the program, reporting success to the OS. */ + } else if (event->type == SDL_EVENT_JOYSTICK_ADDED) { + /* this event is sent for each hotplugged stick, but also each already-connected joystick during SDL_Init(). */ + const SDL_JoystickID which = event->jdevice.which; + SDL_Joystick *joystick = SDL_OpenJoystick(which); + if (!joystick) { + add_message(which, "Joystick #%u add, but not opened: %s", (unsigned int) which, SDL_GetError()); + } else { + add_message(which, "Joystick #%u ('%s') added", (unsigned int) which, SDL_GetJoystickName(joystick)); + } + } else if (event->type == SDL_EVENT_JOYSTICK_REMOVED) { + const SDL_JoystickID which = event->jdevice.which; + SDL_Joystick *joystick = SDL_GetJoystickFromID(which); + if (joystick) { + SDL_CloseJoystick(joystick); /* the joystick was unplugged. */ + } + add_message(which, "Joystick #%u removed", (unsigned int) which); + } else if (event->type == SDL_EVENT_JOYSTICK_AXIS_MOTION) { + static Uint64 axis_motion_cooldown_time = 0; /* these are spammy, only show every X milliseconds. */ + const Uint64 now = SDL_GetTicks(); + if (now >= axis_motion_cooldown_time) { + const SDL_JoystickID which = event->jaxis.which; + axis_motion_cooldown_time = now + MOTION_EVENT_COOLDOWN; + add_message(which, "Joystick #%u axis %d -> %d", (unsigned int) which, (int) event->jaxis.axis, (int) event->jaxis.value); + } + } else if (event->type == SDL_EVENT_JOYSTICK_BALL_MOTION) { + static Uint64 ball_motion_cooldown_time = 0; /* these are spammy, only show every X milliseconds. */ + const Uint64 now = SDL_GetTicks(); + if (now >= ball_motion_cooldown_time) { + const SDL_JoystickID which = event->jball.which; + ball_motion_cooldown_time = now + MOTION_EVENT_COOLDOWN; + add_message(which, "Joystick #%u ball %d -> %d, %d", (unsigned int) which, (int) event->jball.ball, (int) event->jball.xrel, (int) event->jball.yrel); + } + } else if (event->type == SDL_EVENT_JOYSTICK_HAT_MOTION) { + const SDL_JoystickID which = event->jhat.which; + add_message(which, "Joystick #%u hat %d -> %s", (unsigned int) which, (int) event->jhat.hat, hat_state_string(event->jhat.value)); + } else if ((event->type == SDL_EVENT_JOYSTICK_BUTTON_UP) || (event->type == SDL_EVENT_JOYSTICK_BUTTON_DOWN)) { + const SDL_JoystickID which = event->jbutton.which; + add_message(which, "Joystick #%u button %d -> %s", (unsigned int) which, (int) event->jbutton.button, event->jbutton.down ? "PRESSED" : "RELEASED"); + } else if (event->type == SDL_EVENT_JOYSTICK_BATTERY_UPDATED) { + const SDL_JoystickID which = event->jbattery.which; + add_message(which, "Joystick #%u battery -> %s - %d%%", (unsigned int) which, battery_state_string(event->jbattery.state), event->jbattery.percent); + } + + return SDL_APP_CONTINUE; /* carry on with the program! */ +} + +/* This function runs once per frame, and is the heart of the program. */ +SDL_AppResult SDL_AppIterate(void *appstate) +{ + const Uint64 now = SDL_GetTicks(); + const float msg_lifetime = 3500.0f; /* milliseconds a message lives for. */ + EventMessage *msg = messages.next; + float prev_y = 0.0f; + int winw = 640, winh = 480; + + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); + SDL_RenderClear(renderer); + SDL_GetWindowSize(window, &winw, &winh); + + while (msg) { + float x, y; + const float life_percent = ((float) (now - msg->start_ticks)) / msg_lifetime; + if (life_percent >= 1.0f) { /* msg is done. */ + messages.next = msg->next; + if (messages_tail == msg) { + messages_tail = &messages; + } + SDL_free(msg->str); + SDL_free(msg); + msg = messages.next; + continue; + } + x = (((float) winw) - (SDL_strlen(msg->str) * SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE)) / 2.0f; + y = ((float) winh) * life_percent; + if ((prev_y != 0.0f) && ((prev_y - y) < ((float) SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE))) { + msg->start_ticks = now; + break; // wait for the previous message to tick up a little. + } + + SDL_SetRenderDrawColor(renderer, msg->color.r, msg->color.g, msg->color.b, (Uint8) (((float) msg->color.a) * (1.0f - life_percent))); + SDL_RenderDebugText(renderer, x, y, msg->str); + + prev_y = y; + msg = msg->next; + } + + SDL_RenderPresent(renderer); + + return SDL_APP_CONTINUE; /* carry on with the program! */ +} + +/* This function runs once at shutdown. */ +void SDL_AppQuit(void *appstate, SDL_AppResult result) +{ + /* SDL will clean up the window/renderer for us. We let the joysticks leak. */ +} diff --git a/libs/SDL3/examples/input/02-joystick-events/onmouseover.webp b/libs/SDL3/examples/input/02-joystick-events/onmouseover.webp new file mode 100644 index 00000000..05a9b426 Binary files /dev/null and b/libs/SDL3/examples/input/02-joystick-events/onmouseover.webp differ diff --git a/libs/SDL3/examples/input/02-joystick-events/thumbnail.png b/libs/SDL3/examples/input/02-joystick-events/thumbnail.png new file mode 100644 index 00000000..07f3ff15 Binary files /dev/null and b/libs/SDL3/examples/input/02-joystick-events/thumbnail.png differ diff --git a/libs/SDL3/examples/pen/01-drawing-lines/onmouseover.webp b/libs/SDL3/examples/pen/01-drawing-lines/onmouseover.webp new file mode 100644 index 00000000..f9c4d3dc Binary files /dev/null and b/libs/SDL3/examples/pen/01-drawing-lines/onmouseover.webp differ diff --git a/libs/SDL3/examples/pen/01-drawing-lines/thumbnail.png b/libs/SDL3/examples/pen/01-drawing-lines/thumbnail.png new file mode 100644 index 00000000..3403d357 Binary files /dev/null and b/libs/SDL3/examples/pen/01-drawing-lines/thumbnail.png differ diff --git a/libs/SDL3/examples/renderer/01-clear/onmouseover.webp b/libs/SDL3/examples/renderer/01-clear/onmouseover.webp new file mode 100644 index 00000000..a0062fe0 Binary files /dev/null and b/libs/SDL3/examples/renderer/01-clear/onmouseover.webp differ diff --git a/libs/SDL3/examples/renderer/01-clear/thumbnail.png b/libs/SDL3/examples/renderer/01-clear/thumbnail.png new file mode 100644 index 00000000..b255675d Binary files /dev/null and b/libs/SDL3/examples/renderer/01-clear/thumbnail.png differ diff --git a/libs/SDL3/examples/renderer/02-primitives/thumbnail.png b/libs/SDL3/examples/renderer/02-primitives/thumbnail.png new file mode 100644 index 00000000..4ddf2ab8 Binary files /dev/null and b/libs/SDL3/examples/renderer/02-primitives/thumbnail.png differ diff --git a/libs/SDL3/examples/renderer/03-lines/onmouseover.webp b/libs/SDL3/examples/renderer/03-lines/onmouseover.webp new file mode 100644 index 00000000..5d3b3fce Binary files /dev/null and b/libs/SDL3/examples/renderer/03-lines/onmouseover.webp differ diff --git a/libs/SDL3/examples/renderer/03-lines/thumbnail.png b/libs/SDL3/examples/renderer/03-lines/thumbnail.png new file mode 100644 index 00000000..9d0ff10b Binary files /dev/null and b/libs/SDL3/examples/renderer/03-lines/thumbnail.png differ diff --git a/libs/SDL3/examples/renderer/04-points/onmouseover.webp b/libs/SDL3/examples/renderer/04-points/onmouseover.webp new file mode 100644 index 00000000..04582da5 Binary files /dev/null and b/libs/SDL3/examples/renderer/04-points/onmouseover.webp differ diff --git a/libs/SDL3/examples/renderer/04-points/thumbnail.png b/libs/SDL3/examples/renderer/04-points/thumbnail.png new file mode 100644 index 00000000..56271136 Binary files /dev/null and b/libs/SDL3/examples/renderer/04-points/thumbnail.png differ diff --git a/libs/SDL3/examples/renderer/05-rectangles/onmouseover.webp b/libs/SDL3/examples/renderer/05-rectangles/onmouseover.webp new file mode 100644 index 00000000..cdfd376e Binary files /dev/null and b/libs/SDL3/examples/renderer/05-rectangles/onmouseover.webp differ diff --git a/libs/SDL3/examples/renderer/05-rectangles/thumbnail.png b/libs/SDL3/examples/renderer/05-rectangles/thumbnail.png new file mode 100644 index 00000000..64e66882 Binary files /dev/null and b/libs/SDL3/examples/renderer/05-rectangles/thumbnail.png differ diff --git a/libs/SDL3/examples/renderer/06-textures/onmouseover.webp b/libs/SDL3/examples/renderer/06-textures/onmouseover.webp new file mode 100644 index 00000000..467afd85 Binary files /dev/null and b/libs/SDL3/examples/renderer/06-textures/onmouseover.webp differ diff --git a/libs/SDL3/examples/renderer/06-textures/thumbnail.png b/libs/SDL3/examples/renderer/06-textures/thumbnail.png new file mode 100644 index 00000000..b33ba317 Binary files /dev/null and b/libs/SDL3/examples/renderer/06-textures/thumbnail.png differ diff --git a/libs/SDL3/examples/renderer/07-streaming-textures/onmouseover.webp b/libs/SDL3/examples/renderer/07-streaming-textures/onmouseover.webp new file mode 100644 index 00000000..7c296936 Binary files /dev/null and b/libs/SDL3/examples/renderer/07-streaming-textures/onmouseover.webp differ diff --git a/libs/SDL3/examples/renderer/07-streaming-textures/thumbnail.png b/libs/SDL3/examples/renderer/07-streaming-textures/thumbnail.png new file mode 100644 index 00000000..60c2a9f2 Binary files /dev/null and b/libs/SDL3/examples/renderer/07-streaming-textures/thumbnail.png differ diff --git a/libs/SDL3/examples/renderer/08-rotating-textures/onmouseover.webp b/libs/SDL3/examples/renderer/08-rotating-textures/onmouseover.webp new file mode 100644 index 00000000..69735cee Binary files /dev/null and b/libs/SDL3/examples/renderer/08-rotating-textures/onmouseover.webp differ diff --git a/libs/SDL3/examples/renderer/08-rotating-textures/thumbnail.png b/libs/SDL3/examples/renderer/08-rotating-textures/thumbnail.png new file mode 100644 index 00000000..12c51e17 Binary files /dev/null and b/libs/SDL3/examples/renderer/08-rotating-textures/thumbnail.png differ diff --git a/libs/SDL3/examples/renderer/09-scaling-textures/onmouseover.webp b/libs/SDL3/examples/renderer/09-scaling-textures/onmouseover.webp new file mode 100644 index 00000000..bcc967c9 Binary files /dev/null and b/libs/SDL3/examples/renderer/09-scaling-textures/onmouseover.webp differ diff --git a/libs/SDL3/examples/renderer/09-scaling-textures/thumbnail.png b/libs/SDL3/examples/renderer/09-scaling-textures/thumbnail.png new file mode 100644 index 00000000..c0a24c20 Binary files /dev/null and b/libs/SDL3/examples/renderer/09-scaling-textures/thumbnail.png differ diff --git a/libs/SDL3/examples/renderer/10-geometry/onmouseover.webp b/libs/SDL3/examples/renderer/10-geometry/onmouseover.webp new file mode 100644 index 00000000..37a518c3 Binary files /dev/null and b/libs/SDL3/examples/renderer/10-geometry/onmouseover.webp differ diff --git a/libs/SDL3/examples/renderer/10-geometry/thumbnail.png b/libs/SDL3/examples/renderer/10-geometry/thumbnail.png new file mode 100644 index 00000000..89195fba Binary files /dev/null and b/libs/SDL3/examples/renderer/10-geometry/thumbnail.png differ diff --git a/libs/SDL3/examples/renderer/11-color-mods/onmouseover.webp b/libs/SDL3/examples/renderer/11-color-mods/onmouseover.webp new file mode 100644 index 00000000..2157063b Binary files /dev/null and b/libs/SDL3/examples/renderer/11-color-mods/onmouseover.webp differ diff --git a/libs/SDL3/examples/renderer/11-color-mods/thumbnail.png b/libs/SDL3/examples/renderer/11-color-mods/thumbnail.png new file mode 100644 index 00000000..d4711128 Binary files /dev/null and b/libs/SDL3/examples/renderer/11-color-mods/thumbnail.png differ diff --git a/libs/SDL3/examples/renderer/14-viewport/thumbnail.png b/libs/SDL3/examples/renderer/14-viewport/thumbnail.png new file mode 100644 index 00000000..bad55214 Binary files /dev/null and b/libs/SDL3/examples/renderer/14-viewport/thumbnail.png differ diff --git a/libs/SDL3/examples/renderer/15-cliprect/onmouseover.webp b/libs/SDL3/examples/renderer/15-cliprect/onmouseover.webp new file mode 100644 index 00000000..943eeef7 Binary files /dev/null and b/libs/SDL3/examples/renderer/15-cliprect/onmouseover.webp differ diff --git a/libs/SDL3/examples/renderer/15-cliprect/thumbnail.png b/libs/SDL3/examples/renderer/15-cliprect/thumbnail.png new file mode 100644 index 00000000..127e6fa0 Binary files /dev/null and b/libs/SDL3/examples/renderer/15-cliprect/thumbnail.png differ diff --git a/libs/SDL3/examples/renderer/17-read-pixels/onmouseover.webp b/libs/SDL3/examples/renderer/17-read-pixels/onmouseover.webp new file mode 100644 index 00000000..bb4e5c4a Binary files /dev/null and b/libs/SDL3/examples/renderer/17-read-pixels/onmouseover.webp differ diff --git a/libs/SDL3/examples/renderer/17-read-pixels/thumbnail.png b/libs/SDL3/examples/renderer/17-read-pixels/thumbnail.png new file mode 100644 index 00000000..8da02ac7 Binary files /dev/null and b/libs/SDL3/examples/renderer/17-read-pixels/thumbnail.png differ diff --git a/libs/SDL3/examples/renderer/18-debug-text/debug-text.c b/libs/SDL3/examples/renderer/18-debug-text/debug-text.c index 85bc66ca..62005e6b 100644 --- a/libs/SDL3/examples/renderer/18-debug-text/debug-text.c +++ b/libs/SDL3/examples/renderer/18-debug-text/debug-text.c @@ -46,6 +46,8 @@ SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) /* This function runs once per frame, and is the heart of the program. */ SDL_AppResult SDL_AppIterate(void *appstate) { + const int charsize = SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE; + /* as you can see from this, rendering draws over whatever was drawn before it. */ SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE); /* black, full alpha */ SDL_RenderClear(renderer); /* start with a blank canvas. */ @@ -62,6 +64,9 @@ SDL_AppResult SDL_AppIterate(void *appstate) SDL_RenderDebugText(renderer, 14, 65, "It can be scaled."); SDL_SetRenderScale(renderer, 1.0f, 1.0f); SDL_RenderDebugText(renderer, 64, 350, "This only does ASCII chars. So this laughing emoji won't draw: 🤣"); + + SDL_RenderDebugTextFormat(renderer, (float) ((WINDOW_WIDTH - (charsize * 46)) / 2), 400, "(This program has been running for %" SDL_PRIu64 " seconds.)", SDL_GetTicks() / 1000); + SDL_RenderPresent(renderer); /* put it all on the screen! */ return SDL_APP_CONTINUE; /* carry on with the program! */ diff --git a/libs/SDL3/examples/renderer/18-debug-text/thumbnail.png b/libs/SDL3/examples/renderer/18-debug-text/thumbnail.png new file mode 100644 index 00000000..f08e4691 Binary files /dev/null and b/libs/SDL3/examples/renderer/18-debug-text/thumbnail.png differ diff --git a/libs/SDL3/examples/template-category.html b/libs/SDL3/examples/template-category.html new file mode 100644 index 00000000..909b2c63 --- /dev/null +++ b/libs/SDL3/examples/template-category.html @@ -0,0 +1,41 @@ + + + + + + + @project_name@ Examples: @category_description@ + + + + + + + + + + + +
+ @project_name@ Examples +
+
+ +

@project_name@ examples: @category_description@

+
@examples_list_html@
+
+ + diff --git a/libs/SDL3/examples/template-homepage.html b/libs/SDL3/examples/template-homepage.html new file mode 100644 index 00000000..46951b28 --- /dev/null +++ b/libs/SDL3/examples/template-homepage.html @@ -0,0 +1,41 @@ + + + + + + + @project_name@ Examples + + + + + + + + + + + +
+ SDL Examples +
+
+ +

@project_name@ examples

+ + @homepage_list_html@ +
+ + diff --git a/libs/SDL3/examples/template-placeholder.png b/libs/SDL3/examples/template-placeholder.png new file mode 100644 index 00000000..202df959 Binary files /dev/null and b/libs/SDL3/examples/template-placeholder.png differ diff --git a/libs/SDL3/examples/template.css b/libs/SDL3/examples/template.css new file mode 100644 index 00000000..8e86723c --- /dev/null +++ b/libs/SDL3/examples/template.css @@ -0,0 +1,284 @@ +/** from ghwikipp.css */ +:root { + color-scheme: dark light; /* both supported */ +} + +body { + background-color: white; + padding: 2vw; + color: #333; + max-width: 1200px; + margin: 0 auto; + font-size: 16px; + line-height: 1.5; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", + Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; + overflow-wrap: break-word; +} + +a { + color: #0969da; + /* text-decoration: none; */ +} + +a:visited { + color: #064998; +} + +h1 { + border-bottom: 2px solid #efefef; +} + +h2 { + border-bottom: 1px solid #efefef; +} + +p { + max-width: 85ch; +} + +li { + max-width: 85ch; +} + +div.sourceCode { + background-color: #f6f8fa; + max-width: 100%; + padding: 16px; +} + +code { + background-color: #f6f8fa; + padding: 0px; + font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, + "Liberation Mono", monospace; +} + +table { + border: 1px solid #808080; + border-collapse: collapse; +} + +td { + border: 1px solid #808080; + padding: 5px; +} + +tr:nth-child(even) { + background-color: #f6f8fa; +} + +.wikitopbanner { + background-color: #efefef; + padding: 10px; + margin-bottom: 10px; + width: auto; +} + +.wikibottombanner { + background-color: #efefef; + padding: 10px; + margin-top: 10px; + width: auto; +} + +.alertBox { + background-color: #f8d7da; + border: 1px solid #f5c6cb; + max-width: 60%; + padding: 10; + margin: auto; +} + +.anchorImage { + visibility: hidden; + padding-left: 0.2em; + color: #fff; +} + +.anchorText:hover .anchorImage { + visibility: visible; +} + +hr { + display: block; + height: 1px; + border: 0; + border-top: 1px solid #efefef; + margin: 1em 0; + padding: 0; +} + +/* Text and background color for dark mode */ +@media (prefers-color-scheme: dark) { + body { + color: #e6edf3; + background-color: #0d1117; + } + + h1 { + border-color: rgba(48, 54, 61, 0.7); + } + + h2 { + border-color: rgba(48, 54, 61, 0.7); + } + + hr { + border-color: rgba(48, 54, 61, 0.7); + } + + div.sourceCode { + background-color: #161b22; + } + + code { + background-color: #161b22; + } + + a { + color: #4493f8; + } + + a:visited { + color: #2f66ad; + } + + table { + border-color: rgba(48, 54, 61, 0.7); + } + + td { + border-color: rgba(48, 54, 61, 0.7); + } + + tr:nth-child(even) { + background-color: #161b22; + } + + .wikitopbanner { + background-color: #263040; + } + + .wikibottombanner { + background-color: #263040; + } + + .anchorText:hover .anchorImage { + filter: invert(100%); + } +} + +@media print { + body { + font-size: 12px; + } + + table { + font-size: inherit; + } + + a:visited { + color: #0969da; + } + + .wikitopbanner, + .anchorText, + .wikibottombanner { + display: none; + } +} + +/** additional (& overrides) for examples */ +header { + background-color: #efefef; + padding: 10px; + font-size: 2rem; +} + +header > a, +header > a:hover, +header > a:visited { + color: inherit; + text-decoration: none; +} + +.breadcrumb { + padding: 0.75rem 0.75rem; +} + +.breadcrumb ul { + display: flex; + flex-wrap: wrap; + list-style: none; + margin: 0; + padding: 0; +} + +.breadcrumb li:not(:last-child)::after { + display: inline-block; + margin: 0 0.25rem; + content: "»"; +} + +.list { + display: flex; + flex-flow: row wrap; + gap: 24px; +} + +.list > a > div { + width: 200px; + border: 5px solid #efefef; + border-radius: 5px; + background: #efefef; + + display: flex; + flex-flow: column nowrap; + + transition: border 0.25s; +} + +.list > a > div:hover { + border-color: #064998; +} + +.list > a > div > img { + width: 100%; + border-radius: 5px; +} + +.list > a > div > div { + text-align: center; +} + +.list > a, +.list > a:visited { + display: block; + color: inherit; + text-decoration: none; +} +.list > a:hover { + color: #0969da; +} + +@media (prefers-color-scheme: dark) { + header { + background-color: #263040; + } + + .breadcrumb li:not(:last-child)::after { + color: #efefef; + } + + .list > a > div { + border-color: #333; + background: #333; + } +} + +@media only screen and (max-width: 992px) { + .list > a > div { + width: 150px; + } +} diff --git a/libs/SDL3/examples/template.html b/libs/SDL3/examples/template.html index e8be678b..d6a73050 100644 --- a/libs/SDL3/examples/template.html +++ b/libs/SDL3/examples/template.html @@ -3,35 +3,64 @@ + @project_name@ Example: @category_name@/@example_name@ + + + + + + + + -
- -
-
- @description@ -
+
+ SDL Examples +
+
+
+ +

@project_name@ example: @category_name@/@example_name@

+
@description@
+
+ +
+
+ +
+
diff --git a/libs/SDL3/include/SDL3/SDL.h b/libs/SDL3/include/SDL3/SDL.h index 53c298b9..0b4388eb 100644 --- a/libs/SDL3/include/SDL3/SDL.h +++ b/libs/SDL3/include/SDL3/SDL.h @@ -20,9 +20,12 @@ */ /** - * \file SDL.h + * Main include header for the SDL library, version 3.1.7 * - * Main include header for the SDL library, version 3.1.6 + * It is almost always best to include just this one header instead of + * picking out individual headers included here. There are exceptions to + * this rule--SDL_main.h is special and not included here--but usually + * letting SDL.h include the kitchen sink for you is the correct approach. */ #ifndef SDL_h_ @@ -30,6 +33,7 @@ #include #include +#include #include #include #include @@ -77,6 +81,7 @@ #include #include #include +#include #include #include #include diff --git a/libs/SDL3/include/SDL3/SDL_assert.h b/libs/SDL3/include/SDL3/SDL_assert.h index d09e46c0..aeaa6223 100644 --- a/libs/SDL3/include/SDL3/SDL_assert.h +++ b/libs/SDL3/include/SDL3/SDL_assert.h @@ -51,9 +51,14 @@ * - It provides statistics and data on all failed assertions to the app. * - It allows the default assertion handler to be controlled with environment * variables, in case an automated script needs to control it. + * - It can be used as an aid to Clang's static analysis; it will treat SDL + * assertions as universally true (under the assumption that you are serious + * about the asserted claims and that your debug builds will detect when + * these claims were wrong). This can help the analyzer avoid false + * positives. * - * To use it: do a debug build and just sprinkle around tests to check your - * code! + * To use it: compile a debug build and just sprinkle around tests to check + * your code! */ #ifndef SDL_assert_h_ @@ -151,14 +156,37 @@ extern "C" { #define SDL_TriggerBreakpoint() #endif -#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 supports __func__ as a standard. */ +#ifdef SDL_WIKI_DOCUMENTATION_SECTION + +/** + * A macro that reports the current function being compiled. + * + * If SDL can't figure how the compiler reports this, it will use "???". + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_FUNCTION __FUNCTION__ + +#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 supports __func__ as a standard. */ # define SDL_FUNCTION __func__ #elif ((defined(__GNUC__) && (__GNUC__ >= 2)) || defined(_MSC_VER) || defined (__WATCOMC__)) # define SDL_FUNCTION __FUNCTION__ #else # define SDL_FUNCTION "???" #endif + +/** + * A macro that reports the current file being compiled. + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_FILE __FILE__ + +/** + * A macro that reports the current line number of the file being compiled. + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_LINE __LINE__ /* @@ -176,14 +204,50 @@ This also solves the problem of... disable assertions. */ +#ifdef SDL_WIKI_DOCUMENTATION_SECTION + +/** + * A macro for wrapping code in `do {} while (0);` without compiler warnings. + * + * Visual Studio with really aggressive warnings enabled needs this to avoid + * compiler complaints. + * + * the `do {} while (0);` trick is useful for wrapping code in a macro that + * may or may not be a single statement, to avoid various C language + * accidents. + * + * To use: + * + * ```c + * do { SomethingOnce(); } while (SDL_NULL_WHILE_LOOP_CONDITION (0)); + * ``` + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_NULL_WHILE_LOOP_CONDITION (0) + +#elif defined(_MSC_VER) /* Avoid /W4 warnings. */ /* "while (0,0)" fools Microsoft's compiler's /W4 warning level into thinking this condition isn't constant. And looks like an owl's face! */ -#ifdef _MSC_VER /* stupid /W4 warnings. */ #define SDL_NULL_WHILE_LOOP_CONDITION (0,0) #else #define SDL_NULL_WHILE_LOOP_CONDITION (0) #endif +/** + * The macro used when an assertion is disabled. + * + * This isn't for direct use by apps, but this is the code that is inserted + * when an SDL_assert is disabled (perhaps in a release build). + * + * The code does nothing, but wraps `condition` in a sizeof operator, which + * generates no code and has no side effects, but avoid compiler warnings + * about unused variables. + * + * \param condition the condition to assert (but not actually run here). + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_disabled_assert(condition) \ do { (void) sizeof ((condition)); } while (SDL_NULL_WHILE_LOOP_CONDITION) @@ -232,7 +296,7 @@ typedef struct SDL_AssertData /** * Never call this directly. * - * Use the SDL_assert* macros instead. + * Use the SDL_assert macros instead. * * \param data assert data structure. * \param func function name. @@ -248,23 +312,49 @@ extern SDL_DECLSPEC SDL_AssertState SDLCALL SDL_ReportAssertion(SDL_AssertData * const char *func, const char *file, int line) SDL_ANALYZER_NORETURN; -/* Define the trigger breakpoint call used in asserts */ -#ifndef SDL_AssertBreakpoint -#if defined(ANDROID) && defined(assert) -/* Define this as empty in case assert() is defined as SDL_assert */ -#define SDL_AssertBreakpoint() -#else + +#ifdef SDL_WIKI_DOCUMENTATION_SECTION + +/** + * The macro used when an assertion triggers a breakpoint. + * + * This isn't for direct use by apps; use SDL_assert or SDL_TriggerBreakpoint + * instead. + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_AssertBreakpoint() SDL_TriggerBreakpoint() -#endif + +#elif !defined(SDL_AssertBreakpoint) +# if defined(ANDROID) && defined(assert) + /* Define this as empty in case assert() is defined as SDL_assert */ +# define SDL_AssertBreakpoint() +# else +# define SDL_AssertBreakpoint() SDL_TriggerBreakpoint() +# endif #endif /* !SDL_AssertBreakpoint */ -/* the do {} while(0) avoids dangling else problems: - if (x) SDL_assert(y); else blah(); - ... without the do/while, the "else" could attach to this macro's "if". - We try to handle just the minimum we need here in a macro...the loop, - the static vars, and break points. The heavy lifting is handled in - SDL_ReportAssertion(), in SDL_assert.c. -*/ +/** + * The macro used when an assertion is enabled. + * + * This isn't for direct use by apps, but this is the code that is inserted + * when an SDL_assert is enabled. + * + * The `do {} while(0)` avoids dangling else problems: + * + * ```c + * if (x) SDL_assert(y); else blah(); + * ``` + * + * ... without the do/while, the "else" could attach to this macro's "if". We + * try to handle just the minimum we need here in a macro...the loop, the + * static vars, and break points. The heavy lifting is handled in + * SDL_ReportAssertion(). + * + * \param condition the condition to assert. + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_enabled_assert(condition) \ do { \ while ( !(condition) ) { \ diff --git a/libs/SDL3/include/SDL3/SDL_asyncio.h b/libs/SDL3/include/SDL3/SDL_asyncio.h new file mode 100644 index 00000000..3c983913 --- /dev/null +++ b/libs/SDL3/include/SDL3/SDL_asyncio.h @@ -0,0 +1,546 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2024 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* WIKI CATEGORY: AsyncIO */ + +/** + * # CategoryAsyncIO + * + * SDL offers a way to perform I/O asynchronously. This allows an app to read + * or write files without waiting for data to actually transfer; the functions + * that request I/O never block while the request is fulfilled. + * + * Instead, the data moves in the background and the app can check for results + * at their leisure. + * + * This is more complicated than just reading and writing files in a + * synchronous way, but it can allow for more efficiency, and never having + * framerate drops as the hard drive catches up, etc. + * + * The general usage pattern for async I/O is: + * + * - Create one or more SDL_AsyncIOQueue objects. + * - Open files with SDL_AsyncIOFromFile. + * - Start I/O tasks to the files with SDL_ReadAsyncIO or SDL_WriteAsyncIO, + * putting those tasks into one of the queues. + * - Later on, use SDL_GetAsyncIOResult on a queue to see if any task is + * finished without blocking. Tasks might finish in any order with success + * or failure. + * - When all your tasks are done, close the file with SDL_CloseAsyncIO. This + * also generates a task, since it might flush data to disk! + * + * This all works, without blocking, in a single thread, but one can also wait + * on a queue in a background thread, sleeping until new results have arrived: + * + * - Call SDL_WaitAsyncIOResult from one or more threads to efficiently block + * until new tasks complete. + * - When shutting down, call SDL_SignalAsyncIOQueue to unblock any sleeping + * threads despite there being no new tasks completed. + * + * And, of course, to match the synchronous SDL_LoadFile, we offer + * SDL_LoadFileAsync as a convenience function. This will handle allocating a + * buffer, slurping in the file data, and null-terminating it; you still check + * for results later. + * + * Behind the scenes, SDL will use newer, efficient APIs on platforms that + * support them: Linux's io_uring and Windows 11's IoRing, for example. If + * those technologies aren't available, SDL will offload the work to a thread + * pool that will manage otherwise-synchronous loads without blocking the app. + * + * ## Best Practices + * + * Simple non-blocking i/o--for an app that just wants to pick up data + * whenever it's ready without losing framerate waiting on disks to spin--can + * use whatever pattern works well for the program. In this case, simply call + * SDL_ReadAsyncIO, or maybe SDL_LoadFileAsync, as needed. Once a frame, call + * SDL_GetAsyncIOResult to check for any completed tasks and deal with the + * data as it arrives. + * + * If two separate pieces of the same program need their own i/o, it is legal + * for each to create their own queue. This will prevent either piece from + * accidentally consuming the other's completed tasks. Each queue does require + * some amount of resources, but it is not an overwhelming cost. Do not make a + * queue for each task, however. It is better to put many tasks into a single + * queue. They will be reported in order of completion, not in the order they + * were submitted, so it doesn't generally matter what order tasks are + * started. + * + * One async i/o queue can be shared by multiple threads, or one thread can + * have more than one queue, but the most efficient way--if ruthless + * efficiency is the goal--is to have one queue per thread, with multiple + * threads working in parallel, and attempt to keep each queue loaded with + * tasks that are both started by and consumed by the same thread. On modern + * platforms that can use newer interfaces, this can keep data flowing as + * efficiently as possible all the way from storage hardware to the app, with + * no contention between threads for access to the same queue. + * + * Written data is not guaranteed to make it to physical media by the time a + * closing task is completed, unless SDL_CloseAsyncIO is called with its + * `flush` parameter set to true, which is to say that a successful result + * here can still result in lost data during an unfortunately-timed power + * outage if not flushed. However, flushing will take longer and may be + * unnecessary, depending on the app's needs. + */ + +#ifndef SDL_asyncio_h_ +#define SDL_asyncio_h_ + +#include + +#include +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +extern "C" { +#endif + +/** + * The asynchronous I/O operation structure. + * + * This operates as an opaque handle. One can then request read or write + * operations on it. + * + * \since This struct is available since SDL 3.0.0. + * + * \sa SDL_AsyncIOFromFile + */ +typedef struct SDL_AsyncIO SDL_AsyncIO; + +/** + * Types of asynchronous I/O tasks. + * + * \since This enum is available since SDL 3.0.0. + */ +typedef enum SDL_AsyncIOTaskType +{ + SDL_ASYNCIO_TASK_READ, /**< A read operation. */ + SDL_ASYNCIO_TASK_WRITE, /**< A write operation. */ + SDL_ASYNCIO_TASK_CLOSE /**< A close operation. */ +} SDL_AsyncIOTaskType; + +/** + * Possible outcomes of an asynchronous I/O task. + * + * \since This enum is available since SDL 3.0.0. + */ +typedef enum SDL_AsyncIOResult +{ + SDL_ASYNCIO_COMPLETE, /**< request was completed without error */ + SDL_ASYNCIO_FAILURE, /**< request failed for some reason; check SDL_GetError()! */ + SDL_ASYNCIO_CANCELLED /**< request was cancelled before completing. */ +} SDL_AsyncIOResult; + +/** + * Information about a completed asynchronous I/O request. + * + * \since This struct is available since SDL 3.0.0. + */ +typedef struct SDL_AsyncIOOutcome +{ + SDL_AsyncIO *asyncio; /**< what generated this task. This pointer will be invalid if it was closed! */ + SDL_AsyncIOTaskType type; /**< What sort of task was this? Read, write, etc? */ + SDL_AsyncIOResult result; /**< the result of the work (success, failure, cancellation). */ + void *buffer; /**< buffer where data was read/written. */ + Uint64 offset; /**< offset in the SDL_AsyncIO where data was read/written. */ + Uint64 bytes_requested; /**< number of bytes the task was to read/write. */ + Uint64 bytes_transferred; /**< actual number of bytes that were read/written. */ + void *userdata; /**< pointer provided by the app when starting the task */ +} SDL_AsyncIOOutcome; + +/** + * A queue of completed asynchronous I/O tasks. + * + * When starting an asynchronous operation, you specify a queue for the new + * task. A queue can be asked later if any tasks in it have completed, + * allowing an app to manage multiple pending tasks in one place, in whatever + * order they complete. + * + * \since This struct is available since SDL 3.0.0. + * + * \sa SDL_CreateAsyncIOQueue + * \sa SDL_ReadAsyncIO + * \sa SDL_WriteAsyncIO + * \sa SDL_GetAsyncIOResult + * \sa SDL_WaitAsyncIOResult + */ +typedef struct SDL_AsyncIOQueue SDL_AsyncIOQueue; + +/** + * Use this function to create a new SDL_AsyncIO object for reading from + * and/or writing to a named file. + * + * The `mode` string understands the following values: + * + * - "r": Open a file for reading only. It must exist. + * - "w": Open a file for writing only. It will create missing files or + * truncate existing ones. + * - "r+": Open a file for update both reading and writing. The file must + * exist. + * - "w+": Create an empty file for both reading and writing. If a file with + * the same name already exists its content is erased and the file is + * treated as a new empty file. + * + * There is no "b" mode, as there is only "binary" style I/O, and no "a" mode + * for appending, since you specify the position when starting a task. + * + * This function supports Unicode filenames, but they must be encoded in UTF-8 + * format, regardless of the underlying operating system. + * + * This call is _not_ asynchronous; it will open the file before returning, + * under the assumption that doing so is generally a fast operation. Future + * reads and writes to the opened file will be async, however. + * + * \param file a UTF-8 string representing the filename to open. + * \param mode an ASCII string representing the mode to be used for opening + * the file. + * \returns a pointer to the SDL_AsyncIO structure that is created or NULL on + * failure; call SDL_GetError() for more information. + * + * \since This function is available since SDL 3.2.0. + * + * \sa SDL_CloseAsyncIO + * \sa SDL_ReadAsyncIO + * \sa SDL_WriteAsyncIO + */ +extern SDL_DECLSPEC SDL_AsyncIO * SDLCALL SDL_AsyncIOFromFile(const char *file, const char *mode); + +/** + * Use this function to get the size of the data stream in an SDL_AsyncIO. + * + * This call is _not_ asynchronous; it assumes that obtaining this info is a + * non-blocking operation in most reasonable cases. + * + * \param asyncio the SDL_AsyncIO to get the size of the data stream from. + * \returns the size of the data stream in the SDL_IOStream on success or a + * negative error code on failure; call SDL_GetError() for more + * information. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.2.0. + */ +extern SDL_DECLSPEC Sint64 SDLCALL SDL_GetAsyncIOSize(SDL_AsyncIO *asyncio); + +/** + * Start an async read. + * + * This function reads up to `size` bytes from `offset` position in the data + * source to the area pointed at by `ptr`. This function may read less bytes + * than requested. + * + * This function returns as quickly as possible; it does not wait for the read + * to complete. On a successful return, this work will continue in the + * background. If the work begins, even failure is asynchronous: a failing + * return value from this function only means the work couldn't start at all. + * + * `ptr` must remain available until the work is done, and may be accessed by + * the system at any time until then. Do not allocate it on the stack, as this + * might take longer than the life of the calling function to complete! + * + * An SDL_AsyncIOQueue must be specified. The newly-created task will be added + * to it when it completes its work. + * + * \param asyncio a pointer to an SDL_AsyncIO structure. + * \param ptr a pointer to a buffer to read data into. + * \param offset the position to start reading in the data source. + * \param size the number of bytes to read from the data source. + * \param queue a queue to add the new SDL_AsyncIO to. + * \param userdata an app-defined pointer that will be provided with the task + * results. + * \returns true on success or false on failure; call SDL_GetError() for more + * information. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.2.0. + * + * \sa SDL_WriteAsyncIO + * \sa SDL_CreateAsyncIOQueue + */ +extern SDL_DECLSPEC bool SDLCALL SDL_ReadAsyncIO(SDL_AsyncIO *asyncio, void *ptr, Uint64 offset, Uint64 size, SDL_AsyncIOQueue *queue, void *userdata); + +/** + * Start an async write. + * + * This function writes `size` bytes from `offset` position in the data source + * to the area pointed at by `ptr`. + * + * This function returns as quickly as possible; it does not wait for the + * write to complete. On a successful return, this work will continue in the + * background. If the work begins, even failure is asynchronous: a failing + * return value from this function only means the work couldn't start at all. + * + * `ptr` must remain available until the work is done, and may be accessed by + * the system at any time until then. Do not allocate it on the stack, as this + * might take longer than the life of the calling function to complete! + * + * An SDL_AsyncIOQueue must be specified. The newly-created task will be added + * to it when it completes its work. + * + * \param asyncio a pointer to an SDL_AsyncIO structure. + * \param ptr a pointer to a buffer to write data from. + * \param offset the position to start writing to the data source. + * \param size the number of bytes to write to the data source. + * \param queue a queue to add the new SDL_AsyncIO to. + * \param userdata an app-defined pointer that will be provided with the task + * results. + * \returns true on success or false on failure; call SDL_GetError() for more + * information. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.2.0. + * + * \sa SDL_ReadAsyncIO + * \sa SDL_CreateAsyncIOQueue + */ +extern SDL_DECLSPEC bool SDLCALL SDL_WriteAsyncIO(SDL_AsyncIO *asyncio, void *ptr, Uint64 offset, Uint64 size, SDL_AsyncIOQueue *queue, void *userdata); + +/** + * Close and free any allocated resources for an async I/O object. + * + * Closing a file is _also_ an asynchronous task! If a write failure were to + * happen during the closing process, for example, the task results will + * report it as usual. + * + * Closing a file that has been written to does not guarantee the data has + * made it to physical media; it may remain in the operating system's file + * cache, for later writing to disk. This means that a successfully-closed + * file can be lost if the system crashes or loses power in this small window. + * To prevent this, call this function with the `flush` parameter set to true. + * This will make the operation take longer, and perhaps increase system load + * in general, but a successful result guarantees that the data has made it to + * physical storage. Don't use this for temporary files, caches, and + * unimportant data, and definitely use it for crucial irreplaceable files, + * like game saves. + * + * This function guarantees that the close will happen after any other pending + * tasks to `asyncio`, so it's safe to open a file, start several operations, + * close the file immediately, then check for all results later. This function + * will not block until the tasks have completed. + * + * Once this function returns true, `asyncio` is no longer valid, regardless + * of any future outcomes. Any completed tasks might still contain this + * pointer in their SDL_AsyncIOOutcome data, in case the app was using this + * value to track information, but it should not be used again. + * + * If this function returns false, the close wasn't started at all, and it's + * safe to attempt to close again later. + * + * An SDL_AsyncIOQueue must be specified. The newly-created task will be added + * to it when it completes its work. + * + * \param asyncio a pointer to an SDL_AsyncIO structure to close. + * \param flush true if data should sync to disk before the task completes. + * \param queue a queue to add the new SDL_AsyncIO to. + * \param userdata an app-defined pointer that will be provided with the task + * results. + * \returns true on success or false on failure; call SDL_GetError() for more + * information. + * + * \threadsafety It is safe to call this function from any thread, but two + * threads should not attempt to close the same object. + * + * \since This function is available since SDL 3.2.0. + */ +extern SDL_DECLSPEC bool SDLCALL SDL_CloseAsyncIO(SDL_AsyncIO *asyncio, bool flush, SDL_AsyncIOQueue *queue, void *userdata); + +/** + * Create a task queue for tracking multiple I/O operations. + * + * Async I/O operations are assigned to a queue when started. The queue can be + * checked for completed tasks thereafter. + * + * \returns a new task queue object or NULL if there was an error; call + * SDL_GetError() for more information. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.2.0. + * + * \sa SDL_DestroyAsyncIOQueue + * \sa SDL_GetAsyncIOResult + * \sa SDL_WaitAsyncIOResult + */ +extern SDL_DECLSPEC SDL_AsyncIOQueue * SDLCALL SDL_CreateAsyncIOQueue(void); + +/** + * Destroy a previously-created async I/O task queue. + * + * If there are still tasks pending for this queue, this call will block until + * those tasks are finished. All those tasks will be deallocated. Their + * results will be lost to the app. + * + * Any pending reads from SDL_LoadFileAsync() that are still in this queue + * will have their buffers deallocated by this function, to prevent a memory + * leak. + * + * Once this function is called, the queue is no longer valid and should not + * be used, including by other threads that might access it while destruction + * is blocking on pending tasks. + * + * Do not destroy a queue that still has threads waiting on it through + * SDL_WaitAsyncIOResult(). You can call SDL_SignalAsyncIOQueue() first to + * unblock those threads, and take measures (such as SDL_WaitThread()) to make + * sure they have finished their wait and won't wait on the queue again. + * + * \param queue the task queue to destroy. + * + * \threadsafety It is safe to call this function from any thread, so long as + * no other thread is waiting on the queue with + * SDL_WaitAsyncIOResult. + * + * \since This function is available since SDL 3.2.0. + */ +extern SDL_DECLSPEC void SDLCALL SDL_DestroyAsyncIOQueue(SDL_AsyncIOQueue *queue); + +/** + * Query an async I/O task queue for completed tasks. + * + * If a task assigned to this queue has finished, this will return true and + * fill in `outcome` with the details of the task. If no task in the queue has + * finished, this function will return false. This function does not block. + * + * If a task has completed, this function will free its resources and the task + * pointer will no longer be valid. The task will be removed from the queue. + * + * It is safe for multiple threads to call this function on the same queue at + * once; a completed task will only go to one of the threads. + * + * \param queue the async I/O task queue to query. + * \param outcome details of a finished task will be written here. May not be + * NULL. + * \returns true if a task has completed, false otherwise. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.2.0. + * + * \sa SDL_WaitAsyncIOResult + */ +extern SDL_DECLSPEC bool SDLCALL SDL_GetAsyncIOResult(SDL_AsyncIOQueue *queue, SDL_AsyncIOOutcome *outcome); + +/** + * Block until an async I/O task queue has a completed task. + * + * This function puts the calling thread to sleep until there a task assigned + * to the queue that has finished. + * + * If a task assigned to the queue has finished, this will return true and + * fill in `outcome` with the details of the task. If no task in the queue has + * finished, this function will return false. + * + * If a task has completed, this function will free its resources and the task + * pointer will no longer be valid. The task will be removed from the queue. + * + * It is safe for multiple threads to call this function on the same queue at + * once; a completed task will only go to one of the threads. + * + * Note that by the nature of various platforms, more than one waiting thread + * may wake to handle a single task, but only one will obtain it, so + * `timeoutMS` is a _maximum_ wait time, and this function may return false + * sooner. + * + * This function may return false if there was a system error, the OS + * inadvertently awoke multiple threads, or if SDL_SignalAsyncIOQueue() was + * called to wake up all waiting threads without a finished task. + * + * A timeout can be used to specify a maximum wait time, but rather than + * polling, it is possible to have a timeout of -1 to wait forever, and use + * SDL_SignalAsyncIOQueue() to wake up the waiting threads later. + * + * \param queue the async I/O task queue to wait on. + * \param outcome details of a finished task will be written here. May not be + * NULL. + * \param timeoutMS the maximum time to wait, in milliseconds, or -1 to wait + * indefinitely. + * \returns true if task has completed, false otherwise. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.2.0. + * + * \sa SDL_SignalAsyncIOQueue + */ +extern SDL_DECLSPEC bool SDLCALL SDL_WaitAsyncIOResult(SDL_AsyncIOQueue *queue, SDL_AsyncIOOutcome *outcome, Sint32 timeoutMS); + +/** + * Wake up any threads that are blocking in SDL_WaitAsyncIOResult(). + * + * This will unblock any threads that are sleeping in a call to + * SDL_WaitAsyncIOResult for the specified queue, and cause them to return + * from that function. + * + * This can be useful when destroying a queue to make sure nothing is touching + * it indefinitely. In this case, once this call completes, the caller should + * take measures to make sure any previously-blocked threads have returned + * from their wait and will not touch the queue again (perhaps by setting a + * flag to tell the threads to terminate and then using SDL_WaitThread() to + * make sure they've done so). + * + * \param queue the async I/O task queue to signal. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.2.0. + * + * \sa SDL_WaitAsyncIOResult + */ +extern SDL_DECLSPEC void SDLCALL SDL_SignalAsyncIOQueue(SDL_AsyncIOQueue *queue); + +/** + * Load all the data from a file path, asynchronously. + * + * This function returns as quickly as possible; it does not wait for the read + * to complete. On a successful return, this work will continue in the + * background. If the work begins, even failure is asynchronous: a failing + * return value from this function only means the work couldn't start at all. + * + * The data is allocated with a zero byte at the end (null terminated) for + * convenience. This extra byte is not included in SDL_AsyncIOOutcome's + * bytes_transferred value. + * + * This function will allocate the buffer to contain the file. It must be + * deallocated by calling SDL_free() on SDL_AsyncIOOutcome's buffer field + * after completion. + * + * An SDL_AsyncIOQueue must be specified. The newly-created task will be added + * to it when it completes its work. + * + * \param file the path to read all available data from. + * \param queue a queue to add the new SDL_AsyncIO to. + * \param userdata an app-defined pointer that will be provided with the task + * results. + * \returns true on success or false on failure; call SDL_GetError() for more + * information. + * + * \since This function is available since SDL 3.2.0. + * + * \sa SDL_LoadFile_IO + */ +extern SDL_DECLSPEC bool SDLCALL SDL_LoadFileAsync(const char *file, SDL_AsyncIOQueue *queue, void *userdata); + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +} +#endif +#include + +#endif /* SDL_asyncio_h_ */ diff --git a/libs/SDL3/include/SDL3/SDL_atomic.h b/libs/SDL3/include/SDL3/SDL_atomic.h index 479e8edd..be4c3988 100644 --- a/libs/SDL3/include/SDL3/SDL_atomic.h +++ b/libs/SDL3/include/SDL3/SDL_atomic.h @@ -155,6 +155,7 @@ extern SDL_DECLSPEC void SDLCALL SDL_UnlockSpinlock(SDL_SpinLock *lock); * \since This macro is available since SDL 3.1.3. */ #define SDL_CompilerBarrier() DoCompilerSpecificReadWriteBarrier() + #elif defined(_MSC_VER) && (_MSC_VER > 1200) && !defined(__clang__) void _ReadWriteBarrier(void); #pragma intrinsic(_ReadWriteBarrier) @@ -171,7 +172,50 @@ extern __inline void SDL_CompilerBarrier(void); #endif /** - * Insert a memory release barrier. + * Insert a memory release barrier (function version). + * + * Please refer to SDL_MemoryBarrierRelease for details. This is a function + * version, which might be useful if you need to use this functionality from a + * scripting language, etc. Also, some of the macro versions call this + * function behind the scenes, where more heavy lifting can happen inside of + * SDL. Generally, though, an app written in C/C++/etc should use the macro + * version, as it will be more efficient. + * + * \threadsafety Obviously this function is safe to use from any thread at any + * time, but if you find yourself needing this, you are probably + * dealing with some very sensitive code; be careful! + * + * \since This function is available since SDL 3.1.3. + * + * \sa SDL_MemoryBarrierRelease + */ +extern SDL_DECLSPEC void SDLCALL SDL_MemoryBarrierReleaseFunction(void); + +/** + * Insert a memory acquire barrier (function version). + * + * Please refer to SDL_MemoryBarrierRelease for details. This is a function + * version, which might be useful if you need to use this functionality from a + * scripting language, etc. Also, some of the macro versions call this + * function behind the scenes, where more heavy lifting can happen inside of + * SDL. Generally, though, an app written in C/C++/etc should use the macro + * version, as it will be more efficient. + * + * \threadsafety Obviously this function is safe to use from any thread at any + * time, but if you find yourself needing this, you are probably + * dealing with some very sensitive code; be careful! + * + * \since This function is available since SDL 3.1.3. + * + * \sa SDL_MemoryBarrierAcquire + */ +extern SDL_DECLSPEC void SDLCALL SDL_MemoryBarrierAcquireFunction(void); + + +#ifdef SDL_WIKI_DOCUMENTATION_SECTION + +/** + * Insert a memory release barrier (macro version). * * Memory barriers are designed to prevent reads and writes from being * reordered by the compiler and being seen out of order on multi-core CPUs. @@ -191,31 +235,47 @@ extern __inline void SDL_CompilerBarrier(void); * For more information on these semantics, take a look at the blog post: * http://preshing.com/20120913/acquire-and-release-semantics * + * This is the macro version of this functionality; if possible, SDL will use + * compiler intrinsics or inline assembly, but some platforms might need to + * call the function version of this, SDL_MemoryBarrierReleaseFunction to do + * the heavy lifting. Apps that can use the macro should favor it over the + * function. + * * \threadsafety Obviously this macro is safe to use from any thread at any * time, but if you find yourself needing this, you are probably * dealing with some very sensitive code; be careful! * - * \since This function is available since SDL 3.1.3. + * \since This macro is available since SDL 3.1.3. + * + * \sa SDL_MemoryBarrierAcquire + * \sa SDL_MemoryBarrierReleaseFunction */ -extern SDL_DECLSPEC void SDLCALL SDL_MemoryBarrierReleaseFunction(void); +#define SDL_MemoryBarrierRelease() SDL_MemoryBarrierReleaseFunction() /** - * Insert a memory acquire barrier. + * Insert a memory acquire barrier (macro version). * - * Please refer to SDL_MemoryBarrierReleaseFunction for the details! + * Please see SDL_MemoryBarrierRelease for the details on what memory barriers + * are and when to use them. * - * \threadsafety Obviously this function is safe to use from any thread at any + * This is the macro version of this functionality; if possible, SDL will use + * compiler intrinsics or inline assembly, but some platforms might need to + * call the function version of this, SDL_MemoryBarrierAcquireFunction, to do + * the heavy lifting. Apps that can use the macro should favor it over the + * function. + * + * \threadsafety Obviously this macro is safe to use from any thread at any * time, but if you find yourself needing this, you are probably * dealing with some very sensitive code; be careful! * - * \since This function is available since SDL 3.1.3. + * \since This macro is available since SDL 3.1.3. * - * \sa SDL_MemoryBarrierReleaseFunction + * \sa SDL_MemoryBarrierRelease + * \sa SDL_MemoryBarrierAcquireFunction */ -extern SDL_DECLSPEC void SDLCALL SDL_MemoryBarrierAcquireFunction(void); +#define SDL_MemoryBarrierAcquire() SDL_MemoryBarrierAcquireFunction() -/* !!! FIXME: this should have documentation! */ -#if defined(__GNUC__) && (defined(__powerpc__) || defined(__ppc__)) +#elif defined(__GNUC__) && (defined(__powerpc__) || defined(__ppc__)) #define SDL_MemoryBarrierRelease() __asm__ __volatile__ ("lwsync" : : : "memory") #define SDL_MemoryBarrierAcquire() __asm__ __volatile__ ("lwsync" : : : "memory") #elif defined(__GNUC__) && defined(__aarch64__) @@ -284,6 +344,7 @@ typedef void (*SDL_KernelMemoryBarrierFunc)(); * \since This macro is available since SDL 3.1.3. */ #define SDL_CPUPauseInstruction() DoACPUPauseInACompilerAndArchitectureSpecificWay + #elif (defined(__GNUC__) || defined(__clang__)) && (defined(__i386__) || defined(__x86_64__)) #define SDL_CPUPauseInstruction() __asm__ __volatile__("pause\n") /* Some assemblers can't do REP NOP, so go with PAUSE. */ #elif (defined(__arm__) && defined(__ARM_ARCH) && __ARM_ARCH >= 7) || defined(__aarch64__) @@ -475,7 +536,6 @@ extern SDL_DECLSPEC int SDLCALL SDL_AddAtomicInt(SDL_AtomicInt *a, int v); * \sa SDL_CompareAndSwapAtomicU32 * \sa SDL_GetAtomicU32 * \sa SDL_SetAtomicU32 - * \sa SDL_AddAtomicU32 */ typedef struct SDL_AtomicU32 { Uint32 value; } SDL_AtomicU32; diff --git a/libs/SDL3/include/SDL3/SDL_audio.h b/libs/SDL3/include/SDL3/SDL_audio.h index 567b0616..d9fa496d 100644 --- a/libs/SDL3/include/SDL3/SDL_audio.h +++ b/libs/SDL3/include/SDL3/SDL_audio.h @@ -33,8 +33,9 @@ * even if the data format changes on either side halfway through. * * An app opens an audio device and binds any number of audio streams to it, - * feeding more data to it as available. When the devices needs more data, it - * will pull it from all bound streams and mix them together for playback. + * feeding more data to the streams as available. When the device needs more + * data, it will pull it from all bound streams and mix them together for + * playback. * * Audio streams can also use an app-provided callback to supply data * on-demand, which maps pretty closely to the SDL2 audio model. @@ -66,6 +67,22 @@ * keep playing; the app doesn't even have to know it happened if it doesn't * want to. * + * ## Simplified audio + * + * As a simplified model for when a single source of audio is all that's + * needed, an app can use SDL_OpenAudioDeviceStream, which is a single + * function to open an audio device, create an audio stream, bind that stream + * to the newly-opened device, and (optionally) provide a callback for + * obtaining audio data. When using this function, the primary interface is + * the SDL_AudioStream and the device handle is mostly hidden away; destroying + * a stream created through this function will also close the device, stream + * bindings cannot be changed, etc. One other quirk of this is that the device + * is started in a _paused_ state and must be explicitly resumed; this is + * partially to offer a clean migration for SDL2 apps and partially because + * the app might have to do more setup before playback begins; in the + * non-simplified form, nothing will play until a stream is bound to a device, + * so they start _unpaused_. + * * ## Channel layouts * * Audio data passing through SDL is uncompressed PCM data, interleaved. One @@ -124,14 +141,68 @@ extern "C" { #endif -/* masks for different parts of SDL_AudioFormat. */ +/** + * Mask of bits in an SDL_AudioFormat that contains the format bit size. + * + * Generally one should use SDL_AUDIO_BITSIZE instead of this macro directly. + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_AUDIO_MASK_BITSIZE (0xFFu) + +/** + * Mask of bits in an SDL_AudioFormat that contain the floating point flag. + * + * Generally one should use SDL_AUDIO_ISFLOAT instead of this macro directly. + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_AUDIO_MASK_FLOAT (1u<<8) + +/** + * Mask of bits in an SDL_AudioFormat that contain the bigendian flag. + * + * Generally one should use SDL_AUDIO_ISBIGENDIAN or SDL_AUDIO_ISLITTLEENDIAN + * instead of this macro directly. + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_AUDIO_MASK_BIG_ENDIAN (1u<<12) + +/** + * Mask of bits in an SDL_AudioFormat that contain the signed data flag. + * + * Generally one should use SDL_AUDIO_ISSIGNED instead of this macro directly. + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_AUDIO_MASK_SIGNED (1u<<15) -#define SDL_DEFINE_AUDIO_FORMAT(signed, bigendian, float, size) \ - (((Uint16)(signed) << 15) | ((Uint16)(bigendian) << 12) | ((Uint16)(float) << 8) | ((size) & SDL_AUDIO_MASK_BITSIZE)) +/** + * Define an SDL_AudioFormat value. + * + * SDL does not support custom audio formats, so this macro is not of much use + * externally, but it can be illustrative as to what the various bits of an + * SDL_AudioFormat mean. + * + * For example, SDL_AUDIO_S32LE looks like this: + * + * ```c + * SDL_DEFINE_AUDIO_FORMAT(1, 0, 0, 32) + * ``` + * + * \param signed 1 for signed data, 0 for unsigned data. + * \param bigendian 1 for bigendian data, 0 for littleendian data. + * \param flt 1 for floating point data, 0 for integer data. + * \param size number of bits per sample. + * \returns a format value in the style of SDL_AudioFormat. + * + * \threadsafety It is safe to call this macro from any thread. + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_DEFINE_AUDIO_FORMAT(signed, bigendian, flt, size) \ + (((Uint16)(signed) << 15) | ((Uint16)(bigendian) << 12) | ((Uint16)(flt) << 8) | ((size) & SDL_AUDIO_MASK_BITSIZE)) /** * Audio format. @@ -382,14 +453,6 @@ typedef struct SDL_AudioStream SDL_AudioStream; /* Function prototypes */ -/** - * \name Driver discovery functions - * - * These functions return the list of built in audio drivers, in the - * order that they are normally initialized by default. - */ -/* @{ */ - /** * Use this function to get the number of built-in audio drivers. * @@ -436,7 +499,6 @@ extern SDL_DECLSPEC int SDLCALL SDL_GetNumAudioDrivers(void); * \sa SDL_GetNumAudioDrivers */ extern SDL_DECLSPEC const char * SDLCALL SDL_GetAudioDriver(int index); -/* @} */ /** * Get the name of the current audio driver. @@ -525,7 +587,6 @@ extern SDL_DECLSPEC SDL_AudioDeviceID * SDLCALL SDL_GetAudioRecordingDevices(int * * \sa SDL_GetAudioPlaybackDevices * \sa SDL_GetAudioRecordingDevices - * \sa SDL_GetDefaultAudioInfo */ extern SDL_DECLSPEC const char * SDLCALL SDL_GetAudioDeviceName(SDL_AudioDeviceID devid); @@ -663,6 +724,45 @@ extern SDL_DECLSPEC int * SDLCALL SDL_GetAudioDeviceChannelMap(SDL_AudioDeviceID */ extern SDL_DECLSPEC SDL_AudioDeviceID SDLCALL SDL_OpenAudioDevice(SDL_AudioDeviceID devid, const SDL_AudioSpec *spec); +/** + * Determine if an audio device is physical (instead of logical). + * + * An SDL_AudioDeviceID that represents physical hardware is a physical + * device; there is one for each piece of hardware that SDL can see. Logical + * devices are created by calling SDL_OpenAudioDevice or + * SDL_OpenAudioDeviceStream, and while each is associated with a physical + * device, there can be any number of logical devices on one physical device. + * + * For the most part, logical and physical IDs are interchangeable--if you try + * to open a logical device, SDL understands to assign that effort to the + * underlying physical device, etc. However, it might be useful to know if an + * arbitrary device ID is physical or logical. This function reports which. + * + * This function may return either true or false for invalid device IDs. + * + * \param devid the device ID to query. + * \returns true if devid is a physical device, false if it is logical. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.2.0. + */ +extern SDL_DECLSPEC bool SDLCALL SDL_IsAudioDevicePhysical(SDL_AudioDeviceID devid); + +/** + * Determine if an audio device is a playback device (instead of recording). + * + * This function may return either true or false for invalid device IDs. + * + * \param devid the device ID to query. + * \returns true if devid is a playback device, false if it is recording. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.2.0. + */ +extern SDL_DECLSPEC bool SDLCALL SDL_IsAudioDevicePlayback(SDL_AudioDeviceID devid); + /** * Use this function to pause audio playback on a specified device. * @@ -1173,8 +1273,12 @@ extern SDL_DECLSPEC int * SDLCALL SDL_GetAudioStreamOutputChannelMap(SDL_AudioSt * channel that it should be remapped to. To reverse a stereo signal's left * and right values, you'd have an array of `{ 1, 0 }`. It is legal to remap * multiple channels to the same thing, so `{ 1, 1 }` would duplicate the - * right channel to both channels of a stereo signal. You cannot change the - * number of channels through a channel map, just reorder them. + * right channel to both channels of a stereo signal. An element in the + * channel map set to -1 instead of a valid channel will mute that channel, + * setting it to a silence value. + * + * You cannot change the number of channels through a channel map, just + * reorder/mute them. * * Data that was previously queued in the stream will still be operated on in * the order that was current when it was added, which is to say you can put @@ -1189,8 +1293,8 @@ extern SDL_DECLSPEC int * SDLCALL SDL_GetAudioStreamOutputChannelMap(SDL_AudioSt * after this call. * * If `count` is not equal to the current number of channels in the audio - * stream's format, this will fail. This is a safety measure to make sure a a - * race condition hasn't changed the format while you this call is setting the + * stream's format, this will fail. This is a safety measure to make sure a + * race condition hasn't changed the format while this call is setting the * channel map. * * \param stream the SDL_AudioStream to change. @@ -1219,12 +1323,16 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetAudioStreamInputChannelMap(SDL_AudioStre * The output channel map reorders data that leaving a stream via * SDL_GetAudioStreamData. * - * Each item in the array represents an output channel, and its value is the + * Each item in the array represents an input channel, and its value is the * channel that it should be remapped to. To reverse a stereo signal's left * and right values, you'd have an array of `{ 1, 0 }`. It is legal to remap * multiple channels to the same thing, so `{ 1, 1 }` would duplicate the - * right channel to both channels of a stereo signal. You cannot change the - * number of channels through a channel map, just reorder them. + * right channel to both channels of a stereo signal. An element in the + * channel map set to -1 instead of a valid channel will mute that channel, + * setting it to a silence value. + * + * You cannot change the number of channels through a channel map, just + * reorder/mute them. * * The output channel map can be changed at any time, as output remapping is * applied during SDL_GetAudioStreamData. @@ -1236,8 +1344,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetAudioStreamInputChannelMap(SDL_AudioStre * after this call. * * If `count` is not equal to the current number of channels in the audio - * stream's format, this will fail. This is a safety measure to make sure a a - * race condition hasn't changed the format while you this call is setting the + * stream's format, this will fail. This is a safety measure to make sure a + * race condition hasn't changed the format while this call is setting the * channel map. * * \param stream the SDL_AudioStream to change. diff --git a/libs/SDL3/include/SDL3/SDL_begin_code.h b/libs/SDL3/include/SDL3/SDL_begin_code.h index acf99284..bfb1c2cc 100644 --- a/libs/SDL3/include/SDL3/SDL_begin_code.h +++ b/libs/SDL3/include/SDL3/SDL_begin_code.h @@ -22,9 +22,15 @@ /* WIKI CATEGORY: BeginCode */ /** - * SDL_begin_code.h sets things up for C dynamic library function definitions, - * static inlined functions, and structures aligned at 4-byte alignment. - * If you don't like ugly C preprocessor code, don't look at this file. :) + * # CategoryBeginCode + * + * `SDL_begin_code.h` sets things up for C dynamic library function + * definitions, static inlined functions, and structures aligned at 4-byte + * alignment. If you don't like ugly C preprocessor code, don't look at this + * file. :) + * + * SDL's headers use this; applications generally should not include this + * header directly. */ /* This shouldn't be nested -- included it around code only. */ @@ -33,6 +39,259 @@ #endif #define SDL_begin_code_h +#ifdef SDL_WIKI_DOCUMENTATION_SECTION + +/** + * A macro to tag a symbol as deprecated. + * + * A function is marked deprecated by adding this macro to its declaration: + * + * ```c + * extern SDL_DEPRECATED int ThisFunctionWasABadIdea(void); + * ``` + * + * Compilers with deprecation support can give a warning when a deprecated + * function is used. This symbol may be used in SDL's headers, but apps are + * welcome to use it for their own interfaces as well. + * + * SDL, on occasion, might deprecate a function for various reasons. However, + * SDL never removes symbols before major versions, so deprecated interfaces + * in SDL3 will remain available until SDL4, where it would be expected an app + * would have to take steps to migrate anyhow. + * + * On compilers without a deprecation mechanism, this is defined to nothing, + * and using a deprecated function will not generate a warning. + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_DEPRECATED __attribute__((deprecated)) + +/** + * A macro to tag a symbol as a public API. + * + * SDL uses this macro for all its public functions. On some targets, it is + * used to signal to the compiler that this function needs to be exported from + * a shared library, but it might have other side effects. + * + * This symbol is used in SDL's headers, but apps and other libraries are + * welcome to use it for their own interfaces as well. + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_DECLSPEC __attribute__ ((visibility("default"))) + +/** + * A macro to set a function's calling conventions. + * + * SDL uses this macro for all its public functions, and any callbacks it + * defines. This macro guarantees that calling conventions match between SDL + * and the app, even if the two were built with different compilers or + * optimization settings. + * + * When writing a callback function, it is very important for it to be + * correctly tagged with SDLCALL, as mismatched calling conventions can cause + * strange behaviors and can be difficult to diagnose. Plus, on many + * platforms, SDLCALL is defined to nothing, so compilers won't be able to + * warn that the tag is missing. + * + * This symbol is used in SDL's headers, but apps and other libraries are + * welcome to use it for their own interfaces as well. + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDLCALL __cdecl + +/** + * A macro to request a function be inlined. + * + * This is a hint to the compiler to inline a function. The compiler is free + * to ignore this request. On compilers without inline support, this is + * defined to nothing. + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_INLINE __inline + +/** + * A macro to demand a function be inlined. + * + * This is a command to the compiler to inline a function. SDL uses this macro + * in its public headers for a handful of simple functions. On compilers + * without forceinline support, this is defined to `static SDL_INLINE`, which + * is often good enough. + * + * This symbol is used in SDL's headers, but apps and other libraries are + * welcome to use it for their own interfaces as well. + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_FORCE_INLINE __forceinline + +/** + * A macro to tag a function as never-returning. + * + * This is a hint to the compiler that a function does not return. An example + * of a function like this is the C runtime's exit() function. + * + * This hint can lead to code optimizations, and help analyzers understand + * code flow better. On compilers without noreturn support, this is defined to + * nothing. + * + * This symbol is used in SDL's headers, but apps and other libraries are + * welcome to use it for their own interfaces as well. + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_NORETURN __attribute__((noreturn)) + +/** + * A macro to tag a function as never-returning (for analysis purposes). + * + * This is almost identical to SDL_NORETURN, except functions marked with this + * _can_ actually return. The difference is that this isn't used for code + * generation, but rather static analyzers use this information to assume + * truths about program state and available code paths. Specifically, this tag + * is useful for writing an assertion mechanism. Indeed, SDL_assert uses this + * tag behind the scenes. Generally, apps that don't understand the specific + * use-case for this tag should avoid using it directly. + * + * On compilers without analyzer_noreturn support, this is defined to nothing. + * + * This symbol is used in SDL's headers, but apps and other libraries are + * welcome to use it for their own interfaces as well. + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_ANALYZER_NORETURN __attribute__((analyzer_noreturn)) + + +/** + * A macro to signal that a case statement without a `break` is intentional. + * + * C compilers have gotten more aggressive about warning when a switch's + * `case` block does not end with a `break` or other flow control statement, + * flowing into the next case's code, as this is a common accident that leads + * to strange bugs. But sometimes falling through to the next case is the + * correct and desired behavior. This symbol lets an app communicate this + * intention to the compiler, so it doesn't generate a warning. + * + * It is used like this: + * + * ```c + * switch (x) { + * case 1: + * DoSomethingOnlyForOne(); + * SDL_FALLTHROUGH; // tell the compiler this was intentional. + * case 2: + * DoSomethingForOneAndTwo(); + * break; + * } + * ``` + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_FALLTHROUGH [[fallthrough]] + +/** + * A macro to tag a function's return value as critical. + * + * This is a hint to the compiler that a function's return value should not be + * ignored. + * + * If an NODISCARD function's return value is thrown away (the function is + * called as if it returns `void`), the compiler will issue a warning. + * + * While it's generally good practice to check return values for errors, often + * times legitimate programs do not for good reasons. Be careful about what + * functions are tagged as NODISCARD. It operates best when used on a function + * that's failure is surprising and catastrophic; a good example would be a + * program that checks the return values of all its file write function calls + * but not the call to close the file, which it assumes incorrectly never + * fails. + * + * Function callers that want to throw away a NODISCARD return value can call + * the function with a `(void)` cast, which informs the compiler the act is + * intentional. + * + * On compilers without nodiscard support, this is defined to nothing. + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_NODISCARD [[nodiscard]] + +/** + * A macro to tag a function as an allocator. + * + * This is a hint to the compiler that a function is an allocator, like + * malloc(), with certain rules. A description of how GCC treats this hint is + * here: + * + * https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-malloc-function-attribute + * + * On compilers without allocator tag support, this is defined to nothing. + * + * Most apps don't need to, and should not, use this directly. + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_MALLOC __declspec(allocator) __desclspec(restrict) + +/** + * A macro to tag a function as returning a certain allocation. + * + * This is a hint to the compiler that a function allocates and returns a + * specific amount of memory based on one of its arguments. For example, the C + * runtime's malloc() function could use this macro with an argument of 1 + * (first argument to malloc is the size of the allocation). + * + * On compilers without alloc_size support, this is defined to nothing. + * + * Most apps don't need to, and should not, use this directly. + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_ALLOC_SIZE(p) __attribute__((alloc_size(p))) + +/** + * A macro to tag a pointer variable, to help with pointer aliasing. + * + * A good explanation of the restrict keyword is here: + * + * https://en.wikipedia.org/wiki/Restrict + * + * On compilers without restrict support, this is defined to nothing. + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_RESTRICT __restrict__ + +/** + * Check if the compiler supports a given builtin functionality. + * + * This allows preprocessor checks for things that otherwise might fail to + * compile. + * + * Supported by virtually all clang versions and more-recent GCCs. Use this + * instead of checking the clang version if possible. + * + * On compilers without has_builtin support, this is defined to 0 (always + * false). + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_HAS_BUILTIN(x) __has_builtin(x) + +/* end of wiki documentation section. */ +#endif + +#ifndef SDL_HAS_BUILTIN +#ifdef __has_builtin +#define SDL_HAS_BUILTIN(x) __has_builtin(x) +#else +#define SDL_HAS_BUILTIN(x) 0 +#endif +#endif + #ifndef SDL_DEPRECATED # if defined(__GNUC__) && (__GNUC__ >= 4) /* technically, this arrived in gcc 3.1, but oh well. */ # define SDL_DEPRECATED __attribute__((deprecated)) diff --git a/libs/SDL3/include/SDL3/SDL_bits.h b/libs/SDL3/include/SDL3/SDL_bits.h index cabf92d1..71f356ce 100644 --- a/libs/SDL3/include/SDL3/SDL_bits.h +++ b/libs/SDL3/include/SDL3/SDL_bits.h @@ -36,10 +36,6 @@ extern "C" { #endif -/** - * \file SDL_bits.h - */ - #if defined(__WATCOMC__) && defined(__386__) extern __inline int _SDL_bsr_watcom(Uint32); #pragma aux _SDL_bsr_watcom = \ diff --git a/libs/SDL3/include/SDL3/SDL_blendmode.h b/libs/SDL3/include/SDL3/SDL_blendmode.h index 0998c1a1..b9dbbe4b 100644 --- a/libs/SDL3/include/SDL3/SDL_blendmode.h +++ b/libs/SDL3/include/SDL3/SDL_blendmode.h @@ -24,7 +24,7 @@ * * Blend modes decide how two colors will mix together. There are both * standard modes for basic needs and a means to create custom modes, - * dictating what sort of math to do what on what color components. + * dictating what sort of math to do on what color components. */ #ifndef SDL_blendmode_h_ diff --git a/libs/SDL3/include/SDL3/SDL_camera.h b/libs/SDL3/include/SDL3/SDL_camera.h index 4ea75908..c3611f07 100644 --- a/libs/SDL3/include/SDL3/SDL_camera.h +++ b/libs/SDL3/include/SDL3/SDL_camera.h @@ -28,6 +28,41 @@ * devices can be enumerated, queried, and opened. Once opened, it will * provide SDL_Surface objects as new frames of video come in. These surfaces * can be uploaded to an SDL_Texture or processed as pixels in memory. + * + * Several platforms will alert the user if an app tries to access a camera, + * and some will present a UI asking the user if your application should be + * allowed to obtain images at all, which they can deny. A successfully opened + * camera will not provide images until permission is granted. Applications, + * after opening a camera device, can see if they were granted access by + * either polling with the SDL_GetCameraPermissionState() function, or waiting + * for an SDL_EVENT_CAMERA_DEVICE_APPROVED or SDL_EVENT_CAMERA_DEVICE_DENIED + * event. Platforms that don't have any user approval process will report + * approval immediately. + * + * Note that SDL cameras only provide video as individual frames; they will + * not provide full-motion video encoded in a movie file format, although an + * app is free to encode the acquired frames into any format it likes. It also + * does not provide audio from the camera hardware through this API; not only + * do many webcams not have microphones at all, many people--from streamers to + * people on Zoom calls--will want to use a separate microphone regardless of + * the camera. In any case, recorded audio will be available through SDL's + * audio API no matter what hardware provides the microphone. + * + * ## Camera gotchas + * + * Consumer-level camera hardware tends to take a little while to warm up, + * once the device has been opened. Generally most camera apps have some sort + * of UI to take a picture (a button to snap a pic while a preview is showing, + * some sort of multi-second countdown for the user to pose, like a photo + * booth), which puts control in the users' hands, or they are intended to + * stay on for long times (Pokemon Go, etc). + * + * It's not uncommon that a newly-opened camera will provide a couple of + * completely black frames, maybe followed by some under-exposed images. If + * taking a single frame automatically, or recording video from a camera's + * input without the user initiating it from a preview, it could be wise to + * drop the first several frames (if not the first several _seconds_ worth of + * frames!) before using images from a camera. */ #ifndef SDL_camera_h_ @@ -409,7 +444,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetCameraFormat(SDL_Camera *camera, SDL_Cam * After use, the frame should be released with SDL_ReleaseCameraFrame(). If * you don't do this, the system may stop providing more video! * - * Do not call SDL_FreeSurface() on the returned surface! It must be given + * Do not call SDL_DestroySurface() on the returned surface! It must be given * back to the camera subsystem with SDL_ReleaseCameraFrame! * * If the system is waiting for the user to approve access to the camera, as @@ -471,7 +506,6 @@ extern SDL_DECLSPEC void SDLCALL SDL_ReleaseCameraFrame(SDL_Camera *camera, SDL_ * * \since This function is available since SDL 3.1.3. * - * \sa SDL_OpenCameraWithSpec * \sa SDL_OpenCamera */ extern SDL_DECLSPEC void SDLCALL SDL_CloseCamera(SDL_Camera *camera); diff --git a/libs/SDL3/include/SDL3/SDL_clipboard.h b/libs/SDL3/include/SDL3/SDL_clipboard.h index b23bdb78..448cb4a5 100644 --- a/libs/SDL3/include/SDL3/SDL_clipboard.h +++ b/libs/SDL3/include/SDL3/SDL_clipboard.h @@ -26,6 +26,51 @@ * from other processes and publishing information of its own. * * This is not just text! SDL apps can access and publish data by mimetype. + * + * ## Basic use (text) + * + * Obtaining and publishing simple text to the system clipboard is as easy as + * calling SDL_GetClipboardText() and SDL_SetClipboardText(), respectively. + * These deal with C strings in UTF-8 encoding. Data transmission and encoding + * conversion is completely managed by SDL. + * + * ## Clipboard callbacks (data other than text) + * + * Things get more complicated when the clipboard contains something other + * than text. Not only can the system clipboard contain data of any type, in + * some cases it can contain the same data in different formats! For example, + * an image painting app might let the user copy a graphic to the clipboard, + * and offers it in .BMP, .JPG, or .PNG format for other apps to consume. + * + * Obtaining clipboard data ("pasting") like this is a matter of calling + * SDL_GetClipboardData() and telling it the mimetype of the data you want. + * But how does one know if that format is available? SDL_HasClipboardData() + * can report if a specific mimetype is offered, and + * SDL_GetClipboardMimeTypes() can provide the entire list of mimetypes + * available, so the app can decide what to do with the data and what formats + * it can support. + * + * Setting the clipboard ("copying") to arbitrary data is done with + * SDL_SetClipboardData. The app does not provide the data in this call, but + * rather the mimetypes it is willing to provide and a callback function. + * During the callback, the app will generate the data. This allows massive + * data sets to be provided to the clipboard, without any data being copied + * before it is explicitly requested. More specifically, it allows an app to + * offer data in multiple formats without providing a copy of all of them + * upfront. If the app has an image that it could provide in PNG or JPG + * format, it doesn't have to encode it to either of those unless and until + * something tries to paste it. + * + * ## Primary Selection + * + * The X11 and Wayland video targets have a concept of the "primary selection" + * in addition to the usual clipboard. This is generally highlighted (but not + * explicitly copied) text from various apps. SDL offers APIs for this through + * SDL_GetPrimarySelectionText() and SDL_SetPrimarySelectionText(). SDL offers + * these APIs on platforms without this concept, too, but only so far that it + * will keep a copy of a string that the app sets for later retrieval; the + * operating system will not ever attempt to change the string externally if + * it doesn't support a primary selection. */ #ifndef SDL_clipboard_h_ @@ -49,7 +94,7 @@ extern "C" { * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -68,7 +113,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetClipboardText(const char *text); * SDL_GetError() for more information. This should be freed with * SDL_free() when it is no longer needed. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -82,7 +127,7 @@ extern SDL_DECLSPEC char * SDLCALL SDL_GetClipboardText(void); * * \returns true if the clipboard has text, or false if it does not. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -98,7 +143,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_HasClipboardText(void); * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -117,7 +162,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetPrimarySelectionText(const char *text); * failure; call SDL_GetError() for more information. This should be * freed with SDL_free() when it is no longer needed. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -132,7 +177,7 @@ extern SDL_DECLSPEC char * SDLCALL SDL_GetPrimarySelectionText(void); * * \returns true if the primary selection has text, or false if it does not. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -181,13 +226,13 @@ typedef void (SDLCALL *SDL_ClipboardCleanupCallback)(void *userdata); * Offer clipboard data to the OS. * * Tell the operating system that the application is offering clipboard data - * for each of the proivded mime-types. Once another application requests the - * data the callback function will be called allowing it to generate and + * for each of the provided mime-types. Once another application requests the + * data the callback function will be called, allowing it to generate and * 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 - * document) + * document). * * \param callback a function pointer to the function that provides the * clipboard data. @@ -199,7 +244,7 @@ typedef void (SDLCALL *SDL_ClipboardCleanupCallback)(void *userdata); * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -215,7 +260,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetClipboardData(SDL_ClipboardDataCallback * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -235,7 +280,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_ClearClipboardData(void); * for more information. This should be freed with SDL_free() when it * is no longer needed. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -251,7 +296,7 @@ extern SDL_DECLSPEC void * SDLCALL SDL_GetClipboardData(const char *mime_type, s * \returns true if there exists data in clipboard for the provided mime type, * false if it does not. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -269,7 +314,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_HasClipboardData(const char *mime_type); * failure; call SDL_GetError() for more information. This should be * freed with SDL_free() when it is no longer needed. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * diff --git a/libs/SDL3/include/SDL3/SDL_close_code.h b/libs/SDL3/include/SDL3/SDL_close_code.h index fa4d3d17..aba9066c 100644 --- a/libs/SDL3/include/SDL3/SDL_close_code.h +++ b/libs/SDL3/include/SDL3/SDL_close_code.h @@ -21,7 +21,10 @@ /* * This file reverses the effects of SDL_begin_code.h and should be included - * after you finish any function and structure declarations in your headers + * after you finish any function and structure declarations in your headers. + * + * SDL's headers use this; applications generally should not include this + * header directly. */ #ifndef SDL_begin_code_h diff --git a/libs/SDL3/include/SDL3/SDL_cpuinfo.h b/libs/SDL3/include/SDL3/SDL_cpuinfo.h index 8d47d92d..eca703aa 100644 --- a/libs/SDL3/include/SDL3/SDL_cpuinfo.h +++ b/libs/SDL3/include/SDL3/SDL_cpuinfo.h @@ -29,6 +29,12 @@ * These functions are largely concerned with reporting if the system has * access to various SIMD instruction sets, but also has other important info * to share, such as system RAM size and number of logical CPU cores. + * + * CPU instruction set checks, like SDL_HasSSE() and SDL_HasNEON(), are + * available on all platforms, even if they don't make sense (an ARM processor + * will never have SSE and an x86 processor will never have NEON, for example, + * but these functions still exist and will simply return false in these + * cases). */ #ifndef SDL_cpuinfo_h_ diff --git a/libs/SDL3/include/SDL3/SDL_dialog.h b/libs/SDL3/include/SDL3/SDL_dialog.h index 5fe62829..9d66573f 100644 --- a/libs/SDL3/include/SDL3/SDL_dialog.h +++ b/libs/SDL3/include/SDL3/SDL_dialog.h @@ -23,6 +23,15 @@ * # CategoryDialog * * File dialog support. + * + * SDL offers file dialogs, to let users select files with native GUI + * interfaces. There are "open" dialogs, "save" dialogs, and folder selection + * dialogs. The app can control some details, such as filtering to specific + * files, or whether multiple files can be selected by the user. + * + * Note that launching a file dialog is a non-blocking operation; control + * returns to the app immediately, and a callback is called later (possibly in + * another thread) when the user makes a choice. */ #ifndef SDL_dialog_h_ @@ -30,6 +39,7 @@ #include #include +#include #include #include @@ -55,6 +65,7 @@ extern "C" { * \sa SDL_ShowOpenFileDialog * \sa SDL_ShowSaveFileDialog * \sa SDL_ShowOpenFolderDialog + * \sa SDL_ShowFileDialogWithProperties */ typedef struct SDL_DialogFileFilter { @@ -76,8 +87,8 @@ typedef struct SDL_DialogFileFilter * is a null-terminated list of pointers to C strings, each containing a * path. * - * The filelist argument does not need to be freed; it will automatically be - * freed when the callback returns. + * The filelist argument should not be freed; it will automatically be freed + * when the callback returns. * * The filter argument is the index of the filter that was selected, or -1 if * no filter was selected or if the platform or method doesn't support @@ -93,14 +104,13 @@ typedef struct SDL_DialogFileFilter * \sa SDL_ShowOpenFileDialog * \sa SDL_ShowSaveFileDialog * \sa SDL_ShowOpenFolderDialog + * \sa SDL_ShowFileDialogWithProperties */ typedef void (SDLCALL *SDL_DialogFileCallback)(void *userdata, const char * const *filelist, int filter); /** * Displays a dialog that lets the user select a file on their filesystem. * - * This function should only be invoked from the main thread. - * * This is an asynchronous function; it will return immediately, and the * result will be passed to the callback. * @@ -118,38 +128,34 @@ typedef void (SDLCALL *SDL_DialogFileCallback)(void *userdata, const char * cons * requires an event-handling loop. Apps that do not use SDL to handle events * should add a call to SDL_PumpEvents in their main loop. * - * \param callback an SDL_DialogFileCallback to be invoked when the user - * selects a file and accepts, or cancels the dialog, or an - * error occurs. The first argument is a null-terminated list - * of C strings, representing the paths chosen by the user. - * The list will be empty if the user canceled the dialog, and - * it will be NULL if an error occurred. If an error occurred, - * it can be fetched with SDL_GetError(). The second argument - * is the userdata pointer passed to the function. The third - * argument is the index of the filter selected by the user, - * or one past the index of the last filter (therefore the - * index of the terminating NULL filter) if no filter was - * chosen, or -1 if the platform does not support detecting - * the selected filter. + * \param callback a function pointer to be invoked when the user selects a + * file and accepts, or cancels the dialog, or an error + * occurs. * \param userdata an optional pointer to pass extra data to the callback when * it will be invoked. * \param window the window that the dialog should be modal for, may be NULL. * Not all platforms support this option. - * \param filters a list of SDL_DialogFileFilter's, may be NULL. Not all - * platforms support this option, and platforms that do support - * it may allow the user to ignore the filters. + * \param filters a list of filters, may be NULL. Not all platforms support + * this option, and platforms that do support it may allow the + * user to ignore the filters. If non-NULL, it must remain + * valid at least until the callback is invoked. * \param nfilters the number of filters. Ignored if filters is NULL. * \param default_location the default folder or file to start the dialog at, * may be NULL. Not all platforms support this option. * \param allow_many if non-zero, the user will be allowed to select multiple * entries. Not all platforms support this option. * + * \threadsafety This function should be called only from the main thread. The + * callback may be invoked from the same thread or from a + * different one, depending on the OS's constraints. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_DialogFileCallback * \sa SDL_DialogFileFilter * \sa SDL_ShowSaveFileDialog * \sa SDL_ShowOpenFolderDialog + * \sa SDL_ShowFileDialogWithProperties */ extern SDL_DECLSPEC void SDLCALL SDL_ShowOpenFileDialog(SDL_DialogFileCallback callback, void *userdata, SDL_Window *window, const SDL_DialogFileFilter *filters, int nfilters, const char *default_location, bool allow_many); @@ -157,8 +163,6 @@ extern SDL_DECLSPEC void SDLCALL SDL_ShowOpenFileDialog(SDL_DialogFileCallback c * Displays a dialog that lets the user choose a new or existing file on their * filesystem. * - * This function should only be invoked from the main thread. - * * This is an asynchronous function; it will return immediately, and the * result will be passed to the callback. * @@ -175,44 +179,38 @@ extern SDL_DECLSPEC void SDLCALL SDL_ShowOpenFileDialog(SDL_DialogFileCallback c * requires an event-handling loop. Apps that do not use SDL to handle events * should add a call to SDL_PumpEvents in their main loop. * - * \param callback an SDL_DialogFileCallback to be invoked when the user - * selects a file and accepts, or cancels the dialog, or an - * error occurs. The first argument is a null-terminated list - * of C strings, representing the paths chosen by the user. - * The list will be empty if the user canceled the dialog, and - * it will be NULL if an error occurred. If an error occurred, - * it can be fetched with SDL_GetError(). The second argument - * is the userdata pointer passed to the function. The third - * argument is the index of the filter selected by the user, - * or one past the index of the last filter (therefore the - * index of the terminating NULL filter) if no filter was - * chosen, or -1 if the platform does not support detecting - * the selected filter. + * \param callback a function pointer to be invoked when the user selects a + * file and accepts, or cancels the dialog, or an error + * occurs. * \param userdata an optional pointer to pass extra data to the callback when * it will be invoked. * \param window the window that the dialog should be modal for, may be NULL. * Not all platforms support this option. - * \param filters a list of SDL_DialogFileFilter's, may be NULL. Not all - * platforms support this option, and platforms that do support - * it may allow the user to ignore the filters. + * \param filters a list of filters, may be NULL. Not all platforms support + * this option, and platforms that do support it may allow the + * user to ignore the filters. If non-NULL, it must remain + * valid at least until the callback is invoked. * \param nfilters the number of filters. Ignored if filters is NULL. * \param default_location the default folder or file to start the dialog at, * may be NULL. Not all platforms support this option. * + * \threadsafety This function should be called only from the main thread. The + * callback may be invoked from the same thread or from a + * different one, depending on the OS's constraints. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_DialogFileCallback * \sa SDL_DialogFileFilter * \sa SDL_ShowOpenFileDialog * \sa SDL_ShowOpenFolderDialog + * \sa SDL_ShowFileDialogWithProperties */ extern SDL_DECLSPEC void SDLCALL SDL_ShowSaveFileDialog(SDL_DialogFileCallback callback, void *userdata, SDL_Window *window, const SDL_DialogFileFilter *filters, int nfilters, const char *default_location); /** * Displays a dialog that lets the user select a folder on their filesystem. * - * This function should only be invoked from the main thread. - * * This is an asynchronous function; it will return immediately, and the * result will be passed to the callback. * @@ -230,15 +228,9 @@ extern SDL_DECLSPEC void SDLCALL SDL_ShowSaveFileDialog(SDL_DialogFileCallback c * requires an event-handling loop. Apps that do not use SDL to handle events * should add a call to SDL_PumpEvents in their main loop. * - * \param callback an SDL_DialogFileCallback to be invoked when the user - * selects a file and accepts, or cancels the dialog, or an - * error occurs. The first argument is a null-terminated list - * of C strings, representing the paths chosen by the user. - * The list will be empty if the user canceled the dialog, and - * it will be NULL if an error occurred. If an error occurred, - * it can be fetched with SDL_GetError(). The second argument - * is the userdata pointer passed to the function. The third - * argument is always -1 for SDL_ShowOpenFolderDialog. + * \param callback a function pointer to be invoked when the user selects a + * file and accepts, or cancels the dialog, or an error + * occurs. * \param userdata an optional pointer to pass extra data to the callback when * it will be invoked. * \param window the window that the dialog should be modal for, may be NULL. @@ -248,14 +240,94 @@ extern SDL_DECLSPEC void SDLCALL SDL_ShowSaveFileDialog(SDL_DialogFileCallback c * \param allow_many if non-zero, the user will be allowed to select multiple * entries. Not all platforms support this option. * + * \threadsafety This function should be called only from the main thread. The + * callback may be invoked from the same thread or from a + * different one, depending on the OS's constraints. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_DialogFileCallback * \sa SDL_ShowOpenFileDialog * \sa SDL_ShowSaveFileDialog + * \sa SDL_ShowFileDialogWithProperties */ extern SDL_DECLSPEC void SDLCALL SDL_ShowOpenFolderDialog(SDL_DialogFileCallback callback, void *userdata, SDL_Window *window, const char *default_location, bool allow_many); +/** + * Various types of file dialogs. + * + * This is used by SDL_ShowFileDialogWithProperties() to decide what kind of + * dialog to present to the user. + * + * \since This enum is available since SDL 3.1.3. + * + * \sa SDL_ShowFileDialogWithProperties + */ +typedef enum SDL_FileDialogType +{ + SDL_FILEDIALOG_OPENFILE, + SDL_FILEDIALOG_SAVEFILE, + SDL_FILEDIALOG_OPENFOLDER +} SDL_FileDialogType; + +/** + * Create and launch a file dialog with the specified properties. + * + * These are the supported properties: + * + * - `SDL_PROP_FILE_DIALOG_FILTERS_POINTER`: a pointer to a list of + * SDL_DialogFileFilter structs, which will be used as filters for + * file-based selections. Ignored if the dialog is an "Open Folder" dialog. + * If non-NULL, the array of filters must remain valid at least until the + * callback is invoked. + * - `SDL_PROP_FILE_DIALOG_NFILTERS_NUMBER`: the number of filters in the + * array of filters, if it exists. + * - `SDL_PROP_FILE_DIALOG_WINDOW_POINTER`: the window that the dialog should + * be modal for. + * - `SDL_PROP_FILE_DIALOG_LOCATION_STRING`: the default folder or file to + * start the dialog at. + * - `SDL_PROP_FILE_DIALOG_MANY_BOOLEAN`: true to allow the user to select + * more than one entry. + * - `SDL_PROP_FILE_DIALOG_TITLE_STRING`: the title for the dialog. + * - `SDL_PROP_FILE_DIALOG_ACCEPT_STRING`: the label that the accept button + * should have. + * - `SDL_PROP_FILE_DIALOG_CANCEL_STRING`: the label that the cancel button + * should have. + * + * Note that each platform may or may not support any of the properties. + * + * \param type the type of file dialog. + * \param callback a function pointer to be invoked when the user selects a + * file and accepts, or cancels the dialog, or an error + * occurs. + * \param userdata an optional pointer to pass extra data to the callback when + * it will be invoked. + * \param props the properties to use. + * + * \threadsafety This function should be called only from the main thread. The + * callback may be invoked from the same thread or from a + * different one, depending on the OS's constraints. + * + * \since This function is available since SDL 3.2.0. + * + * \sa SDL_FileDialogType + * \sa SDL_DialogFileCallback + * \sa SDL_DialogFileFilter + * \sa SDL_ShowOpenFileDialog + * \sa SDL_ShowSaveFileDialog + * \sa SDL_ShowOpenFolderDialog + */ +extern SDL_DECLSPEC void SDLCALL SDL_ShowFileDialogWithProperties(SDL_FileDialogType type, SDL_DialogFileCallback callback, void *userdata, SDL_PropertiesID props); + +#define SDL_PROP_FILE_DIALOG_FILTERS_POINTER "SDL.filedialog.filters" +#define SDL_PROP_FILE_DIALOG_NFILTERS_NUMBER "SDL.filedialog.nfilters" +#define SDL_PROP_FILE_DIALOG_WINDOW_POINTER "SDL.filedialog.window" +#define SDL_PROP_FILE_DIALOG_LOCATION_STRING "SDL.filedialog.location" +#define SDL_PROP_FILE_DIALOG_MANY_BOOLEAN "SDL.filedialog.many" +#define SDL_PROP_FILE_DIALOG_TITLE_STRING "SDL.filedialog.title" +#define SDL_PROP_FILE_DIALOG_ACCEPT_STRING "SDL.filedialog.accept" +#define SDL_PROP_FILE_DIALOG_CANCEL_STRING "SDL.filedialog.cancel" + /* Ends C function definitions when using C++ */ #ifdef __cplusplus } diff --git a/libs/SDL3/include/SDL3/SDL_endian.h b/libs/SDL3/include/SDL3/SDL_endian.h index 88818044..bf1dd206 100644 --- a/libs/SDL3/include/SDL3/SDL_endian.h +++ b/libs/SDL3/include/SDL3/SDL_endian.h @@ -22,7 +22,20 @@ /** * # CategoryEndian * - * Functions for reading and writing endian-specific values. + * Functions converting endian-specific values to different byte orders. + * + * These functions either unconditionally swap byte order (SDL_Swap16, + * SDL_Swap32, SDL_Swap64, SDL_SwapFloat), or they swap to/from the system's + * native byte order (SDL_Swap16LE, SDL_Swap16BE, SDL_Swap32LE, SDL_Swap32BE, + * SDL_Swap32LE, SDL_Swap32BE, SDL_SwapFloatLE, SDL_SwapFloatBE). In the + * latter case, the functionality is provided by macros that become no-ops if + * a swap isn't necessary: on an x86 (littleendian) processor, SDL_Swap32LE + * does nothing, but SDL_Swap32BE reverses the bytes of the data. On a PowerPC + * processor (bigendian), the macros behavior is reversed. + * + * The swap routines are inline functions, and attempt to use compiler + * intrinsics, inline assembly, and other magic to make byteswapping + * efficient. */ #ifndef SDL_endian_h_ @@ -51,12 +64,71 @@ _m_prefetch(void *__P) * \name The two types of endianness */ /* @{ */ + + +/** + * A value to represent littleendian byteorder. + * + * This is used with the preprocessor macro SDL_BYTEORDER, to determine a + * platform's byte ordering: + * + * ```c + * #if SDL_BYTEORDER == SDL_LIL_ENDIAN + * SDL_Log("This system is littleendian."); + * #endif + * ``` + * + * \since This macro is available since SDL 3.1.3. + * + * \sa SDL_BYTEORDER + * \sa SDL_BIG_ENDIAN + */ #define SDL_LIL_ENDIAN 1234 + +/** + * A value to represent bigendian byteorder. + * + * This is used with the preprocessor macro SDL_BYTEORDER, to determine a + * platform's byte ordering: + * + * ```c + * #if SDL_BYTEORDER == SDL_BIG_ENDIAN + * SDL_Log("This system is bigendian."); + * #endif + * ``` + * + * \since This macro is available since SDL 3.1.3. + * + * \sa SDL_BYTEORDER + * \sa SDL_LIL_ENDIAN + */ #define SDL_BIG_ENDIAN 4321 + /* @} */ #ifndef SDL_BYTEORDER -#ifdef SDL_PLATFORM_LINUX +#ifdef SDL_WIKI_DOCUMENTATION_SECTION + +/** + * A macro that reports the target system's byte order. + * + * This is set to either SDL_LIL_ENDIAN or SDL_BIG_ENDIAN (and maybe other + * values in the future, if something else becomes popular). This can be + * tested with the preprocessor, so decisions can be made at compile time. + * + * ```c + * #if SDL_BYTEORDER == SDL_BIG_ENDIAN + * SDL_Log("This system is bigendian."); + * #endif + * ``` + * + * \since This macro is available since SDL 3.1.3. + * + * \sa SDL_LIL_ENDIAN + * \sa SDL_BIG_ENDIAN + */ +#define SDL_BYTEORDER SDL_LIL_ENDIAN___or_maybe___SDL_BIG_ENDIAN +#elif defined(SDL_PLATFORM_LINUX) #include #define SDL_BYTEORDER __BYTE_ORDER #elif defined(SDL_PLATFORM_SOLARIS) @@ -97,8 +169,29 @@ _m_prefetch(void *__P) #endif /* !SDL_BYTEORDER */ #ifndef SDL_FLOATWORDORDER +#ifdef SDL_WIKI_DOCUMENTATION_SECTION + +/** + * A macro that reports the target system's floating point word order. + * + * This is set to either SDL_LIL_ENDIAN or SDL_BIG_ENDIAN (and maybe other + * values in the future, if something else becomes popular). This can be + * tested with the preprocessor, so decisions can be made at compile time. + * + * ```c + * #if SDL_FLOATWORDORDER == SDL_BIG_ENDIAN + * SDL_Log("This system's floats are bigendian."); + * #endif + * ``` + * + * \since This macro is available since SDL 3.1.3. + * + * \sa SDL_LIL_ENDIAN + * \sa SDL_BIG_ENDIAN + */ +#define SDL_FLOATWORDORDER SDL_LIL_ENDIAN___or_maybe___SDL_BIG_ENDIAN /* predefs from newer gcc versions: */ -#if defined(__ORDER_LITTLE_ENDIAN__) && defined(__ORDER_BIG_ENDIAN__) && defined(__FLOAT_WORD_ORDER__) +#elif defined(__ORDER_LITTLE_ENDIAN__) && defined(__ORDER_BIG_ENDIAN__) && defined(__FLOAT_WORD_ORDER__) #if (__FLOAT_WORD_ORDER__ == __ORDER_LITTLE_ENDIAN__) #define SDL_FLOATWORDORDER SDL_LIL_ENDIAN #elif (__FLOAT_WORD_ORDER__ == __ORDER_BIG_ENDIAN__) @@ -125,10 +218,6 @@ _m_prefetch(void *__P) extern "C" { #endif -/** - * \file SDL_endian.h - */ - /* various modern compilers may have builtin swap */ #if defined(__GNUC__) || defined(__clang__) # define HAS_BUILTIN_BSWAP16 (SDL_HAS_BUILTIN(__builtin_bswap16)) || \ @@ -148,6 +237,7 @@ extern "C" { #endif /* Byte swap 16-bit integer. */ +#ifndef SDL_WIKI_DOCUMENTATION_SECTION #if HAS_BUILTIN_BSWAP16 #define SDL_Swap16(x) __builtin_bswap16(x) #elif (defined(_MSC_VER) && (_MSC_VER >= 1400)) && !defined(__ICL) @@ -191,8 +281,10 @@ SDL_FORCE_INLINE Uint16 SDL_Swap16(Uint16 x) return SDL_static_cast(Uint16, ((x << 8) | (x >> 8))); } #endif +#endif /* Byte swap 32-bit integer. */ +#ifndef SDL_WIKI_DOCUMENTATION_SECTION #if HAS_BUILTIN_BSWAP32 #define SDL_Swap32(x) __builtin_bswap32(x) #elif (defined(_MSC_VER) && (_MSC_VER >= 1400)) && !defined(__ICL) @@ -239,8 +331,10 @@ SDL_FORCE_INLINE Uint32 SDL_Swap32(Uint32 x) ((x >> 8) & 0x0000FF00) | (x >> 24))); } #endif +#endif /* Byte swap 64-bit integer. */ +#ifndef SDL_WIKI_DOCUMENTATION_SECTION #if HAS_BUILTIN_BSWAP64 #define SDL_Swap64(x) __builtin_bswap64(x) #elif (defined(_MSC_VER) && (_MSC_VER >= 1400)) && !defined(__ICL) @@ -290,7 +384,7 @@ SDL_FORCE_INLINE Uint64 SDL_Swap64(Uint64 x) return (x); } #endif - +#endif /** * Byte-swap a floating point number. diff --git a/libs/SDL3/include/SDL3/SDL_error.h b/libs/SDL3/include/SDL3/SDL_error.h index 5482bbbf..c96d1511 100644 --- a/libs/SDL3/include/SDL3/SDL_error.h +++ b/libs/SDL3/include/SDL3/SDL_error.h @@ -99,7 +99,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetError(SDL_PRINTF_FORMAT_STRING const cha * * \threadsafety It is safe to call this function from any thread. * - * \since This function is available since SDL 3.2.0. + * \since This function is available since SDL 3.1.6. * * \sa SDL_ClearError * \sa SDL_GetError @@ -178,8 +178,43 @@ extern SDL_DECLSPEC bool SDLCALL SDL_ClearError(void); * Private error reporting function - used internally. */ /* @{ */ + +/** + * A macro to standardize error reporting on unsupported operations. + * + * This simply calls SDL_SetError() with a standardized error string, for + * convenience, consistency, and clarity. + * + * \threadsafety It is safe to call this macro from any thread. + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_Unsupported() SDL_SetError("That operation is not supported") + +/** + * A macro to standardize error reporting on unsupported operations. + * + * This simply calls SDL_SetError() with a standardized error string, for + * convenience, consistency, and clarity. + * + * A common usage pattern inside SDL is this: + * + * ```c + * bool MyFunction(const char *str) { + * if (!str) { + * return SDL_InvalidParamError("str"); // returns false. + * } + * DoSomething(str); + * return true; + * } + * ``` + * + * \threadsafety It is safe to call this macro from any thread. + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_InvalidParamError(param) SDL_SetError("Parameter '%s' is invalid", (param)) + /* @} *//* Internal error functions */ /* Ends C function definitions when using C++ */ diff --git a/libs/SDL3/include/SDL3/SDL_events.h b/libs/SDL3/include/SDL3/SDL_events.h index 5b3d4752..2850b763 100644 --- a/libs/SDL3/include/SDL3/SDL_events.h +++ b/libs/SDL3/include/SDL3/SDL_events.h @@ -23,6 +23,30 @@ * # CategoryEvents * * Event queue management. + * + * It's extremely common--often required--that an app deal with SDL's event + * queue. Almost all useful information about interactions with the real world + * flow through here: the user interacting with the computer and app, hardware + * coming and going, the system changing in some way, etc. + * + * An app generally takes a moment, perhaps at the start of a new frame, to + * examine any events that have occured since the last time and process or + * ignore them. This is generally done by calling SDL_PollEvent() in a loop + * until it returns false (or, if using the main callbacks, events are + * provided one at a time in calls to SDL_AppEvent() before the next call to + * SDL_AppIterate(); in this scenario, the app does not call SDL_PollEvent() + * at all). + * + * There is other forms of control, too: SDL_PeepEvents() has more + * functionality at the cost of more complexity, and SDL_WaitEvents() can + * block the process until something interesting happens, which might be + * beneficial for certain types of programs on low-power hardware. One may + * also call SDL_AddEventWatch() to set a callback when new events arrive. + * + * The app is free to generate their own events, too: SDL_PushEvent allows the + * app to put events onto the queue for later retrieval; SDL_RegisterEvents + * can guarantee that these events have a type that isn't in use by other + * parts of the system. */ #ifndef SDL_events_h_ @@ -334,8 +358,8 @@ typedef struct SDL_KeyboardEvent SDL_Keycode key; /**< SDL virtual key code */ SDL_Keymod mod; /**< current key modifiers */ Uint16 raw; /**< The platform dependent scancode for this event */ - bool down; /**< true if the key is pressed */ - bool repeat; /**< true if this is a key repeat */ + bool down; /**< true if the key is pressed */ + bool repeat; /**< true if this is a key repeat */ } SDL_KeyboardEvent; /** @@ -422,7 +446,7 @@ typedef struct SDL_MouseMotionEvent Uint32 reserved; Uint64 timestamp; /**< In nanoseconds, populated using SDL_GetTicksNS() */ SDL_WindowID windowID; /**< The window with mouse focus, if any */ - SDL_MouseID which; /**< The mouse instance id or SDL_TOUCH_MOUSEID */ + SDL_MouseID which; /**< The mouse instance id in relative mode, SDL_TOUCH_MOUSEID for touch events, or 0 */ SDL_MouseButtonFlags state; /**< The current button state */ float x; /**< X coordinate, relative to window */ float y; /**< Y coordinate, relative to window */ @@ -441,9 +465,9 @@ typedef struct SDL_MouseButtonEvent Uint32 reserved; Uint64 timestamp; /**< In nanoseconds, populated using SDL_GetTicksNS() */ SDL_WindowID windowID; /**< The window with mouse focus, if any */ - SDL_MouseID which; /**< The mouse instance id, SDL_TOUCH_MOUSEID */ + SDL_MouseID which; /**< The mouse instance id in relative mode, SDL_TOUCH_MOUSEID for touch events, or 0 */ Uint8 button; /**< The mouse button index */ - bool down; /**< true if the button is pressed */ + bool down; /**< true if the button is pressed */ Uint8 clicks; /**< 1 for single-click, 2 for double-click, etc. */ Uint8 padding; float x; /**< X coordinate, relative to window */ @@ -461,7 +485,7 @@ typedef struct SDL_MouseWheelEvent Uint32 reserved; Uint64 timestamp; /**< In nanoseconds, populated using SDL_GetTicksNS() */ SDL_WindowID windowID; /**< The window with mouse focus, if any */ - SDL_MouseID which; /**< The mouse instance id, SDL_TOUCH_MOUSEID */ + SDL_MouseID which; /**< The mouse instance id in relative mode or 0 */ float x; /**< The amount scrolled horizontally, positive to the right and negative to the left */ float y; /**< The amount scrolled vertically, positive away from the user and negative toward the user */ SDL_MouseWheelDirection direction; /**< Set to one of the SDL_MOUSEWHEEL_* defines. When FLIPPED the values in X and Y will be opposite. Multiply by -1 to change them back */ @@ -704,6 +728,21 @@ typedef struct SDL_CameraDeviceEvent SDL_CameraID which; /**< SDL_CameraID for the device being added or removed or changing */ } SDL_CameraDeviceEvent; + +/** + * Renderer event structure (event.render.*) + * + * \since This struct is available since SDL 3.1.7. + */ +typedef struct SDL_RenderEvent +{ + SDL_EventType type; /**< SDL_EVENT_RENDER_TARGETS_RESET, SDL_EVENT_RENDER_DEVICE_RESET, SDL_EVENT_RENDER_DEVICE_LOST */ + Uint32 reserved; + Uint64 timestamp; /**< In nanoseconds, populated using SDL_GetTicksNS() */ + SDL_WindowID windowID; /**< The window containing the renderer in question. */ +} SDL_RenderEvent; + + /** * Touch finger event structure (event.tfinger.*) * @@ -758,7 +797,7 @@ typedef struct SDL_PenProximityEvent SDL_EventType type; /**< SDL_EVENT_PEN_PROXIMITY_IN or SDL_EVENT_PEN_PROXIMITY_OUT */ Uint32 reserved; Uint64 timestamp; /**< In nanoseconds, populated using SDL_GetTicksNS() */ - SDL_WindowID windowID; /**< The window with mouse focus, if any */ + SDL_WindowID windowID; /**< The window with pen focus, if any */ SDL_PenID which; /**< The pen instance id */ } SDL_PenProximityEvent; @@ -778,7 +817,7 @@ typedef struct SDL_PenMotionEvent SDL_EventType type; /**< SDL_EVENT_PEN_MOTION */ Uint32 reserved; Uint64 timestamp; /**< In nanoseconds, populated using SDL_GetTicksNS() */ - SDL_WindowID windowID; /**< The window with mouse focus, if any */ + SDL_WindowID windowID; /**< The window with pen focus, if any */ SDL_PenID which; /**< The pen instance id */ SDL_PenInputFlags pen_state; /**< Complete pen input state at time of event */ float x; /**< X coordinate, relative to window */ @@ -980,6 +1019,7 @@ typedef union SDL_Event SDL_PenMotionEvent pmotion; /**< Pen motion event data */ SDL_PenButtonEvent pbutton; /**< Pen button event data */ SDL_PenAxisEvent paxis; /**< Pen axis event data */ + SDL_RenderEvent render; /**< Render event data */ SDL_DropEvent drop; /**< Drag and drop event data */ SDL_ClipboardEvent clipboard; /**< Clipboard event data */ @@ -1018,9 +1058,7 @@ SDL_COMPILE_TIME_ASSERT(SDL_Event, sizeof(SDL_Event) == sizeof(((SDL_Event *)NUL * polling or waiting for events (e.g. you are filtering them), then you must * call SDL_PumpEvents() to force an event queue update. * - * \threadsafety This should only be run in the thread that initialized the - * video subsystem, and for extra safety, you should consider - * only doing those things on the main thread in any case. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -1218,9 +1256,7 @@ extern SDL_DECLSPEC void SDLCALL SDL_FlushEvents(Uint32 minType, Uint32 maxType) * the queue, or NULL. * \returns true if this got an event or false if there are none available. * - * \threadsafety This should only be run in the thread that initialized the - * video subsystem, and for extra safety, you should consider - * only doing those things on the main thread in any case. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -1244,9 +1280,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_PollEvent(SDL_Event *event); * \returns true on success or false if there was an error while waiting for * events; call SDL_GetError() for more information. * - * \threadsafety This should only be run in the thread that initialized the - * video subsystem, and for extra safety, you should consider - * only doing those things on the main thread in any case. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -1276,9 +1310,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_WaitEvent(SDL_Event *event); * \returns true if this got an event or false if the timeout elapsed without * any events available. * - * \threadsafety This should only be run in the thread that initialized the - * video subsystem, and for extra safety, you should consider - * only doing those things on the main thread in any case. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * diff --git a/libs/SDL3/include/SDL3/SDL_filesystem.h b/libs/SDL3/include/SDL3/SDL_filesystem.h index bf403e28..13234afd 100644 --- a/libs/SDL3/include/SDL3/SDL_filesystem.h +++ b/libs/SDL3/include/SDL3/SDL_filesystem.h @@ -166,29 +166,17 @@ extern SDL_DECLSPEC char * SDLCALL SDL_GetPrefPath(const char *org, const char * typedef enum SDL_Folder { SDL_FOLDER_HOME, /**< The folder which contains all of the current user's data, preferences, and documents. It usually contains most of the other folders. If a requested folder does not exist, the home folder can be considered a safe fallback to store a user's documents. */ - SDL_FOLDER_DESKTOP, /**< The folder of files that are displayed on the desktop. Note that the existence of a desktop folder does not guarantee that the system does show icons on its desktop; certain GNU/Linux distros with a graphical environment may not have desktop icons. */ - SDL_FOLDER_DOCUMENTS, /**< User document files, possibly application-specific. This is a good place to save a user's projects. */ - SDL_FOLDER_DOWNLOADS, /**< Standard folder for user files downloaded from the internet. */ - SDL_FOLDER_MUSIC, /**< Music files that can be played using a standard music player (mp3, ogg...). */ - SDL_FOLDER_PICTURES, /**< Image files that can be displayed using a standard viewer (png, jpg...). */ - SDL_FOLDER_PUBLICSHARE, /**< Files that are meant to be shared with other users on the same computer. */ - SDL_FOLDER_SAVEDGAMES, /**< Save files for games. */ - SDL_FOLDER_SCREENSHOTS, /**< Application screenshots. */ - SDL_FOLDER_TEMPLATES, /**< Template files to be used when the user requests the desktop environment to create a new file in a certain folder, such as "New Text File.txt". Any file in the Templates folder can be used as a starting point for a new file. */ - SDL_FOLDER_VIDEOS, /**< Video files that can be played using a standard video player (mp4, webm...). */ - SDL_FOLDER_COUNT /**< Total number of types in this enum, not a folder type by itself. */ - } SDL_Folder; /** @@ -219,6 +207,17 @@ extern SDL_DECLSPEC const char * SDLCALL SDL_GetUserFolder(SDL_Folder folder); /* Abstract filesystem interface */ +/** + * Types of filesystem entries. + * + * Note that there may be other sorts of items on a filesystem: devices, + * symlinks, named pipes, etc. They are currently reported as + * SDL_PATHTYPE_OTHER. + * + * \since This enum is available since SDL 3.1.3. + * + * \sa SDL_PathInfo + */ typedef enum SDL_PathType { SDL_PATHTYPE_NONE, /**< path does not exist */ @@ -227,6 +226,14 @@ typedef enum SDL_PathType SDL_PATHTYPE_OTHER /**< something completely different like a device node (not a symlink, those are always followed) */ } SDL_PathType; +/** + * Information about a path on the filesystem. + * + * \since This datatype is available since SDL 3.1.3. + * + * \sa SDL_GetPathInfo + * \sa SDL_GetStoragePathInfo + */ typedef struct SDL_PathInfo { SDL_PathType type; /**< the path type */ @@ -237,7 +244,7 @@ typedef struct SDL_PathInfo } SDL_PathInfo; /** - * Flags for path matching + * Flags for path matching. * * \since This datatype is available since SDL 3.1.3. * @@ -447,6 +454,24 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetPathInfo(const char *path, SDL_PathInfo */ extern SDL_DECLSPEC char ** SDLCALL SDL_GlobDirectory(const char *path, const char *pattern, SDL_GlobFlags flags, int *count); +/** + * Get what the system believes is the "current working directory." + * + * For systems without a concept of a current working directory, this will + * still attempt to provide something reasonable. + * + * SDL does not provide a means to _change_ the current working directory; for + * platforms without this concept, this would cause surprises with file access + * outside of SDL. + * + * \returns a UTF-8 string of the current working directory in + * platform-dependent notation. NULL if there's a problem. This + * should be freed with SDL_free() when it is no longer needed. + * + * \since This function is available since SDL 3.2.0. + */ +extern SDL_DECLSPEC char * SDLCALL SDL_GetCurrentDirectory(void); + /* Ends C function definitions when using C++ */ #ifdef __cplusplus } diff --git a/libs/SDL3/include/SDL3/SDL_gpu.h b/libs/SDL3/include/SDL3/SDL_gpu.h index e20c443c..6d8b90e7 100644 --- a/libs/SDL3/include/SDL3/SDL_gpu.h +++ b/libs/SDL3/include/SDL3/SDL_gpu.h @@ -25,7 +25,7 @@ * # CategoryGPU * * The GPU API offers a cross-platform way for apps to talk to modern graphics - * hardware. It offers both 3D graphics and "compute" support, in the style of + * hardware. It offers both 3D graphics and compute support, in the style of * Metal, Vulkan, and Direct3D 12. * * A basic workflow might be something like this: @@ -56,7 +56,7 @@ * Rendering can happen to a texture (what other APIs call a "render target") * or it can happen to the swapchain texture (which is just a special texture * that represents a window's contents). The app can use - * SDL_AcquireGPUSwapchainTexture() to render to the window. + * SDL_WaitAndAcquireGPUSwapchainTexture() to render to the window. * * Rendering actually happens in a Render Pass, which is encoded into a * command buffer. One can encode multiple render passes (or alternate between @@ -96,8 +96,8 @@ * efficient way of doing this, provided that the app is willing to tolerate * some latency. When the app uses SDL_DownloadFromGPUTexture() or * SDL_DownloadFromGPUBuffer(), submitting the command buffer with - * SubmitGPUCommandBufferAndAcquireFence() will return a fence handle that the - * app can poll or wait on in a thread. Once the fence indicates that the + * SDL_SubmitGPUCommandBufferAndAcquireFence() will return a fence handle that + * the app can poll or wait on in a thread. Once the fence indicates that the * command buffer is done processing, it is safe to read the downloaded data. * Make sure to call SDL_ReleaseGPUFence() when done with the fence. * @@ -129,7 +129,7 @@ * * It is optimal for apps to pre-compile the shader formats they might use, * but for ease of use SDL provides a separate project, - * [SDL_gpu_shadercross](https://github.com/libsdl-org/SDL_gpu_shadercross) + * [SDL_shadercross](https://github.com/libsdl-org/SDL_shadercross) * , for performing runtime shader cross-compilation. * * This is an extremely quick overview that leaves out several important @@ -148,6 +148,35 @@ * Examples demonstrating proper usage of this API can be found * [here](https://github.com/TheSpydog/SDL_gpu_examples) * . + * + * ## FAQ + * + * **Question: When are you adding more advanced features, like ray tracing or + * mesh shaders?** + * + * Answer: We don't have immediate plans to add more bleeding-edge features, + * but we certainly might in the future, when these features prove worthwhile, + * and reasonable to implement across several platforms and underlying APIs. + * So while these things are not in the "never" category, they are definitely + * not "near future" items either. + * + * ## System Requirements + * + * **Vulkan:** Supported on Windows, Linux, Nintendo Switch, and certain + * Android devices. Requires Vulkan 1.0 with the following extensions and + * device features: - `VK_KHR_swapchain` - `VK_KHR_maintenance1` - + * `independentBlend` - `imageCubeArray` - `depthClamp` - `shaderClipDistance` + * - `drawIndirectFirstInstance` + * + * **D3D12:** Supported on Windows 10 or newer, Xbox One (GDK), and Xbox + * Series X|S (GDK). Requires a GPU that supports DirectX 12 Feature Level + * 11_1. + * + * **Metal:** Supported on macOS 10.14+ and iOS/tvOS 13.0+. Hardware + * requirements vary by operating system: - macOS requires an Apple Silicon or + * [Intel Mac2 family](https://developer.apple.com/documentation/metal/mtlfeatureset/mtlfeatureset_macos_gpufamily2_v1?language=objc) + * GPU - iOS/tvOS requires an A9 GPU or newer - iOS Simulator and tvOS + * Simulator are unsupported */ #ifndef SDL_gpu_h_ @@ -782,8 +811,8 @@ typedef Uint32 SDL_GPUShaderFormat; #define SDL_GPU_SHADERFORMAT_INVALID 0 #define SDL_GPU_SHADERFORMAT_PRIVATE (1u << 0) /**< Shaders for NDA'd platforms. */ #define SDL_GPU_SHADERFORMAT_SPIRV (1u << 1) /**< SPIR-V shaders for Vulkan. */ -#define SDL_GPU_SHADERFORMAT_DXBC (1u << 2) /**< DXBC SM5_0 shaders for D3D11. */ -#define SDL_GPU_SHADERFORMAT_DXIL (1u << 3) /**< DXIL shaders for D3D12. */ +#define SDL_GPU_SHADERFORMAT_DXBC (1u << 2) /**< DXBC SM5_1 shaders for D3D12. */ +#define SDL_GPU_SHADERFORMAT_DXIL (1u << 3) /**< DXIL SM6_0 shaders for D3D12. */ #define SDL_GPU_SHADERFORMAT_MSL (1u << 4) /**< MSL shaders for Metal. */ #define SDL_GPU_SHADERFORMAT_METALLIB (1u << 5) /**< Precompiled metallib shaders for Metal. */ @@ -1057,32 +1086,26 @@ typedef enum SDL_GPUSamplerAddressMode * Specifies the timing that will be used to present swapchain textures to the * OS. * - * Note that this value affects the behavior of - * SDL_AcquireGPUSwapchainTexture. VSYNC mode will always be supported. - * IMMEDIATE and MAILBOX modes may not be supported on certain systems. + * VSYNC mode will always be supported. IMMEDIATE and MAILBOX modes may not be + * supported on certain systems. * * It is recommended to query SDL_WindowSupportsGPUPresentMode after claiming * the window if you wish to change the present mode to IMMEDIATE or MAILBOX. * * - VSYNC: Waits for vblank before presenting. No tearing is possible. If * there is a pending image to present, the new image is enqueued for - * presentation. Disallows tearing at the cost of visual latency. When using - * this present mode, AcquireGPUSwapchainTexture will block if too many - * frames are in flight. + * presentation. Disallows tearing at the cost of visual latency. * - IMMEDIATE: Immediately presents. Lowest latency option, but tearing may - * occur. When using this mode, AcquireGPUSwapchainTexture will fill the - * swapchain texture pointer with NULL if too many frames are in flight. + * occur. * - MAILBOX: Waits for vblank before presenting. No tearing is possible. If * there is a pending image to present, the pending image is replaced by the - * new image. Similar to VSYNC, but with reduced visual latency. When using - * this mode, AcquireGPUSwapchainTexture will fill the swapchain texture - * pointer with NULL if too many frames are in flight. + * new image. Similar to VSYNC, but with reduced visual latency. * * \since This enum is available since SDL 3.1.3 * * \sa SDL_SetGPUSwapchainParameters * \sa SDL_WindowSupportsGPUPresentMode - * \sa SDL_AcquireGPUSwapchainTexture + * \sa SDL_WaitAndAcquireGPUSwapchainTexture */ typedef enum SDL_GPUPresentMode { @@ -1114,7 +1137,7 @@ typedef enum SDL_GPUPresentMode * * \sa SDL_SetGPUSwapchainParameters * \sa SDL_WindowSupportsGPUSwapchainComposition - * \sa SDL_AcquireGPUSwapchainTexture + * \sa SDL_WaitAndAcquireGPUSwapchainTexture */ typedef enum SDL_GPUSwapchainComposition { @@ -1499,13 +1522,6 @@ typedef struct SDL_GPUTextureCreateInfo SDL_PropertiesID props; /**< A properties ID for extensions. Should be 0 if no extensions are needed. */ } SDL_GPUTextureCreateInfo; -#define SDL_PROP_GPU_CREATETEXTURE_D3D12_CLEAR_R_FLOAT "SDL.gpu.createtexture.d3d12.clear.r" -#define SDL_PROP_GPU_CREATETEXTURE_D3D12_CLEAR_G_FLOAT "SDL.gpu.createtexture.d3d12.clear.g" -#define SDL_PROP_GPU_CREATETEXTURE_D3D12_CLEAR_B_FLOAT "SDL.gpu.createtexture.d3d12.clear.b" -#define SDL_PROP_GPU_CREATETEXTURE_D3D12_CLEAR_A_FLOAT "SDL.gpu.createtexture.d3d12.clear.a" -#define SDL_PROP_GPU_CREATETEXTURE_D3D12_CLEAR_DEPTH_FLOAT "SDL.gpu.createtexture.d3d12.clear.depth" -#define SDL_PROP_GPU_CREATETEXTURE_D3D12_CLEAR_STENCIL_UINT8 "SDL.gpu.createtexture.d3d12.clear.stencil" - /** * A structure specifying the parameters of a buffer. * @@ -1515,6 +1531,7 @@ typedef struct SDL_GPUTextureCreateInfo * \since This struct is available since SDL 3.1.3 * * \sa SDL_CreateGPUBuffer + * \sa SDL_GPUBufferUsageFlags */ typedef struct SDL_GPUBufferCreateInfo { @@ -1648,6 +1665,12 @@ typedef struct SDL_GPUGraphicsPipelineTargetInfo * \since This struct is available since SDL 3.1.3 * * \sa SDL_CreateGPUGraphicsPipeline + * \sa SDL_GPUVertexInputState + * \sa SDL_GPUPrimitiveType + * \sa SDL_GPURasterizerState + * \sa SDL_GPUMultisampleState + * \sa SDL_GPUDepthStencilState + * \sa SDL_GPUGraphicsPipelineTargetInfo */ typedef struct SDL_GPUGraphicsPipelineCreateInfo { @@ -1827,11 +1850,11 @@ typedef struct SDL_GPUBlitInfo { * \since This struct is available since SDL 3.1.3 * * \sa SDL_BindGPUVertexBuffers - * \sa SDL_BindGPUIndexBuffers + * \sa SDL_BindGPUIndexBuffer */ typedef struct SDL_GPUBufferBinding { - SDL_GPUBuffer *buffer; /**< The buffer to bind. Must have been created with SDL_GPU_BUFFERUSAGE_VERTEX for SDL_BindGPUVertexBuffers, or SDL_GPU_BUFFERUSAGE_INDEX for SDL_BindGPUIndexBuffers. */ + SDL_GPUBuffer *buffer; /**< The buffer to bind. Must have been created with SDL_GPU_BUFFERUSAGE_VERTEX for SDL_BindGPUVertexBuffers, or SDL_GPU_BUFFERUSAGE_INDEX for SDL_BindGPUIndexBuffer. */ Uint32 offset; /**< The starting byte of the data to bind in the buffer. */ } SDL_GPUBufferBinding; @@ -2073,31 +2096,23 @@ extern SDL_DECLSPEC SDL_GPUShaderFormat SDLCALL SDL_GetGPUShaderFormats(SDL_GPUD * * - 0: Sampled textures, followed by read-only storage textures, followed by * read-only storage buffers - * - 1: Write-only storage textures, followed by write-only storage buffers + * - 1: Read-write storage textures, followed by read-write storage buffers * - 2: Uniform buffers * - * For DXBC Shader Model 5_0 shaders, use the following register order: - * - * - t registers: Sampled textures, followed by read-only storage textures, - * followed by read-only storage buffers - * - u registers: Write-only storage textures, followed by write-only storage - * buffers - * - b registers: Uniform buffers - * - * For DXIL shaders, use the following register order: + * For DXBC and DXIL shaders, use the following register order: * * - (t[n], space0): Sampled textures, followed by read-only storage textures, * followed by read-only storage buffers - * - (u[n], space1): Write-only storage textures, followed by write-only + * - (u[n], space1): Read-write storage textures, followed by read-write * storage buffers * - (b[n], space2): Uniform buffers * * For MSL/metallib, use the following order: * - * - [[buffer]]: Uniform buffers, followed by write-only storage buffers, - * followed by write-only storage buffers + * - [[buffer]]: Uniform buffers, followed by read-only storage buffers, + * followed by read-write storage buffers * - [[texture]]: Sampled textures, followed by read-only storage textures, - * followed by write-only storage textures + * followed by read-write storage textures * * \param device a GPU Context. * \param createinfo a struct describing the state of the compute pipeline to @@ -2146,7 +2161,7 @@ extern SDL_DECLSPEC SDL_GPUGraphicsPipeline *SDLCALL SDL_CreateGPUGraphicsPipeli * * \sa SDL_BindGPUVertexSamplers * \sa SDL_BindGPUFragmentSamplers - * \sa SDL_ReleaseSampler + * \sa SDL_ReleaseGPUSampler */ extern SDL_DECLSPEC SDL_GPUSampler *SDLCALL SDL_CreateGPUSampler( SDL_GPUDevice *device, @@ -2172,14 +2187,7 @@ extern SDL_DECLSPEC SDL_GPUSampler *SDLCALL SDL_CreateGPUSampler( * buffers * - 3: Uniform buffers * - * For DXBC Shader Model 5_0 shaders, use the following register order: - * - * - t registers: Sampled textures, followed by storage textures, followed by - * storage buffers - * - s registers: Samplers with indices corresponding to the sampled textures - * - b registers: Uniform buffers - * - * For DXIL shaders, use the following register order: + * For DXBC and DXIL shaders, use the following register order: * * For vertex shaders: * @@ -2205,7 +2213,16 @@ extern SDL_DECLSPEC SDL_GPUSampler *SDLCALL SDL_CreateGPUSampler( * is bound at [[buffer(14)]], vertex buffer 1 at [[buffer(15)]], and so on. * Rather than manually authoring vertex buffer indices, use the * [[stage_in]] attribute which will automatically use the vertex input - * information from the SDL_GPUPipeline. + * information from the SDL_GPUGraphicsPipeline. + * + * Shader semantics other than system-value semantics do not matter in D3D12 + * and for ease of use the SDL implementation assumes that non system-value + * semantics will all be TEXCOORD. If you are using HLSL as the shader source + * language, your vertex semantics should start at TEXCOORD0 and increment + * like so: TEXCOORD1, TEXCOORD2, etc. If you wish to change the semantic + * prefix to something other than TEXCOORD you can use + * SDL_PROP_GPU_DEVICE_CREATE_D3D12_SEMANTIC_NAME_STRING with + * SDL_CreateGPUDeviceWithProperties(). * * \param device a GPU Context. * \param createinfo a struct describing the state of the shader to create. @@ -2234,6 +2251,31 @@ extern SDL_DECLSPEC SDL_GPUShader *SDLCALL SDL_CreateGPUShader( * implementation will automatically fall back to the highest available sample * count. * + * There are optional properties that can be provided through + * SDL_GPUTextureCreateInfo's `props`. These are the supported properties: + * + * - `SDL_PROP_PROCESS_CREATE_ARGS_POINTER`: an array of strings containing + * the program to run, any arguments, and a NULL pointer, e.g. const char + * *args[] = { "myprogram", "argument", NULL }. This is a required property. + * - `SDL_PROP_GPU_CREATETEXTURE_D3D12_CLEAR_R_FLOAT`: (Direct3D 12 only) if + * the texture usage is SDL_GPU_TEXTUREUSAGE_COLOR_TARGET, clear the texture + * to a color with this red intensity. Defaults to zero. + * - `SDL_PROP_GPU_CREATETEXTURE_D3D12_CLEAR_G_FLOAT`: (Direct3D 12 only) if + * the texture usage is SDL_GPU_TEXTUREUSAGE_COLOR_TARGET, clear the texture + * to a color with this green intensity. Defaults to zero. + * - `SDL_PROP_GPU_CREATETEXTURE_D3D12_CLEAR_B_FLOAT`: (Direct3D 12 only) if + * the texture usage is SDL_GPU_TEXTUREUSAGE_COLOR_TARGET, clear the texture + * to a color with this blue intensity. Defaults to zero. + * - `SDL_PROP_GPU_CREATETEXTURE_D3D12_CLEAR_A_FLOAT`: (Direct3D 12 only) if + * the texture usage is SDL_GPU_TEXTUREUSAGE_COLOR_TARGET, clear the texture + * to a color with this alpha intensity. Defaults to zero. + * - `SDL_PROP_GPU_CREATETEXTURE_D3D12_CLEAR_DEPTH_FLOAT`: (Direct3D 12 only) + * if the texture usage is SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET, clear + * the texture to a depth of this value. Defaults to zero. + * - `SDL_PROP_GPU_CREATETEXTURE_D3D12_CLEAR_STENCIL_UINT8`: (Direct3D 12 + * only) if the texture usage is SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET, + * clear the texture to a stencil of this value. Defaults to zero. + * * \param device a GPU Context. * \param createinfo a struct describing the state of the texture to create. * \returns a texture object on success, or NULL on failure; call @@ -2256,6 +2298,14 @@ extern SDL_DECLSPEC SDL_GPUTexture *SDLCALL SDL_CreateGPUTexture( SDL_GPUDevice *device, const SDL_GPUTextureCreateInfo *createinfo); +#define SDL_PROP_GPU_CREATETEXTURE_D3D12_CLEAR_R_FLOAT "SDL.gpu.createtexture.d3d12.clear.r" +#define SDL_PROP_GPU_CREATETEXTURE_D3D12_CLEAR_G_FLOAT "SDL.gpu.createtexture.d3d12.clear.g" +#define SDL_PROP_GPU_CREATETEXTURE_D3D12_CLEAR_B_FLOAT "SDL.gpu.createtexture.d3d12.clear.b" +#define SDL_PROP_GPU_CREATETEXTURE_D3D12_CLEAR_A_FLOAT "SDL.gpu.createtexture.d3d12.clear.a" +#define SDL_PROP_GPU_CREATETEXTURE_D3D12_CLEAR_DEPTH_FLOAT "SDL.gpu.createtexture.d3d12.clear.depth" +#define SDL_PROP_GPU_CREATETEXTURE_D3D12_CLEAR_STENCIL_UINT8 "SDL.gpu.createtexture.d3d12.clear.stencil" + + /** * Creates a buffer object to be used in graphics or compute workflows. * @@ -2265,6 +2315,11 @@ extern SDL_DECLSPEC SDL_GPUTexture *SDLCALL SDL_CreateGPUTexture( * Note that certain combinations of usage flags are invalid. For example, a * buffer cannot have both the VERTEX and INDEX flags. * + * For better understanding of underlying concepts and memory management with + * SDL GPU API, you may refer + * [this blog post](https://moonside.games/posts/sdl-gpu-concepts-cycling/) + * . + * * \param device a GPU Context. * \param createinfo a struct describing the state of the buffer to create. * \returns a buffer object on success, or NULL on failure; call @@ -3446,9 +3501,12 @@ extern SDL_DECLSPEC bool SDLCALL SDL_WindowSupportsGPUPresentMode( * \returns true on success, or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called from the thread that + * created the window. + * * \since This function is available since SDL 3.1.3. * - * \sa SDL_AcquireGPUSwapchainTexture + * \sa SDL_WaitAndAcquireGPUSwapchainTexture * \sa SDL_ReleaseWindowFromGPUDevice * \sa SDL_WindowSupportsGPUPresentMode * \sa SDL_WindowSupportsGPUSwapchainComposition @@ -3500,6 +3558,35 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetGPUSwapchainParameters( SDL_GPUSwapchainComposition swapchain_composition, SDL_GPUPresentMode present_mode); +/** + * Configures the maximum allowed number of frames in flight. + * + * The default value when the device is created is 2. This means that after + * you have submitted 2 frames for presentation, if the GPU has not finished + * working on the first frame, SDL_AcquireGPUSwapchainTexture() will fill the + * swapchain texture pointer with NULL, and + * SDL_WaitAndAcquireGPUSwapchainTexture() will block. + * + * Higher values increase throughput at the expense of visual latency. Lower + * values decrease visual latency at the expense of throughput. + * + * Note that calling this function will stall and flush the command queue to + * prevent synchronization issues. + * + * The minimum value of allowed frames in flight is 1, and the maximum is 3. + * + * \param device a GPU context. + * \param allowed_frames_in_flight the maximum number of frames that can be + * pending on the GPU. + * \returns true if successful, false on error; call SDL_GetError() for more + * information. + * + * \since This function is available since SDL 3.2.0. + */ +extern SDL_DECLSPEC bool SDLCALL SDL_SetGPUAllowedFramesInFlight( + SDL_GPUDevice *device, + Uint32 allowed_frames_in_flight); + /** * Obtains the texture format of the swapchain for the given window. * @@ -3521,20 +3608,21 @@ extern SDL_DECLSPEC SDL_GPUTextureFormat SDLCALL SDL_GetGPUSwapchainTextureForma * When a swapchain texture is acquired on a command buffer, it will * automatically be submitted for presentation when the command buffer is * submitted. The swapchain texture should only be referenced by the command - * buffer used to acquire it. The swapchain texture handle can be filled in - * with NULL under certain conditions. This is not necessarily an error. If - * this function returns false then there is an error. + * buffer used to acquire it. + * + * This function will fill the swapchain texture handle with NULL if too many + * frames are in flight. This is not an error. + * + * If you use this function, it is possible to create a situation where many + * command buffers are allocated while the rendering context waits for the GPU + * to catch up, which will cause memory usage to grow. You should use + * SDL_WaitAndAcquireGPUSwapchainTexture() unless you know what you are doing + * with timing. * * The swapchain texture is managed by the implementation and must not be * freed by the user. You MUST NOT call this function from any thread other * than the one that created the window. * - * When using SDL_GPU_PRESENTMODE_VSYNC, this function will block if too many - * frames are in flight. Otherwise, this function will fill the swapchain - * texture handle with NULL if too many frames are in flight. The best - * practice is to call SDL_CancelGPUCommandBuffer if the swapchain texture - * handle is NULL to avoid enqueuing needless work on the GPU. - * * \param command_buffer a command buffer. * \param window a window that has been claimed. * \param swapchain_texture a pointer filled in with a swapchain texture @@ -3546,14 +3634,19 @@ extern SDL_DECLSPEC SDL_GPUTextureFormat SDLCALL SDL_GetGPUSwapchainTextureForma * \returns true on success, false on error; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called from the thread that + * created the window. + * * \since This function is available since SDL 3.1.3. * - * \sa SDL_GPUPresentMode * \sa SDL_ClaimWindowForGPUDevice * \sa SDL_SubmitGPUCommandBuffer * \sa SDL_SubmitGPUCommandBufferAndAcquireFence * \sa SDL_CancelGPUCommandBuffer * \sa SDL_GetWindowSizeInPixels + * \sa SDL_WaitForGPUSwapchain + * \sa SDL_WaitAndAcquireGPUSwapchainTexture + * \sa SDL_SetGPUAllowedFramesInFlight */ extern SDL_DECLSPEC bool SDLCALL SDL_AcquireGPUSwapchainTexture( SDL_GPUCommandBuffer *command_buffer, @@ -3562,6 +3655,72 @@ extern SDL_DECLSPEC bool SDLCALL SDL_AcquireGPUSwapchainTexture( Uint32 *swapchain_texture_width, Uint32 *swapchain_texture_height); +/** + * Blocks the thread until a swapchain texture is available to be acquired. + * + * \param device a GPU context. + * \param window a window that has been claimed. + * \returns true on success, false on failure; call SDL_GetError() for more + * information. + * + * \threadsafety This function should only be called from the thread that + * created the window. + * + * \since This function is available since SDL 3.2.0. + * + * \sa SDL_AcquireGPUSwapchainTexture + * \sa SDL_WaitAndAcquireGPUSwapchainTexture + * \sa SDL_SetGPUAllowedFramesInFlight + */ +extern SDL_DECLSPEC bool SDLCALL SDL_WaitForGPUSwapchain( + SDL_GPUDevice *device, + SDL_Window *window); + +/** + * Blocks the thread until a swapchain texture is available to be acquired, + * and then acquires it. + * + * When a swapchain texture is acquired on a command buffer, it will + * automatically be submitted for presentation when the command buffer is + * submitted. The swapchain texture should only be referenced by the command + * buffer used to acquire it. It is an error to call + * SDL_CancelGPUCommandBuffer() after a swapchain texture is acquired. + * + * This function can fill the swapchain texture handle with NULL in certain + * cases, for example if the window is minimized. This is not an error. You + * should always make sure to check whether the pointer is NULL before + * actually using it. + * + * The swapchain texture is managed by the implementation and must not be + * freed by the user. You MUST NOT call this function from any thread other + * than the one that created the window. + * + * \param command_buffer a command buffer. + * \param window a window that has been claimed. + * \param swapchain_texture a pointer filled in with a swapchain texture + * handle. + * \param swapchain_texture_width a pointer filled in with the swapchain + * texture width, may be NULL. + * \param swapchain_texture_height a pointer filled in with the swapchain + * texture height, may be NULL. + * \returns true on success, false on error; call SDL_GetError() for more + * information. + * + * \threadsafety This function should only be called from the thread that + * created the window. + * + * \since This function is available since SDL 3.2.0. + * + * \sa SDL_SubmitGPUCommandBuffer + * \sa SDL_SubmitGPUCommandBufferAndAcquireFence + */ +extern SDL_DECLSPEC bool SDLCALL SDL_WaitAndAcquireGPUSwapchainTexture( + SDL_GPUCommandBuffer *command_buffer, + SDL_Window *window, + SDL_GPUTexture **swapchain_texture, + Uint32 *swapchain_texture_width, + Uint32 *swapchain_texture_height); + /** * Submits a command buffer so its commands can be processed on the GPU. * @@ -3579,6 +3738,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_AcquireGPUSwapchainTexture( * \since This function is available since SDL 3.1.3. * * \sa SDL_AcquireGPUCommandBuffer + * \sa SDL_WaitAndAcquireGPUSwapchainTexture * \sa SDL_AcquireGPUSwapchainTexture * \sa SDL_SubmitGPUCommandBufferAndAcquireFence */ @@ -3604,6 +3764,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SubmitGPUCommandBuffer( * \since This function is available since SDL 3.1.3. * * \sa SDL_AcquireGPUCommandBuffer + * \sa SDL_WaitAndAcquireGPUSwapchainTexture * \sa SDL_AcquireGPUSwapchainTexture * \sa SDL_SubmitGPUCommandBuffer * \sa SDL_ReleaseGPUFence @@ -3616,18 +3777,20 @@ extern SDL_DECLSPEC SDL_GPUFence *SDLCALL SDL_SubmitGPUCommandBufferAndAcquireFe * * None of the enqueued commands are executed. * + * It is an error to call this function after a swapchain texture has been + * acquired. + * * This must be called from the thread the command buffer was acquired on. * - * You must not reference the command buffer after calling this function. It - * is an error to call this function after a swapchain texture has been - * acquired. + * You must not reference the command buffer after calling this function. * * \param command_buffer a command buffer. * \returns true on success, false on error; call SDL_GetError() for more * information. * - * \since This function is available since SDL 3.2.0. + * \since This function is available since SDL 3.1.6. * + * \sa SDL_WaitAndAcquireGPUSwapchainTexture * \sa SDL_AcquireGPUCommandBuffer * \sa SDL_AcquireGPUSwapchainTexture */ @@ -3756,7 +3919,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GPUTextureSupportsSampleCount( * \param depth_or_layer_count depth for 3D textures or layer count otherwise. * \returns the size of a texture with this format and dimensions. * - * \since This function is available since SDL 3.2.0. + * \since This function is available since SDL 3.1.6. */ extern SDL_DECLSPEC Uint32 SDLCALL SDL_CalculateGPUTextureFormatSize( SDL_GPUTextureFormat format, diff --git a/libs/SDL3/include/SDL3/SDL_guid.h b/libs/SDL3/include/SDL3/SDL_guid.h index 7806d835..2f464c67 100644 --- a/libs/SDL3/include/SDL3/SDL_guid.h +++ b/libs/SDL3/include/SDL3/SDL_guid.h @@ -26,6 +26,8 @@ * * A GUID is a 128-bit value that represents something that is uniquely * identifiable by this value: "globally unique." + * + * SDL provides functions to convert a GUID to/from a string. */ #ifndef SDL_guid_h_ diff --git a/libs/SDL3/include/SDL3/SDL_haptic.h b/libs/SDL3/include/SDL3/SDL_haptic.h index c123cd90..533f3bce 100644 --- a/libs/SDL3/include/SDL3/SDL_haptic.h +++ b/libs/SDL3/include/SDL3/SDL_haptic.h @@ -299,12 +299,24 @@ typedef struct SDL_Haptic SDL_Haptic; #define SDL_HAPTIC_LEFTRIGHT (1u<<11) /** - * Reserved for future use + * Reserved for future use. * * \since This macro is available since SDL 3.1.3. */ #define SDL_HAPTIC_RESERVED1 (1u<<12) + +/** + * Reserved for future use. + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_HAPTIC_RESERVED2 (1u<<13) + +/** + * Reserved for future use. + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_HAPTIC_RESERVED3 (1u<<14) /** diff --git a/libs/SDL3/include/SDL3/SDL_hints.h b/libs/SDL3/include/SDL3/SDL_hints.h index ea8e51fc..1e03207d 100644 --- a/libs/SDL3/include/SDL3/SDL_hints.h +++ b/libs/SDL3/include/SDL3/SDL_hints.h @@ -100,6 +100,23 @@ extern "C" { */ #define SDL_HINT_ANDROID_BLOCK_ON_PAUSE "SDL_ANDROID_BLOCK_ON_PAUSE" +/** + * A variable to control whether low latency audio should be enabled. + * + * Some devices have poor quality output when this is enabled, but this is + * usually an improvement in audio latency. + * + * The variable can be set to the following values: + * + * - "0": Low latency audio is not enabled. + * - "1": Low latency audio is enabled. (default) + * + * This hint should be set before SDL audio is initialized. + * + * \since This hint is available since SDL 3.1.8. + */ +#define SDL_HINT_ANDROID_LOW_LATENCY_AUDIO "SDL_ANDROID_LOW_LATENCY_AUDIO" + /** * A variable to control whether we trap the Android back button to handle it * manually. @@ -199,16 +216,58 @@ extern "C" { * Specify the default ALSA audio device name. * * This variable is a specific audio device to open when the "default" audio - * device is used. By default if 4 channel audio is requested, the - * "plug:surround40" device will be opened and if 6 channel audio is requested - * the "plug:surround51" device will be opened. + * device is used. + * + * This hint will be ignored when opening the default playback device if + * SDL_HINT_AUDIO_ALSA_DEFAULT_PLAYBACK_DEVICE is set, or when opening the + * default recording device if SDL_HINT_AUDIO_ALSA_DEFAULT_RECORDING_DEVICE is + * set. * * This hint should be set before an audio device is opened. * * \since This hint is available since SDL 3.1.3. + * + * \sa SDL_HINT_AUDIO_ALSA_DEFAULT_PLAYBACK_DEVICE + * \sa SDL_HINT_AUDIO_ALSA_DEFAULT_RECORDING_DEVICE */ #define SDL_HINT_AUDIO_ALSA_DEFAULT_DEVICE "SDL_AUDIO_ALSA_DEFAULT_DEVICE" +/** + * Specify the default ALSA audio playback device name. + * + * This variable is a specific audio device to open for playback, when the + * "default" audio device is used. + * + * If this hint isn't set, SDL will check SDL_HINT_AUDIO_ALSA_DEFAULT_DEVICE + * before choosing a reasonable default. + * + * This hint should be set before an audio device is opened. + * + * \since This hint is available since SDL 3.1.7. + * + * \sa SDL_HINT_AUDIO_ALSA_DEFAULT_RECORDING_DEVICE + * \sa SDL_HINT_AUDIO_ALSA_DEFAULT_DEVICE + */ +#define SDL_HINT_AUDIO_ALSA_DEFAULT_PLAYBACK_DEVICE "SDL_AUDIO_ALSA_DEFAULT_PLAYBACK_DEVICE" + +/** + * Specify the default ALSA audio recording device name. + * + * This variable is a specific audio device to open for recording, when the + * "default" audio device is used. + * + * If this hint isn't set, SDL will check SDL_HINT_AUDIO_ALSA_DEFAULT_DEVICE + * before choosing a reasonable default. + * + * This hint should be set before an audio device is opened. + * + * \since This hint is available since SDL 3.1.7. + * + * \sa SDL_HINT_AUDIO_ALSA_DEFAULT_PLAYBACK_DEVICE + * \sa SDL_HINT_AUDIO_ALSA_DEFAULT_DEVICE + */ +#define SDL_HINT_AUDIO_ALSA_DEFAULT_RECORDING_DEVICE "SDL_AUDIO_ALSA_DEFAULT_RECORDING_DEVICE" + /** * A variable controlling the audio category on iOS and macOS. * @@ -1017,7 +1076,7 @@ extern "C" { * force a specific target, such as "direct3d11" if, say, your hardware * supports D3D12 but want to try using D3D11 instead. * - * This hint should be set before SDL_GPUSelectBackend() is called. + * This hint should be set before any GPU functions are called. * * \since This hint is available since SDL 3.1.3. */ @@ -1310,7 +1369,7 @@ extern "C" { * This variable is the default for all drivers, but can be overridden by the * hints for specific drivers below. * - * This hint should be set before enumerating controllers. + * This hint should be set before initializing joysticks and gamepads. * * \since This hint is available since SDL 3.1.3. */ @@ -1327,7 +1386,7 @@ extern "C" { * - "1": Left and right Joy-Con controllers will be combined into a single * controller. (default) * - * This hint should be set before enumerating controllers. + * This hint should be set before initializing joysticks and gamepads. * * \since This hint is available since SDL 3.1.3. */ @@ -1344,7 +1403,7 @@ extern "C" { * * The default is the value of SDL_HINT_JOYSTICK_HIDAPI * - * This hint should be set before enumerating controllers. + * This hint should be set before initializing joysticks and gamepads. * * \since This hint is available since SDL 3.1.3. */ @@ -1382,7 +1441,7 @@ extern "C" { * * The default is the value of SDL_HINT_JOYSTICK_HIDAPI. * - * This hint should be set before enumerating controllers. + * This hint should be set before initializing joysticks and gamepads. * * \since This hint is available since SDL 3.1.3. */ @@ -1418,7 +1477,7 @@ extern "C" { * * The default is the value of SDL_HINT_JOYSTICK_HIDAPI. * - * This hint should be set before enumerating controllers. + * This hint should be set before initializing joysticks and gamepads. * * \since This hint is available since SDL 3.1.3. */ @@ -1435,7 +1494,7 @@ extern "C" { * * The default is the value of SDL_HINT_JOYSTICK_HIDAPI. * - * This hint should be set before enumerating controllers. + * This hint should be set before initializing joysticks and gamepads. * * \since This hint is available since SDL 3.1.3. */ @@ -1457,7 +1516,7 @@ extern "C" { * SDL_HINT_JOYSTICK_HIDAPI_PS3_SIXAXIS_DRIVER. See * https://github.com/ViGEm/DsHidMini for an alternative driver on Windows. * - * This hint should be set before enumerating controllers. + * This hint should be set before initializing joysticks and gamepads. * * \since This hint is available since SDL 3.1.3. */ @@ -1474,7 +1533,7 @@ extern "C" { * * The default value is 0. * - * This hint should be set before enumerating controllers. + * This hint should be set before initializing joysticks and gamepads. * * \since This hint is available since SDL 3.1.3. */ @@ -1491,7 +1550,7 @@ extern "C" { * * The default is the value of SDL_HINT_JOYSTICK_HIDAPI. * - * This hint should be set before enumerating controllers. + * This hint should be set before initializing joysticks and gamepads. * * \since This hint is available since SDL 3.1.3. */ @@ -1549,7 +1608,7 @@ extern "C" { * * The default is the value of SDL_HINT_JOYSTICK_HIDAPI. * - * This hint should be set before enumerating controllers. + * This hint should be set before initializing joysticks and gamepads. * * \since This hint is available since SDL 3.1.3. */ @@ -1604,7 +1663,7 @@ extern "C" { * * The default is the value of SDL_HINT_JOYSTICK_HIDAPI. * - * This hint should be set before enumerating controllers. + * This hint should be set before initializing joysticks and gamepads. * * \since This hint is available since SDL 3.1.3. */ @@ -1636,12 +1695,31 @@ extern "C" { * Bluetooth access and may prompt the user for permission on iOS and * Android. * - * This hint should be set before enumerating controllers. + * This hint should be set before initializing joysticks and gamepads. * * \since This hint is available since SDL 3.1.3. */ #define SDL_HINT_JOYSTICK_HIDAPI_STEAM "SDL_JOYSTICK_HIDAPI_STEAM" +/** + * A variable controlling whether the Steam button LED should be turned on + * when a Steam controller is opened. + * + * The variable can be set to the following values: + * + * - "0": Steam button LED is turned off. + * - "1": Steam button LED is turned on. + * + * By default the Steam button LED state is not changed. This hint can also be + * set to a floating point value between 0.0 and 1.0 which controls the + * brightness of the Steam button LED. + * + * This hint can be set anytime. + * + * \since This hint is available since SDL 3.1.8. + */ +#define SDL_HINT_JOYSTICK_HIDAPI_STEAM_HOME_LED "SDL_JOYSTICK_HIDAPI_STEAM_HOME_LED" + /** * A variable controlling whether the HIDAPI driver for the Steam Deck builtin * controller should be used. @@ -1653,7 +1731,7 @@ extern "C" { * * The default is the value of SDL_HINT_JOYSTICK_HIDAPI. * - * This hint should be set before enumerating controllers. + * This hint should be set before initializing joysticks and gamepads. * * \since This hint is available since SDL 3.1.3. */ @@ -1681,7 +1759,7 @@ extern "C" { * * The default is the value of SDL_HINT_JOYSTICK_HIDAPI. * - * This hint should be set before enumerating controllers. + * This hint should be set before initializing joysticks and gamepads. * * \since This hint is available since SDL 3.1.3. */ @@ -1749,7 +1827,7 @@ extern "C" { * This driver doesn't work with the dolphinbar, so the default is false for * now. * - * This hint should be set before enumerating controllers. + * This hint should be set before initializing joysticks and gamepads. * * \since This hint is available since SDL 3.1.3. */ @@ -1782,7 +1860,7 @@ extern "C" { * The default is "0" on Windows, otherwise the value of * SDL_HINT_JOYSTICK_HIDAPI * - * This hint should be set before enumerating controllers. + * This hint should be set before initializing joysticks and gamepads. * * \since This hint is available since SDL 3.1.3. */ @@ -1799,7 +1877,7 @@ extern "C" { * * The default is the value of SDL_HINT_JOYSTICK_HIDAPI_XBOX * - * This hint should be set before enumerating controllers. + * This hint should be set before initializing joysticks and gamepads. * * \since This hint is available since SDL 3.1.3. */ @@ -1831,7 +1909,7 @@ extern "C" { * * The default is the value of SDL_HINT_JOYSTICK_HIDAPI_XBOX_360 * - * This hint should be set before enumerating controllers. + * This hint should be set before initializing joysticks and gamepads. * * \since This hint is available since SDL 3.1.3. */ @@ -1848,7 +1926,7 @@ extern "C" { * * The default is the value of SDL_HINT_JOYSTICK_HIDAPI_XBOX. * - * This hint should be set before enumerating controllers. + * This hint should be set before initializing joysticks and gamepads. * * \since This hint is available since SDL 3.1.3. */ @@ -2365,6 +2443,18 @@ extern "C" { */ #define SDL_HINT_MOUSE_DOUBLE_CLICK_TIME "SDL_MOUSE_DOUBLE_CLICK_TIME" +/** + * A variable setting which system cursor to use as the default cursor. + * + * This should be an integer corresponding to the SDL_SystemCursor enum. The + * default value is zero (SDL_SYSTEM_CURSOR_DEFAULT). + * + * This hint needs to be set before SDL_Init(). + * + * \since This hint is available since SDL 3.1.3. + */ +#define SDL_HINT_MOUSE_DEFAULT_SYSTEM_CURSOR "SDL_MOUSE_DEFAULT_SYSTEM_CURSOR" + /** * A variable controlling whether warping a hidden mouse cursor will activate * relative mouse mode. @@ -2442,21 +2532,6 @@ extern "C" { */ #define SDL_HINT_MOUSE_RELATIVE_MODE_CENTER "SDL_MOUSE_RELATIVE_MODE_CENTER" -/** - * A variable controlling whether relative mouse mode is implemented using - * mouse warping. - * - * The variable can be set to the following values: - * - * - "0": Relative mouse mode uses raw input. (default) - * - "1": Relative mouse mode uses mouse warping. - * - * This hint can be set anytime relative mode is not currently enabled. - * - * \since This hint is available since SDL 3.1.3. - */ -#define SDL_HINT_MOUSE_RELATIVE_MODE_WARP "SDL_MOUSE_RELATIVE_MODE_WARP" - /** * A variable setting the scale for mouse motion, in floating point, when the * mouse is in relative mode. @@ -2524,23 +2599,6 @@ extern "C" { */ #define SDL_HINT_MOUSE_RELATIVE_CURSOR_VISIBLE "SDL_MOUSE_RELATIVE_CURSOR_VISIBLE" -/** - * Controls how often SDL issues cursor confinement commands to the operating - * system while relative mode is active, in case the desired confinement state - * became out-of-sync due to interference from other running programs. - * - * The variable can be integers representing milliseconds between each - * refresh. A value of zero means SDL will not automatically refresh the - * confinement. The default value varies depending on the operating system, - * this variable might not have any effects on inapplicable platforms such as - * those without a cursor. - * - * This hint can be set anytime. - * - * \since This hint is available since SDL 3.1.3. - */ -#define SDL_HINT_MOUSE_RELATIVE_CLIP_INTERVAL "SDL_MOUSE_RELATIVE_CLIP_INTERVAL" - /** * A variable controlling whether mouse events should generate synthetic touch * events. @@ -2814,6 +2872,7 @@ extern "C" { * - "opengles" * - "metal" * - "vulkan" + * - "gpu" * - "software" * * The default varies by platform, but it's the first one in the list that is @@ -3962,6 +4021,15 @@ extern "C" { * \since This hint is available since SDL 3.1.3. */ #define SDL_HINT_WINDOWS_INTRESOURCE_ICON "SDL_WINDOWS_INTRESOURCE_ICON" + +/** + * A variable to specify custom icon resource id from RC file on Windows + * platform. + * + * This hint should be set before SDL is initialized. + * + * \since This hint is available since SDL 3.1.3. + */ #define SDL_HINT_WINDOWS_INTRESOURCE_ICON_SMALL "SDL_WINDOWS_INTRESOURCE_ICON_SMALL" /** diff --git a/libs/SDL3/include/SDL3/SDL_init.h b/libs/SDL3/include/SDL3/SDL_init.h index 3f7f45d1..10b04b4e 100644 --- a/libs/SDL3/include/SDL3/SDL_init.h +++ b/libs/SDL3/include/SDL3/SDL_init.h @@ -78,7 +78,7 @@ extern "C" { typedef Uint32 SDL_InitFlags; #define SDL_INIT_AUDIO 0x00000010u /**< `SDL_INIT_AUDIO` implies `SDL_INIT_EVENTS` */ -#define SDL_INIT_VIDEO 0x00000020u /**< `SDL_INIT_VIDEO` implies `SDL_INIT_EVENTS` */ +#define SDL_INIT_VIDEO 0x00000020u /**< `SDL_INIT_VIDEO` implies `SDL_INIT_EVENTS`, should be initialized on the main thread */ #define SDL_INIT_JOYSTICK 0x00000200u /**< `SDL_INIT_JOYSTICK` implies `SDL_INIT_EVENTS`, should be initialized on the same thread as SDL_INIT_VIDEO on Windows if you don't set SDL_HINT_JOYSTICK_THREAD */ #define SDL_INIT_HAPTIC 0x00001000u #define SDL_INIT_GAMEPAD 0x00002000u /**< `SDL_INIT_GAMEPAD` implies `SDL_INIT_JOYSTICK` */ @@ -113,11 +113,71 @@ typedef enum SDL_AppResult SDL_APP_FAILURE /**< Value that requests termination with error from the main callbacks. */ } SDL_AppResult; +/** + * Function pointer typedef for SDL_AppInit. + * + * These are used by SDL_EnterAppMainCallbacks. This mechanism operates behind + * the scenes for apps using the optional main callbacks. Apps that want to + * use this should just implement SDL_AppInit directly. + * + * \param appstate a place where the app can optionally store a pointer for + * future use. + * \param argc the standard ANSI C main's argc; number of elements in `argv`. + * \param argv the standard ANSI C main's argv; array of command line + * arguments. + * \returns SDL_APP_FAILURE to terminate with an error, SDL_APP_SUCCESS to + * terminate with success, SDL_APP_CONTINUE to continue. + * + * \since This datatype is available since SDL 3.1.3. + */ typedef SDL_AppResult (SDLCALL *SDL_AppInit_func)(void **appstate, int argc, char *argv[]); + +/** + * Function pointer typedef for SDL_AppIterate. + * + * These are used by SDL_EnterAppMainCallbacks. This mechanism operates behind + * the scenes for apps using the optional main callbacks. Apps that want to + * use this should just implement SDL_AppIterate directly. + * + * \param appstate an optional pointer, provided by the app in SDL_AppInit. + * \returns SDL_APP_FAILURE to terminate with an error, SDL_APP_SUCCESS to + * terminate with success, SDL_APP_CONTINUE to continue. + * + * \since This datatype is available since SDL 3.1.3. + */ typedef SDL_AppResult (SDLCALL *SDL_AppIterate_func)(void *appstate); + +/** + * Function pointer typedef for SDL_AppEvent. + * + * These are used by SDL_EnterAppMainCallbacks. This mechanism operates behind + * the scenes for apps using the optional main callbacks. Apps that want to + * use this should just implement SDL_AppEvent directly. + * + * \param appstate an optional pointer, provided by the app in SDL_AppInit. + * \param event the new event for the app to examine. + * \returns SDL_APP_FAILURE to terminate with an error, SDL_APP_SUCCESS to + * terminate with success, SDL_APP_CONTINUE to continue. + * + * \since This datatype is available since SDL 3.1.3. + */ typedef SDL_AppResult (SDLCALL *SDL_AppEvent_func)(void *appstate, SDL_Event *event); + +/** + * Function pointer typedef for SDL_AppQuit. + * + * These are used by SDL_EnterAppMainCallbacks. This mechanism operates behind + * the scenes for apps using the optional main callbacks. Apps that want to + * use this should just implement SDL_AppEvent directly. + * + * \param appstate an optional pointer, provided by the app in SDL_AppInit. + * \param result the result code that terminated the app (success or failure). + * + * \since This datatype is available since SDL 3.1.3. + */ typedef void (SDLCALL *SDL_AppQuit_func)(void *appstate, SDL_AppResult result); + /** * Initialize the SDL library. * @@ -139,7 +199,7 @@ typedef void (SDLCALL *SDL_AppQuit_func)(void *appstate, SDL_AppResult result); * - `SDL_INIT_AUDIO`: audio subsystem; automatically initializes the events * subsystem * - `SDL_INIT_VIDEO`: video subsystem; automatically initializes the events - * subsystem + * subsystem, should be initialized on the main thread. * - `SDL_INIT_JOYSTICK`: joystick subsystem; automatically initializes the * events subsystem * - `SDL_INIT_HAPTIC`: haptic (force feedback) subsystem @@ -239,6 +299,63 @@ extern SDL_DECLSPEC SDL_InitFlags SDLCALL SDL_WasInit(SDL_InitFlags flags); */ extern SDL_DECLSPEC void SDLCALL SDL_Quit(void); +/** + * Return whether this is the main thread. + * + * On Apple platforms, the main thread is the thread that runs your program's + * main() entry point. On other platforms, the main thread is the one that + * calls SDL_Init(SDL_INIT_VIDEO), which should usually be the one that runs + * your program's main() entry point. If you are using the main callbacks, + * SDL_AppInit(), SDL_AppIterate(), and SDL_AppQuit() are all called on the + * main thread. + * + * \returns true if this thread is the main thread, or false otherwise. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.2.0. + * + * \sa SDL_RunOnMainThread + */ +extern SDL_DECLSPEC bool SDLCALL SDL_IsMainThread(void); + +/** + * Callback run on the main thread. + * + * \param userdata an app-controlled pointer that is passed to the callback. + * + * \since This datatype is available since SDL 3.1.8. + * + * \sa SDL_RunOnMainThread + */ +typedef void (SDLCALL *SDL_MainThreadCallback)(void *userdata); + +/** + * Call a function on the main thread during event processing. + * + * If this is called on the main thread, the callback is executed immediately. + * If this is called on another thread, this callback is queued for execution + * on the main thread during event processing. + * + * Be careful of deadlocks when using this functionality. You should not have + * the main thread wait for the current thread while this function is being + * called with `wait_complete` true. + * + * \param callback the callback to call on the main thread. + * \param userdata a pointer that is passed to `callback`. + * \param wait_complete true to wait for the callback to complete, false to + * return immediately. + * \returns true on success or false on failure; call SDL_GetError() for more + * information. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.2.0. + * + * \sa SDL_IsMainThread + */ +extern SDL_DECLSPEC bool SDLCALL SDL_RunOnMainThread(SDL_MainThreadCallback callback, void *userdata, bool wait_complete); + /** * Specify basic metadata about your app. * @@ -292,7 +409,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetAppMetadata(const char *appname, const c * Multiple calls to this function are allowed, but various state might not * change once it has been set up with a previous call to this function. * - * Once set, this metadata can be read using SDL_GetMetadataProperty(). + * Once set, this metadata can be read using SDL_GetAppMetadataProperty(). * * These are the supported properties: * diff --git a/libs/SDL3/include/SDL3/SDL_intrin.h b/libs/SDL3/include/SDL3/SDL_intrin.h index efba11d5..457328fa 100644 --- a/libs/SDL3/include/SDL3/SDL_intrin.h +++ b/libs/SDL3/include/SDL3/SDL_intrin.h @@ -19,8 +19,31 @@ 3. This notice may not be removed or altered from any source distribution. */ -/* - * Header file for CPU intrinsics for SDL +/* WIKI CATEGORY: Intrinsics */ + +/** + * # CategoryIntrinsics + * + * SDL does some preprocessor gymnastics to determine if any CPU-specific + * compiler intrinsics are available, as this is not necessarily an easy thing + * to calculate, and sometimes depends on quirks of a system, versions of + * build tools, and other external forces. + * + * Apps including SDL's headers will be able to check consistent preprocessor + * definitions to decide if it's safe to use compiler intrinsics for a + * specific CPU architecture. This check only tells you that the compiler is + * capable of using those intrinsics; at runtime, you should still check if + * they are available on the current system with the + * [CPU info functions](https://wiki.libsdl.org/SDL3/CategoryCPUInfo) + * , such as SDL_HasSSE() or SDL_HasNEON(). Otherwise, the process might crash + * for using an unsupported CPU instruction. + * + * SDL only sets preprocessor defines for CPU intrinsics if they are + * supported, so apps should check with `#ifdef` and not `#if`. + * + * SDL will also include the appropriate instruction-set-specific support + * headers, so if SDL decides to define SDL_SSE2_INTRINSICS, it will also + * `#include ` as well. */ #ifndef SDL_intrin_h_ @@ -28,6 +51,169 @@ #include +#ifdef SDL_WIKI_DOCUMENTATION_SECTION + +/** + * Defined if (and only if) the compiler supports Loongarch LSX intrinsics. + * + * If this macro is defined, SDL will have already included `` + * + * \since This macro is available since 3.1.3. + * + * \sa SDL_LASX_INTRINSICS + */ +#define SDL_LSX_INTRINSICS 1 + +/** + * Defined if (and only if) the compiler supports Loongarch LSX intrinsics. + * + * If this macro is defined, SDL will have already included `` + * + * \since This macro is available since 3.1.3. + * + * \sa SDL_LASX_INTRINSICS + */ +#define SDL_LASX_INTRINSICS 1 + +/** + * Defined if (and only if) the compiler supports ARM NEON intrinsics. + * + * If this macro is defined, SDL will have already included `` + * ``, ``, and ``, as appropriate. + * + * \since This macro is available since 3.1.3. + */ +#define SDL_NEON_INTRINSICS 1 + +/** + * Defined if (and only if) the compiler supports PowerPC Altivec intrinsics. + * + * If this macro is defined, SDL will have already included `` + * + * \since This macro is available since 3.1.3. + */ +#define SDL_ALTIVEC_INTRINSICS 1 + +/** + * Defined if (and only if) the compiler supports Intel MMX intrinsics. + * + * If this macro is defined, SDL will have already included `` + * + * \since This macro is available since 3.1.3. + * + * \sa SDL_SSE_INTRINSICS + */ +#define SDL_MMX_INTRINSICS 1 + +/** + * Defined if (and only if) the compiler supports Intel SSE intrinsics. + * + * If this macro is defined, SDL will have already included `` + * + * \since This macro is available since 3.1.3. + * + * \sa SDL_SSE2_INTRINSICS + * \sa SDL_SSE3_INTRINSICS + * \sa SDL_SSE4_1_INTRINSICS + * \sa SDL_SSE4_2_INTRINSICS + */ +#define SDL_SSE_INTRINSICS 1 + +/** + * Defined if (and only if) the compiler supports Intel SSE2 intrinsics. + * + * If this macro is defined, SDL will have already included `` + * + * \since This macro is available since 3.1.3. + * + * \sa SDL_SSE_INTRINSICS + * \sa SDL_SSE3_INTRINSICS + * \sa SDL_SSE4_1_INTRINSICS + * \sa SDL_SSE4_2_INTRINSICS + */ +#define SDL_SSE2_INTRINSICS 1 + +/** + * Defined if (and only if) the compiler supports Intel SSE3 intrinsics. + * + * If this macro is defined, SDL will have already included `` + * + * \since This macro is available since 3.1.3. + * + * \sa SDL_SSE_INTRINSICS + * \sa SDL_SSE2_INTRINSICS + * \sa SDL_SSE4_1_INTRINSICS + * \sa SDL_SSE4_2_INTRINSICS + */ +#define SDL_SSE3_INTRINSICS 1 + +/** + * Defined if (and only if) the compiler supports Intel SSE4.1 intrinsics. + * + * If this macro is defined, SDL will have already included `` + * + * \since This macro is available since 3.1.3. + * + * \sa SDL_SSE_INTRINSICS + * \sa SDL_SSE2_INTRINSICS + * \sa SDL_SSE3_INTRINSICS + * \sa SDL_SSE4_2_INTRINSICS + */ +#define SDL_SSE4_1_INTRINSICS 1 + +/** + * Defined if (and only if) the compiler supports Intel SSE4.2 intrinsics. + * + * If this macro is defined, SDL will have already included `` + * + * \since This macro is available since 3.1.3. + * + * \sa SDL_SSE_INTRINSICS + * \sa SDL_SSE2_INTRINSICS + * \sa SDL_SSE3_INTRINSICS + * \sa SDL_SSE4_1_INTRINSICS + */ +#define SDL_SSE4_2_INTRINSICS 1 + +/** + * Defined if (and only if) the compiler supports Intel AVX intrinsics. + * + * If this macro is defined, SDL will have already included `` + * + * \since This macro is available since 3.1.3. + * + * \sa SDL_AVX2_INTRINSICS + * \sa SDL_AVX512F_INTRINSICS + */ +#define SDL_AVX_INTRINSICS 1 + +/** + * Defined if (and only if) the compiler supports Intel AVX2 intrinsics. + * + * If this macro is defined, SDL will have already included `` + * + * \since This macro is available since 3.1.3. + * + * \sa SDL_AVX_INTRINSICS + * \sa SDL_AVX512F_INTRINSICS + */ +#define SDL_AVX2_INTRINSICS 1 + +/** + * Defined if (and only if) the compiler supports Intel AVX-512F intrinsics. + * + * AVX-512F is also sometimes referred to as "AVX-512 Foundation." + * + * If this macro is defined, SDL will have already included `` + * + * \since This macro is available since 3.1.3. + * + * \sa SDL_AVX_INTRINSICS + * \sa SDL_AVX2_INTRINSICS + */ +#define SDL_AVX512F_INTRINSICS 1 +#endif + /* Need to do this here because intrin.h has C++ code in it */ /* Visual Studio 2005 has a bug where intrin.h conflicts with winnt.h */ #if defined(_MSC_VER) && (_MSC_VER >= 1500) && (defined(_M_IX86) || defined(_M_X64)) @@ -81,7 +267,21 @@ _m_prefetch(void *__P) #endif #endif /* compiler version */ -#if defined(__clang__) && defined(__has_attribute) +#ifdef SDL_WIKI_DOCUMENTATION_SECTION +/** + * A macro to decide if the compiler supports `__attribute__((target))`. + * + * Even though this is defined in SDL's public headers, it is generally not + * used directly by apps. Apps should probably just use SDL_TARGETING + * directly, instead. + * + * \since This macro is available since SDL 3.1.3. + * + * \sa SDL_TARGETING + */ +#define SDL_HAS_TARGET_ATTRIBS + +#elif defined(__clang__) && defined(__has_attribute) # if __has_attribute(target) # define SDL_HAS_TARGET_ATTRIBS # endif @@ -91,7 +291,55 @@ _m_prefetch(void *__P) # define SDL_HAS_TARGET_ATTRIBS #endif -#ifdef SDL_HAS_TARGET_ATTRIBS + +#ifdef SDL_WIKI_DOCUMENTATION_SECTION + +/** + * A macro to tag a function as targeting a specific CPU architecture. + * + * This is a hint to the compiler that a function should be built with support + * for a CPU instruction set that might be different than the rest of the + * program. + * + * The particulars of this are explained in the GCC documentation: + * + * https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-target-function-attribute + * + * An example of using this feature is to turn on SSE2 support for a specific + * function, even if the rest of the source code is not compiled to use SSE2 + * code: + * + * ```c + * #ifdef SDL_SSE2_INTRINSICS + * static void SDL_TARGETING("sse2") DoSomethingWithSSE2(char *x) { + * ...use SSE2 intrinsic functions, etc... + * } + * #endif + * + * // later... + * #ifdef SDL_SSE2_INTRINSICS + * if (SDL_HasSSE2()) { + * DoSomethingWithSSE2(str); + * } + * #endif + * ``` + * + * The application is, on a whole, built without SSE2 instructions, so it will + * run on Intel machines that don't support SSE2. But then at runtime, it + * checks if the system supports the instructions, and then calls into a + * function that uses SSE2 opcodes. The ifdefs make sure that this code isn't + * used on platforms that don't have SSE2 at all. + * + * On compilers without target support, this is defined to nothing. + * + * This symbol is used by SDL internally, but apps and other libraries are + * welcome to use it for their own interfaces as well. + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_TARGETING(x) __attribute__((target(x))) + +#elif defined(SDL_HAS_TARGET_ATTRIBS) # define SDL_TARGETING(x) __attribute__((target(x))) #else # define SDL_TARGETING(x) diff --git a/libs/SDL3/include/SDL3/SDL_iostream.h b/libs/SDL3/include/SDL3/SDL_iostream.h index 76fdf0ad..c6ff1c28 100644 --- a/libs/SDL3/include/SDL3/SDL_iostream.h +++ b/libs/SDL3/include/SDL3/SDL_iostream.h @@ -541,10 +541,12 @@ extern SDL_DECLSPEC Sint64 SDLCALL SDL_TellIO(SDL_IOStream *context); * Read from a data source. * * This function reads up `size` bytes from the data source to the area - * pointed at by `ptr`. This function may read less bytes than requested. It - * will return zero when the data stream is completely read, and - * SDL_GetIOStatus() will return SDL_IO_STATUS_EOF, or on error, and - * SDL_GetIOStatus() will return SDL_IO_STATUS_ERROR. + * pointed at by `ptr`. This function may read less bytes than requested. + * + * This function will return zero when the data stream is completely read, and + * SDL_GetIOStatus() will return SDL_IO_STATUS_EOF. If zero is returned and + * the stream is not at EOF, SDL_GetIOStatus() will return a different error + * value and SDL_GetError() will offer a human-readable message. * * \param context a pointer to an SDL_IOStream structure. * \param ptr a pointer to a buffer to read data into. @@ -664,6 +666,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_FlushIO(SDL_IOStream *context); * \since This function is available since SDL 3.1.3. * * \sa SDL_LoadFile + * \sa SDL_SaveFile_IO */ extern SDL_DECLSPEC void * SDLCALL SDL_LoadFile_IO(SDL_IOStream *src, size_t *datasize, bool closeio); @@ -684,9 +687,46 @@ extern SDL_DECLSPEC void * SDLCALL SDL_LoadFile_IO(SDL_IOStream *src, size_t *da * \since This function is available since SDL 3.1.3. * * \sa SDL_LoadFile_IO + * \sa SDL_SaveFile */ extern SDL_DECLSPEC void * SDLCALL SDL_LoadFile(const char *file, size_t *datasize); +/** + * Save all the data into an SDL data stream. + * + * \param src the SDL_IOStream to write all data to. + * \param data the data to be written. If datasize is 0, may be NULL or a + * invalid pointer. + * \param datasize the number of bytes to be written. + * \param closeio if true, calls SDL_CloseIO() on `src` before returning, even + * in the case of an error. + * \returns true on success or false on failure; call SDL_GetError() for more + * information. + * + * \since This function is available since SDL 3.2.0. + * + * \sa SDL_SaveFile + * \sa SDL_LoadFile_IO + */ +extern SDL_DECLSPEC bool SDLCALL SDL_SaveFile_IO(SDL_IOStream *src, const void *data, size_t datasize, bool closeio); + +/** + * Save all the data into a file path. + * + * \param file the path to read all available data from. + * \param data the data to be written. If datasize is 0, may be NULL or a + * invalid pointer. + * \param datasize the number of bytes to be written. + * \returns true on success or false on failure; call SDL_GetError() for more + * information. + * + * \since This function is available since SDL 3.2.0. + * + * \sa SDL_SaveFile_IO + * \sa SDL_LoadFile + */ +extern SDL_DECLSPEC bool SDLCALL SDL_SaveFile(const char *file, const void *data, size_t datasize); + /** * \name Read endian functions * @@ -697,10 +737,15 @@ extern SDL_DECLSPEC void * SDLCALL SDL_LoadFile(const char *file, size_t *datasi /** * Use this function to read a byte from an SDL_IOStream. * + * This function will return false when the data stream is completely read, + * and SDL_GetIOStatus() will return SDL_IO_STATUS_EOF. If false is returned + * and the stream is not at EOF, SDL_GetIOStatus() will return a different + * error value and SDL_GetError() will offer a human-readable message. + * * \param src the SDL_IOStream to read from. * \param value a pointer filled in with the data read. - * \returns true on success or false on failure; call SDL_GetError() for more - * information. + * \returns true on success or false on failure or EOF; call SDL_GetError() + * for more information. * * \since This function is available since SDL 3.1.3. */ @@ -709,6 +754,11 @@ extern SDL_DECLSPEC bool SDLCALL SDL_ReadU8(SDL_IOStream *src, Uint8 *value); /** * Use this function to read a signed byte from an SDL_IOStream. * + * This function will return false when the data stream is completely read, + * and SDL_GetIOStatus() will return SDL_IO_STATUS_EOF. If false is returned + * and the stream is not at EOF, SDL_GetIOStatus() will return a different + * error value and SDL_GetError() will offer a human-readable message. + * * \param src the SDL_IOStream to read from. * \param value a pointer filled in with the data read. * \returns true on success or false on failure; call SDL_GetError() for more @@ -725,6 +775,11 @@ extern SDL_DECLSPEC bool SDLCALL SDL_ReadS8(SDL_IOStream *src, Sint8 *value); * SDL byteswaps the data only if necessary, so the data returned will be in * the native byte order. * + * This function will return false when the data stream is completely read, + * and SDL_GetIOStatus() will return SDL_IO_STATUS_EOF. If false is returned + * and the stream is not at EOF, SDL_GetIOStatus() will return a different + * error value and SDL_GetError() will offer a human-readable message. + * * \param src the stream from which to read data. * \param value a pointer filled in with the data read. * \returns true on successful write or false on failure; call SDL_GetError() @@ -741,6 +796,11 @@ extern SDL_DECLSPEC bool SDLCALL SDL_ReadU16LE(SDL_IOStream *src, Uint16 *value) * SDL byteswaps the data only if necessary, so the data returned will be in * the native byte order. * + * This function will return false when the data stream is completely read, + * and SDL_GetIOStatus() will return SDL_IO_STATUS_EOF. If false is returned + * and the stream is not at EOF, SDL_GetIOStatus() will return a different + * error value and SDL_GetError() will offer a human-readable message. + * * \param src the stream from which to read data. * \param value a pointer filled in with the data read. * \returns true on successful write or false on failure; call SDL_GetError() @@ -757,6 +817,11 @@ extern SDL_DECLSPEC bool SDLCALL SDL_ReadS16LE(SDL_IOStream *src, Sint16 *value) * SDL byteswaps the data only if necessary, so the data returned will be in * the native byte order. * + * This function will return false when the data stream is completely read, + * and SDL_GetIOStatus() will return SDL_IO_STATUS_EOF. If false is returned + * and the stream is not at EOF, SDL_GetIOStatus() will return a different + * error value and SDL_GetError() will offer a human-readable message. + * * \param src the stream from which to read data. * \param value a pointer filled in with the data read. * \returns true on successful write or false on failure; call SDL_GetError() @@ -773,6 +838,11 @@ extern SDL_DECLSPEC bool SDLCALL SDL_ReadU16BE(SDL_IOStream *src, Uint16 *value) * SDL byteswaps the data only if necessary, so the data returned will be in * the native byte order. * + * This function will return false when the data stream is completely read, + * and SDL_GetIOStatus() will return SDL_IO_STATUS_EOF. If false is returned + * and the stream is not at EOF, SDL_GetIOStatus() will return a different + * error value and SDL_GetError() will offer a human-readable message. + * * \param src the stream from which to read data. * \param value a pointer filled in with the data read. * \returns true on successful write or false on failure; call SDL_GetError() @@ -789,6 +859,11 @@ extern SDL_DECLSPEC bool SDLCALL SDL_ReadS16BE(SDL_IOStream *src, Sint16 *value) * SDL byteswaps the data only if necessary, so the data returned will be in * the native byte order. * + * This function will return false when the data stream is completely read, + * and SDL_GetIOStatus() will return SDL_IO_STATUS_EOF. If false is returned + * and the stream is not at EOF, SDL_GetIOStatus() will return a different + * error value and SDL_GetError() will offer a human-readable message. + * * \param src the stream from which to read data. * \param value a pointer filled in with the data read. * \returns true on successful write or false on failure; call SDL_GetError() @@ -805,6 +880,11 @@ extern SDL_DECLSPEC bool SDLCALL SDL_ReadU32LE(SDL_IOStream *src, Uint32 *value) * SDL byteswaps the data only if necessary, so the data returned will be in * the native byte order. * + * This function will return false when the data stream is completely read, + * and SDL_GetIOStatus() will return SDL_IO_STATUS_EOF. If false is returned + * and the stream is not at EOF, SDL_GetIOStatus() will return a different + * error value and SDL_GetError() will offer a human-readable message. + * * \param src the stream from which to read data. * \param value a pointer filled in with the data read. * \returns true on successful write or false on failure; call SDL_GetError() @@ -821,6 +901,11 @@ extern SDL_DECLSPEC bool SDLCALL SDL_ReadS32LE(SDL_IOStream *src, Sint32 *value) * SDL byteswaps the data only if necessary, so the data returned will be in * the native byte order. * + * This function will return false when the data stream is completely read, + * and SDL_GetIOStatus() will return SDL_IO_STATUS_EOF. If false is returned + * and the stream is not at EOF, SDL_GetIOStatus() will return a different + * error value and SDL_GetError() will offer a human-readable message. + * * \param src the stream from which to read data. * \param value a pointer filled in with the data read. * \returns true on successful write or false on failure; call SDL_GetError() @@ -837,6 +922,11 @@ extern SDL_DECLSPEC bool SDLCALL SDL_ReadU32BE(SDL_IOStream *src, Uint32 *value) * SDL byteswaps the data only if necessary, so the data returned will be in * the native byte order. * + * This function will return false when the data stream is completely read, + * and SDL_GetIOStatus() will return SDL_IO_STATUS_EOF. If false is returned + * and the stream is not at EOF, SDL_GetIOStatus() will return a different + * error value and SDL_GetError() will offer a human-readable message. + * * \param src the stream from which to read data. * \param value a pointer filled in with the data read. * \returns true on successful write or false on failure; call SDL_GetError() @@ -853,6 +943,11 @@ extern SDL_DECLSPEC bool SDLCALL SDL_ReadS32BE(SDL_IOStream *src, Sint32 *value) * SDL byteswaps the data only if necessary, so the data returned will be in * the native byte order. * + * This function will return false when the data stream is completely read, + * and SDL_GetIOStatus() will return SDL_IO_STATUS_EOF. If false is returned + * and the stream is not at EOF, SDL_GetIOStatus() will return a different + * error value and SDL_GetError() will offer a human-readable message. + * * \param src the stream from which to read data. * \param value a pointer filled in with the data read. * \returns true on successful write or false on failure; call SDL_GetError() @@ -869,6 +964,11 @@ extern SDL_DECLSPEC bool SDLCALL SDL_ReadU64LE(SDL_IOStream *src, Uint64 *value) * SDL byteswaps the data only if necessary, so the data returned will be in * the native byte order. * + * This function will return false when the data stream is completely read, + * and SDL_GetIOStatus() will return SDL_IO_STATUS_EOF. If false is returned + * and the stream is not at EOF, SDL_GetIOStatus() will return a different + * error value and SDL_GetError() will offer a human-readable message. + * * \param src the stream from which to read data. * \param value a pointer filled in with the data read. * \returns true on successful write or false on failure; call SDL_GetError() @@ -885,6 +985,11 @@ extern SDL_DECLSPEC bool SDLCALL SDL_ReadS64LE(SDL_IOStream *src, Sint64 *value) * SDL byteswaps the data only if necessary, so the data returned will be in * the native byte order. * + * This function will return false when the data stream is completely read, + * and SDL_GetIOStatus() will return SDL_IO_STATUS_EOF. If false is returned + * and the stream is not at EOF, SDL_GetIOStatus() will return a different + * error value and SDL_GetError() will offer a human-readable message. + * * \param src the stream from which to read data. * \param value a pointer filled in with the data read. * \returns true on successful write or false on failure; call SDL_GetError() @@ -901,6 +1006,11 @@ extern SDL_DECLSPEC bool SDLCALL SDL_ReadU64BE(SDL_IOStream *src, Uint64 *value) * SDL byteswaps the data only if necessary, so the data returned will be in * the native byte order. * + * This function will return false when the data stream is completely read, + * and SDL_GetIOStatus() will return SDL_IO_STATUS_EOF. If false is returned + * and the stream is not at EOF, SDL_GetIOStatus() will return a different + * error value and SDL_GetError() will offer a human-readable message. + * * \param src the stream from which to read data. * \param value a pointer filled in with the data read. * \returns true on successful write or false on failure; call SDL_GetError() diff --git a/libs/SDL3/include/SDL3/SDL_joystick.h b/libs/SDL3/include/SDL3/SDL_joystick.h index 44770972..58000020 100644 --- a/libs/SDL3/include/SDL3/SDL_joystick.h +++ b/libs/SDL3/include/SDL3/SDL_joystick.h @@ -25,8 +25,8 @@ * SDL joystick support. * * This is the lower-level joystick handling. If you want the simpler option, - * where what buttons does what is well-defined, you should use the gamepad - * API instead. + * where what each button does is well-defined, you should use the gamepad API + * instead. * * The term "instance_id" is the current instantiation of a joystick device in * the system, if the joystick is removed and then re-inserted then it will diff --git a/libs/SDL3/include/SDL3/SDL_keyboard.h b/libs/SDL3/include/SDL3/SDL_keyboard.h index 4432dd1f..f6505d19 100644 --- a/libs/SDL3/include/SDL3/SDL_keyboard.h +++ b/libs/SDL3/include/SDL3/SDL_keyboard.h @@ -23,6 +23,11 @@ * # CategoryKeyboard * * SDL keyboard management. + * + * Please refer to the Best Keyboard Practices document for details on how + * best to accept keyboard input in various types of programs: + * + * https://wiki.libsdl.org/SDL3/BestKeyboardPractices */ #ifndef SDL_keyboard_h_ @@ -61,6 +66,8 @@ typedef Uint32 SDL_KeyboardID; * * \returns true if a keyboard is connected, false otherwise. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetKeyboards @@ -81,6 +88,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_HasKeyboard(void); * call SDL_GetError() for more information. This should be freed * with SDL_free() when it is no longer needed. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetKeyboardNameForID @@ -97,6 +106,8 @@ extern SDL_DECLSPEC SDL_KeyboardID * SDLCALL SDL_GetKeyboards(int *count); * \returns the name of the selected keyboard or NULL on failure; call * SDL_GetError() for more information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetKeyboards @@ -108,6 +119,8 @@ extern SDL_DECLSPEC const char * SDLCALL SDL_GetKeyboardNameForID(SDL_KeyboardID * * \returns the window with keyboard focus. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. */ extern SDL_DECLSPEC SDL_Window * SDLCALL SDL_GetKeyboardFocus(void); @@ -136,6 +149,8 @@ extern SDL_DECLSPEC SDL_Window * SDLCALL SDL_GetKeyboardFocus(void); * \param numkeys if non-NULL, receives the length of the returned array. * \returns a pointer to an array of key states. * + * \threadsafety It is safe to call this function from any thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_PumpEvents @@ -148,6 +163,8 @@ extern SDL_DECLSPEC const bool * SDLCALL SDL_GetKeyboardState(int *numkeys); * * This function will generate key up events for all pressed keys. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetKeyboardState @@ -160,6 +177,8 @@ extern SDL_DECLSPEC void SDLCALL SDL_ResetKeyboard(void); * \returns an OR'd combination of the modifier keys for the keyboard. See * SDL_Keymod for details. * + * \threadsafety It is safe to call this function from any thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetKeyboardState @@ -180,6 +199,8 @@ extern SDL_DECLSPEC SDL_Keymod SDLCALL SDL_GetModState(void); * * \param modstate the desired SDL_Keymod for the keyboard. * + * \threadsafety It is safe to call this function from any thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetModState @@ -201,6 +222,8 @@ extern SDL_DECLSPEC void SDLCALL SDL_SetModState(SDL_Keymod modstate); * \param key_event true if the keycode will be used in key events. * \returns the SDL_Keycode that corresponds to the given SDL_Scancode. * + * \threadsafety This function is not thread safe. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetKeyName @@ -220,6 +243,8 @@ extern SDL_DECLSPEC SDL_Keycode SDLCALL SDL_GetKeyFromScancode(SDL_Scancode scan * scancode generates this key, may be NULL. * \returns the SDL_Scancode that corresponds to the given SDL_Keycode. * + * \threadsafety This function is not thread safe. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetKeyFromScancode @@ -237,6 +262,8 @@ extern SDL_DECLSPEC SDL_Scancode SDLCALL SDL_GetScancodeFromKey(SDL_Keycode key, * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function is not thread safe. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetScancodeName @@ -259,6 +286,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetScancodeName(SDL_Scancode scancode, cons * \returns a pointer to the name for the scancode. If the scancode doesn't * have a name this function returns an empty string (""). * + * \threadsafety This function is not thread safe. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetScancodeFromKey @@ -274,6 +303,8 @@ extern SDL_DECLSPEC const char * SDLCALL SDL_GetScancodeName(SDL_Scancode scanco * \returns the SDL_Scancode, or `SDL_SCANCODE_UNKNOWN` if the name wasn't * recognized; call SDL_GetError() for more information. * + * \threadsafety This function is not thread safe. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetKeyFromName @@ -290,6 +321,8 @@ extern SDL_DECLSPEC SDL_Scancode SDLCALL SDL_GetScancodeFromName(const char *nam * \param key the desired SDL_Keycode to query. * \returns a UTF-8 encoded string of the key name. * + * \threadsafety This function is not thread safe. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetKeyFromName @@ -305,6 +338,8 @@ extern SDL_DECLSPEC const char * SDLCALL SDL_GetKeyName(SDL_Keycode key); * \returns key code, or `SDLK_UNKNOWN` if the name wasn't recognized; call * SDL_GetError() for more information. * + * \threadsafety This function is not thread safe. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetKeyFromScancode @@ -330,6 +365,8 @@ extern SDL_DECLSPEC SDL_Keycode SDLCALL SDL_GetKeyFromName(const char *name); * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_SetTextInputArea @@ -366,10 +403,9 @@ typedef enum SDL_TextInputType /** * Auto capitalization type. * - * These are the valid values for - * SDL_PROP_TEXTINPUT_AUTOCAPITALIZATION_NUMBER. Not every value is valid on - * every platform, but where a value isn't supported, a reasonable fallback - * will be used. + * These are the valid values for SDL_PROP_TEXTINPUT_CAPITALIZATION_NUMBER. + * Not every value is valid on every platform, but where a value isn't + * supported, a reasonable fallback will be used. * * \since This enum is available since SDL 3.1.3. * @@ -424,6 +460,8 @@ typedef enum SDL_Capitalization * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_SetTextInputArea @@ -445,6 +483,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_StartTextInputWithProperties(SDL_Window *wi * \param window the window to check. * \returns true if text input events are enabled else false. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_StartTextInput @@ -461,6 +501,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_TextInputActive(SDL_Window *window); * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_StartTextInput @@ -474,6 +516,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_StopTextInput(SDL_Window *window); * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_StartTextInput @@ -495,6 +539,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_ClearComposition(SDL_Window *window); * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetTextInputArea @@ -515,6 +561,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetTextInputArea(SDL_Window *window, const * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_SetTextInputArea @@ -527,6 +575,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetTextInputArea(SDL_Window *window, SDL_Re * \returns true if the platform has some screen keyboard support or false if * not. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_StartTextInput @@ -540,6 +590,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_HasScreenKeyboardSupport(void); * \param window the window for which screen keyboard should be queried. * \returns true if screen keyboard is shown or false if not. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_HasScreenKeyboardSupport diff --git a/libs/SDL3/include/SDL3/SDL_keycode.h b/libs/SDL3/include/SDL3/SDL_keycode.h index 77968bd0..3d7723d1 100644 --- a/libs/SDL3/include/SDL3/SDL_keycode.h +++ b/libs/SDL3/include/SDL3/SDL_keycode.h @@ -23,6 +23,11 @@ * # CategoryKeycode * * Defines constants which identify keyboard keys and modifiers. + * + * Please refer to the Best Keyboard Practices document for details on what + * this information means and how best to use it. + * + * https://wiki.libsdl.org/SDL3/BestKeyboardPractices */ #ifndef SDL_keycode_h_ diff --git a/libs/SDL3/include/SDL3/SDL_loadso.h b/libs/SDL3/include/SDL3/SDL_loadso.h index 53c49671..027462fe 100644 --- a/libs/SDL3/include/SDL3/SDL_loadso.h +++ b/libs/SDL3/include/SDL3/SDL_loadso.h @@ -46,6 +46,11 @@ * not defined whether or not it goes into the global symbol namespace for * the application. If it does and it conflicts with symbols in your code or * other shared libraries, you will not get the results you expect. :) + * - Once a library is unloaded, all pointers into it obtained through + * SDL_LoadFunction() become invalid, even if the library is later reloaded. + * Don't unload a library if you plan to use these pointers in the future. + * Notably: beware of giving one of these pointers to atexit(), since it may + * call that pointer after the library unloads. */ #ifndef SDL_loadso_h_ diff --git a/libs/SDL3/include/SDL3/SDL_locale.h b/libs/SDL3/include/SDL3/SDL_locale.h index 3042bd85..a5035830 100644 --- a/libs/SDL3/include/SDL3/SDL_locale.h +++ b/libs/SDL3/include/SDL3/SDL_locale.h @@ -23,6 +23,12 @@ * # CategoryLocale * * SDL locale services. + * + * This provides a way to get a list of preferred locales (language plus + * country) for the user. There is exactly one function: + * SDL_GetPreferredLocales(), which handles all the heavy lifting, and offers + * documentation on all the strange ways humans might have configured their + * language settings. */ #ifndef SDL_locale_h diff --git a/libs/SDL3/include/SDL3/SDL_log.h b/libs/SDL3/include/SDL3/SDL_log.h index 81a53c52..b1955cbf 100644 --- a/libs/SDL3/include/SDL3/SDL_log.h +++ b/libs/SDL3/include/SDL3/SDL_log.h @@ -480,7 +480,7 @@ typedef void (SDLCALL *SDL_LogOutputFunction)(void *userdata, int category, SDL_ * * \threadsafety It is safe to call this function from any thread. * - * \since This function is available since SDL 3.2.0. + * \since This function is available since SDL 3.1.6. * * \sa SDL_SetLogOutputFunction * \sa SDL_GetLogOutputFunction diff --git a/libs/SDL3/include/SDL3/SDL_main.h b/libs/SDL3/include/SDL3/SDL_main.h index 9b97abda..b244af1c 100644 --- a/libs/SDL3/include/SDL3/SDL_main.h +++ b/libs/SDL3/include/SDL3/SDL_main.h @@ -28,13 +28,20 @@ * should look like this: * * ```c - * int main(int argc, char *argv[]) - * { - * } + * int main(int argc, char *argv[]) + * { + * } * ``` * * SDL will take care of platform specific details on how it gets called. * + * This is also where an app can be configured to use the main callbacks, via + * the SDL_MAIN_USE_CALLBACKS macro. + * + * This is a "single-header library," which is to say that including this + * header inserts code into your program, and you should only include it once + * in most cases. SDL.h does not include this header automatically. + * * For more information, see: * * https://wiki.libsdl.org/SDL3/README/main-functions @@ -48,6 +55,84 @@ #include #include +#ifdef SDL_WIKI_DOCUMENTATION_SECTION + +/** + * Inform SDL that the app is providing an entry point instead of SDL. + * + * SDL does not define this macro, but will check if it is defined when + * including `SDL_main.h`. If defined, SDL will expect the app to provide the + * proper entry point for the platform, and all the other magic details + * needed, like manually calling SDL_SetMainReady. + * + * Please see [README/main-functions](README/main-functions), (or + * docs/README-main-functions.md in the source tree) for a more detailed + * explanation. + * + * \since This macro is used by the headers since SDL 3.1.3. + */ +#define SDL_MAIN_HANDLED 1 + +/** + * Inform SDL to use the main callbacks instead of main. + * + * SDL does not define this macro, but will check if it is defined when + * including `SDL_main.h`. If defined, SDL will expect the app to provide + * several functions: SDL_AppInit, SDL_AppEvent, SDL_AppIterate, and + * SDL_AppQuit. The app should not provide a `main` function in this case, and + * doing so will likely cause the build to fail. + * + * Please see [README/main-functions](README/main-functions), (or + * docs/README-main-functions.md in the source tree) for a more detailed + * explanation. + * + * \since This macro is used by the headers since SDL 3.1.3. + * + * \sa SDL_AppInit + * \sa SDL_AppEvent + * \sa SDL_AppIterate + * \sa SDL_AppQuit + */ +#define SDL_MAIN_USE_CALLBACKS 1 + +/** + * Defined if the target platform offers a special mainline through SDL. + * + * This won't be defined otherwise. If defined, SDL's headers will redefine + * `main` to `SDL_main`. + * + * This macro is defined by `SDL_main.h`, which is not automatically included + * by `SDL.h`. + * + * Even if available, an app can define SDL_MAIN_HANDLED and provide their + * own, if they know what they're doing. + * + * This macro is used internally by SDL, and apps probably shouldn't rely on it. + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_MAIN_AVAILABLE + +/** + * Defined if the target platform _requires_ a special mainline through SDL. + * + * This won't be defined otherwise. If defined, SDL's headers will redefine + * `main` to `SDL_main`. + * + * This macro is defined by `SDL_main.h`, which is not automatically included + * by `SDL.h`. + * + * Even if required, an app can define SDL_MAIN_HANDLED and provide their + * own, if they know what they're doing. + * + * This macro is used internally by SDL, and apps probably shouldn't rely on it. + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_MAIN_NEEDED + +#endif + #ifndef SDL_MAIN_HANDLED #if defined(SDL_PLATFORM_PRIVATE_MAIN) /* Private platforms may have their own ideas about entry points. */ @@ -134,17 +219,29 @@ */ #define SDL_MAIN_AVAILABLE - #elif defined(SDL_PLATFORM_NGAGE) - /* - TODO: not sure if it should be SDL_MAIN_NEEDED, in SDL2 ngage had a - main implementation, but wasn't mentioned in SDL_main.h - */ - #define SDL_MAIN_AVAILABLE - #endif #endif /* SDL_MAIN_HANDLED */ -#ifdef SDL_MAIN_EXPORTED + +#ifdef SDL_WIKI_DOCUMENTATION_SECTION + +/** + * A macro to tag a main entry point function as exported. + * + * Most platforms don't need this, and the macro will be defined to nothing. + * Some, like Android, keep the entry points in a shared library and need to + * explicitly export the symbols. + * + * External code rarely needs this, and if it needs something, it's almost + * always SDL_DECLSPEC instead. + * + * \since This macro is available since SDL 3.1.3. + * + * \sa SDL_DECLSPEC + */ +#define SDLMAIN_DECLSPEC + +#elif defined(SDL_MAIN_EXPORTED) /* We need to export SDL_main so it can be launched from external code, like SDLActivity.java on Android */ #define SDLMAIN_DECLSPEC SDL_DECLSPEC @@ -153,31 +250,6 @@ #define SDLMAIN_DECLSPEC #endif /* SDL_MAIN_EXPORTED */ -#ifdef SDL_WIKI_DOCUMENTATION_SECTION - -/** - * Inform SDL to use the main callbacks instead of main. - * - * SDL does not define this macro, but will check if it is defined when - * including `SDL_main.h`. If defined, SDL will expect the app to provide - * several functions: SDL_AppInit, SDL_AppEvent, SDL_AppIterate, and - * SDL_AppQuit. The app should not provide a `main` function in this case, and - * doing so will likely cause the build to fail. - * - * Please see [README/main-functions](README/main-functions), (or - * docs/README-main-functions.md in the source tree) for a more detailed - * explanation. - * - * \since This macro is used by the headers since SDL 3.1.3. - * - * \sa SDL_AppInit - * \sa SDL_AppEvent - * \sa SDL_AppIterate - * \sa SDL_AppQuit - */ -#define SDL_MAIN_USE_CALLBACKS 1 -#endif - #if defined(SDL_MAIN_NEEDED) || defined(SDL_MAIN_AVAILABLE) || defined(SDL_MAIN_USE_CALLBACKS) #define main SDL_main #endif @@ -567,17 +639,16 @@ extern SDL_DECLSPEC void SDLCALL SDL_UnregisterApp(void); #endif /* defined(SDL_PLATFORM_WINDOWS) */ -#ifdef SDL_PLATFORM_GDK - /** * Callback from the application to let the suspend continue. * + * This function is only needed for Xbox GDK support; all other platforms will + * do nothing and set an "unsupported" error message. + * * \since This function is available since SDL 3.1.3. */ extern SDL_DECLSPEC void SDLCALL SDL_GDKSuspendComplete(void); -#endif /* SDL_PLATFORM_GDK */ - #ifdef __cplusplus } #endif diff --git a/libs/SDL3/include/SDL3/SDL_main_impl.h b/libs/SDL3/include/SDL3/SDL_main_impl.h index e5560e38..761e6b88 100644 --- a/libs/SDL3/include/SDL3/SDL_main_impl.h +++ b/libs/SDL3/include/SDL3/SDL_main_impl.h @@ -131,18 +131,6 @@ /* end of SDL_PLATFORM_WINDOWS impls */ - #elif defined(SDL_PLATFORM_NGAGE) - /* same typedef as in ngage SDKs e32def.h */ - typedef signed int TInt; - /* TODO: if it turns out that this only works when built as C++, - move SDL_PLATFORM_NGAGE into the C++ section in SDL_main.h */ - TInt E32Main() - { - return SDL_RunApp(0, NULL, SDL_main, NULL); - } - - /* end of SDL_PLATFORM_NGAGE impl */ - #else /* platforms that use a standard main() and just call SDL_RunApp(), like iOS and 3DS */ int main(int argc, char *argv[]) { diff --git a/libs/SDL3/include/SDL3/SDL_messagebox.h b/libs/SDL3/include/SDL3/SDL_messagebox.h index 5161ef71..776da2ac 100644 --- a/libs/SDL3/include/SDL3/SDL_messagebox.h +++ b/libs/SDL3/include/SDL3/SDL_messagebox.h @@ -39,7 +39,7 @@ extern "C" { #endif /** - * SDL_MessageBox flags. + * Message box flags. * * If supported will display warning icon, etc. * diff --git a/libs/SDL3/include/SDL3/SDL_mouse.h b/libs/SDL3/include/SDL3/SDL_mouse.h index 16c07100..e6c1ab60 100644 --- a/libs/SDL3/include/SDL3/SDL_mouse.h +++ b/libs/SDL3/include/SDL3/SDL_mouse.h @@ -139,6 +139,8 @@ typedef Uint32 SDL_MouseButtonFlags; * * \returns true if a mouse is connected, false otherwise. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetMice @@ -159,6 +161,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_HasMouse(void); * call SDL_GetError() for more information. This should be freed * with SDL_free() when it is no longer needed. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetMouseNameForID @@ -175,6 +179,8 @@ extern SDL_DECLSPEC SDL_MouseID * SDLCALL SDL_GetMice(int *count); * \returns the name of the selected mouse, or NULL on failure; call * SDL_GetError() for more information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetMice @@ -186,24 +192,37 @@ extern SDL_DECLSPEC const char * SDLCALL SDL_GetMouseNameForID(SDL_MouseID insta * * \returns the window with mouse focus. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. */ extern SDL_DECLSPEC SDL_Window * SDLCALL SDL_GetMouseFocus(void); /** - * Retrieve the current state of the mouse. + * Query SDL's cache for the synchronous mouse button state and the + * window-relative SDL-cursor position. * - * The current button state is returned as a button bitmask, which can be - * tested using the SDL_BUTTON_MASK(X) macro (where `X` is generally 1 for the - * left, 2 for middle, 3 for the right button), and `x` and `y` are set to the - * mouse cursor position relative to the focus window. You can pass NULL for - * either `x` or `y`. + * This function returns the cached synchronous state as SDL understands it + * from the last pump of the event queue. * - * \param x the x coordinate of the mouse cursor position relative to the - * focus window. - * \param y the y coordinate of the mouse cursor position relative to the - * focus window. - * \returns a 32-bit button bitmask of the current button state. + * To query the platform for immediate asynchronous state, use + * SDL_GetGlobalMouseState. + * + * Passing non-NULL pointers to `x` or `y` will write the destination with + * respective x or y coordinates relative to the focused window. + * + * In Relative Mode, the SDL-cursor's position usually contradicts the + * platform-cursor's position as manually calculated from + * SDL_GetGlobalMouseState() and SDL_GetWindowPosition. + * + * \param x a pointer to receive the SDL-cursor's x-position from the focused + * window's top left corner, can be NULL if unused. + * \param y a pointer to receive the SDL-cursor's y-position from the focused + * window's top left corner, can be NULL if unused. + * \returns a 32-bit bitmask of the button state that can be bitwise-compared + * against the SDL_BUTTON_MASK(X) macro. + * + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -213,51 +232,74 @@ extern SDL_DECLSPEC SDL_Window * SDLCALL SDL_GetMouseFocus(void); extern SDL_DECLSPEC SDL_MouseButtonFlags SDLCALL SDL_GetMouseState(float *x, float *y); /** - * Get the current state of the mouse in relation to the desktop. + * Query the platform for the asynchronous mouse button state and the + * desktop-relative platform-cursor position. * - * This works similarly to SDL_GetMouseState(), but the coordinates will be - * reported relative to the top-left of the desktop. This can be useful if you - * need to track the mouse outside of a specific window and SDL_CaptureMouse() - * doesn't fit your needs. For example, it could be useful if you need to - * track the mouse while dragging a window, where coordinates relative to a - * window might not be in sync at all times. + * This function immediately queries the platform for the most recent + * asynchronous state, more costly than retrieving SDL's cached state in + * SDL_GetMouseState(). * - * Note: SDL_GetMouseState() returns the mouse position as SDL understands it - * from the last pump of the event queue. This function, however, queries the - * OS for the current mouse position, and as such, might be a slightly less - * efficient function. Unless you know what you're doing and have a good - * reason to use this function, you probably want SDL_GetMouseState() instead. + * Passing non-NULL pointers to `x` or `y` will write the destination with + * respective x or y coordinates relative to the desktop. * - * \param x filled in with the current X coord relative to the desktop; can be - * NULL. - * \param y filled in with the current Y coord relative to the desktop; can be - * NULL. - * \returns the current button state as a bitmask which can be tested using - * the SDL_BUTTON_MASK(X) macros. + * In Relative Mode, the platform-cursor's position usually contradicts the + * SDL-cursor's position as manually calculated from SDL_GetMouseState() and + * SDL_GetWindowPosition. + * + * This function can be useful if you need to track the mouse outside of a + * specific window and SDL_CaptureMouse() doesn't fit your needs. For example, + * it could be useful if you need to track the mouse while dragging a window, + * where coordinates relative to a window might not be in sync at all times. + * + * \param x a pointer to receive the platform-cursor's x-position from the + * desktop's top left corner, can be NULL if unused. + * \param y a pointer to receive the platform-cursor's y-position from the + * desktop's top left corner, can be NULL if unused. + * \returns a 32-bit bitmask of the button state that can be bitwise-compared + * against the SDL_BUTTON_MASK(X) macro. + * + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * * \sa SDL_CaptureMouse * \sa SDL_GetMouseState + * \sa SDL_GetGlobalMouseState */ extern SDL_DECLSPEC SDL_MouseButtonFlags SDLCALL SDL_GetGlobalMouseState(float *x, float *y); /** - * Retrieve the relative state of the mouse. + * Query SDL's cache for the synchronous mouse button state and accumulated + * mouse delta since last call. * - * The current button state is returned as a button bitmask, which can be - * tested using the `SDL_BUTTON_MASK(X)` macros (where `X` is generally 1 for - * the left, 2 for middle, 3 for the right button), and `x` and `y` are set to - * the mouse deltas since the last call to SDL_GetRelativeMouseState() or - * since event initialization. You can pass NULL for either `x` or `y`. + * This function returns the cached synchronous state as SDL understands it + * from the last pump of the event queue. * - * \param x a pointer filled with the last recorded x coordinate of the mouse. - * \param y a pointer filled with the last recorded y coordinate of the mouse. - * \returns a 32-bit button bitmask of the relative button state. + * To query the platform for immediate asynchronous state, use + * SDL_GetGlobalMouseState. + * + * Passing non-NULL pointers to `x` or `y` will write the destination with + * respective x or y deltas accumulated since the last call to this function + * (or since event initialization). + * + * This function is useful for reducing overhead by processing relative mouse + * inputs in one go per-frame instead of individually per-event, at the + * expense of losing the order between events within the frame (e.g. quickly + * pressing and releasing a button within the same frame). + * + * \param x a pointer to receive the x mouse delta accumulated since last + * call, can be NULL if unused. + * \param y a pointer to receive the y mouse delta accumulated since last + * call, can be NULL if unused. + * \returns a 32-bit bitmask of the button state that can be bitwise-compared + * against the SDL_BUTTON_MASK(X) macro. + * + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetMouseState + * \sa SDL_GetGlobalMouseState */ extern SDL_DECLSPEC SDL_MouseButtonFlags SDLCALL SDL_GetRelativeMouseState(float *x, float *y); @@ -276,6 +318,8 @@ extern SDL_DECLSPEC SDL_MouseButtonFlags SDLCALL SDL_GetRelativeMouseState(float * \param x the x coordinate within the window. * \param y the y coordinate within the window. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_WarpMouseGlobal @@ -299,6 +343,8 @@ extern SDL_DECLSPEC void SDLCALL SDL_WarpMouseInWindow(SDL_Window * window, * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_WarpMouseInWindow @@ -320,6 +366,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_WarpMouseGlobal(float x, float y); * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetWindowRelativeMouseMode @@ -332,6 +380,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowRelativeMouseMode(SDL_Window *wind * \param window the window to query. * \returns true if relative mode is enabled for a window or false otherwise. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_SetWindowRelativeMouseMode @@ -378,6 +428,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetWindowRelativeMouseMode(SDL_Window *wind * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetGlobalMouseState @@ -419,6 +471,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_CaptureMouse(bool enabled); * \returns a new cursor with the specified parameters on success or NULL on * failure; call SDL_GetError() for more information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_CreateColorCursor @@ -450,6 +504,8 @@ extern SDL_DECLSPEC SDL_Cursor * SDLCALL SDL_CreateCursor(const Uint8 * data, * \returns the new cursor on success or NULL on failure; call SDL_GetError() * for more information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_CreateCursor @@ -468,6 +524,8 @@ extern SDL_DECLSPEC SDL_Cursor * SDLCALL SDL_CreateColorCursor(SDL_Surface *surf * \returns a cursor on success or NULL on failure; call SDL_GetError() for * more information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_DestroyCursor @@ -486,6 +544,8 @@ extern SDL_DECLSPEC SDL_Cursor * SDLCALL SDL_CreateSystemCursor(SDL_SystemCursor * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetCursor @@ -500,6 +560,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetCursor(SDL_Cursor *cursor); * * \returns the active cursor or NULL if there is no mouse. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_SetCursor @@ -515,6 +577,8 @@ extern SDL_DECLSPEC SDL_Cursor * SDLCALL SDL_GetCursor(void); * \returns the default cursor on success or NULL on failuree; call * SDL_GetError() for more information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. */ extern SDL_DECLSPEC SDL_Cursor * SDLCALL SDL_GetDefaultCursor(void); @@ -527,6 +591,8 @@ extern SDL_DECLSPEC SDL_Cursor * SDLCALL SDL_GetDefaultCursor(void); * * \param cursor the cursor to free. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_CreateColorCursor @@ -541,6 +607,8 @@ extern SDL_DECLSPEC void SDLCALL SDL_DestroyCursor(SDL_Cursor *cursor); * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_CursorVisible @@ -554,6 +622,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_ShowCursor(void); * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_CursorVisible @@ -567,6 +637,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_HideCursor(void); * \returns `true` if the cursor is being shown, or `false` if the cursor is * hidden. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_HideCursor diff --git a/libs/SDL3/include/SDL3/SDL_mutex.h b/libs/SDL3/include/SDL3/SDL_mutex.h index b3fddeeb..e21d3df1 100644 --- a/libs/SDL3/include/SDL3/SDL_mutex.h +++ b/libs/SDL3/include/SDL3/SDL_mutex.h @@ -33,78 +33,225 @@ #include #include -/******************************************************************************/ -/* Enable thread safety attributes only with clang. +#ifdef SDL_WIKI_DOCUMENTATION_SECTION + +/** + * Enable thread safety attributes, only with clang. + * * The attributes can be safely erased when compiling with other compilers. * * To enable analysis, set these environment variables before running cmake: - * export CC=clang - * export CFLAGS="-DSDL_THREAD_SAFETY_ANALYSIS -Wthread-safety" + * + * ```bash + * export CC=clang + * export CFLAGS="-DSDL_THREAD_SAFETY_ANALYSIS -Wthread-safety" + * ``` */ -#if defined(SDL_THREAD_SAFETY_ANALYSIS) && \ - defined(__clang__) && (!defined(SWIG)) +#define SDL_THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x)) + +#elif defined(SDL_THREAD_SAFETY_ANALYSIS) && defined(__clang__) && (!defined(SWIG)) #define SDL_THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x)) #else #define SDL_THREAD_ANNOTATION_ATTRIBUTE__(x) /* no-op */ #endif +/** + * Wrapper around Clang thread safety analysis annotations. + * + * Please see https://clang.llvm.org/docs/ThreadSafetyAnalysis.html#mutex-h + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_CAPABILITY(x) \ SDL_THREAD_ANNOTATION_ATTRIBUTE__(capability(x)) +/** + * Wrapper around Clang thread safety analysis annotations. + * + * Please see https://clang.llvm.org/docs/ThreadSafetyAnalysis.html#mutex-h + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_SCOPED_CAPABILITY \ SDL_THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable) +/** + * Wrapper around Clang thread safety analysis annotations. + * + * Please see https://clang.llvm.org/docs/ThreadSafetyAnalysis.html#mutex-h + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_GUARDED_BY(x) \ SDL_THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x)) +/** + * Wrapper around Clang thread safety analysis annotations. + * + * Please see https://clang.llvm.org/docs/ThreadSafetyAnalysis.html#mutex-h + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_PT_GUARDED_BY(x) \ SDL_THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x)) +/** + * Wrapper around Clang thread safety analysis annotations. + * + * Please see https://clang.llvm.org/docs/ThreadSafetyAnalysis.html#mutex-h + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_ACQUIRED_BEFORE(x) \ SDL_THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(x)) +/** + * Wrapper around Clang thread safety analysis annotations. + * + * Please see https://clang.llvm.org/docs/ThreadSafetyAnalysis.html#mutex-h + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_ACQUIRED_AFTER(x) \ SDL_THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(x)) +/** + * Wrapper around Clang thread safety analysis annotations. + * + * Please see https://clang.llvm.org/docs/ThreadSafetyAnalysis.html#mutex-h + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_REQUIRES(x) \ SDL_THREAD_ANNOTATION_ATTRIBUTE__(requires_capability(x)) +/** + * Wrapper around Clang thread safety analysis annotations. + * + * Please see https://clang.llvm.org/docs/ThreadSafetyAnalysis.html#mutex-h + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_REQUIRES_SHARED(x) \ SDL_THREAD_ANNOTATION_ATTRIBUTE__(requires_shared_capability(x)) +/** + * Wrapper around Clang thread safety analysis annotations. + * + * Please see https://clang.llvm.org/docs/ThreadSafetyAnalysis.html#mutex-h + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_ACQUIRE(x) \ SDL_THREAD_ANNOTATION_ATTRIBUTE__(acquire_capability(x)) +/** + * Wrapper around Clang thread safety analysis annotations. + * + * Please see https://clang.llvm.org/docs/ThreadSafetyAnalysis.html#mutex-h + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_ACQUIRE_SHARED(x) \ SDL_THREAD_ANNOTATION_ATTRIBUTE__(acquire_shared_capability(x)) +/** + * Wrapper around Clang thread safety analysis annotations. + * + * Please see https://clang.llvm.org/docs/ThreadSafetyAnalysis.html#mutex-h + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_RELEASE(x) \ SDL_THREAD_ANNOTATION_ATTRIBUTE__(release_capability(x)) +/** + * Wrapper around Clang thread safety analysis annotations. + * + * Please see https://clang.llvm.org/docs/ThreadSafetyAnalysis.html#mutex-h + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_RELEASE_SHARED(x) \ SDL_THREAD_ANNOTATION_ATTRIBUTE__(release_shared_capability(x)) +/** + * Wrapper around Clang thread safety analysis annotations. + * + * Please see https://clang.llvm.org/docs/ThreadSafetyAnalysis.html#mutex-h + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_RELEASE_GENERIC(x) \ SDL_THREAD_ANNOTATION_ATTRIBUTE__(release_generic_capability(x)) +/** + * Wrapper around Clang thread safety analysis annotations. + * + * Please see https://clang.llvm.org/docs/ThreadSafetyAnalysis.html#mutex-h + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_TRY_ACQUIRE(x, y) \ SDL_THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_capability(x, y)) +/** + * Wrapper around Clang thread safety analysis annotations. + * + * Please see https://clang.llvm.org/docs/ThreadSafetyAnalysis.html#mutex-h + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_TRY_ACQUIRE_SHARED(x, y) \ SDL_THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_shared_capability(x, y)) +/** + * Wrapper around Clang thread safety analysis annotations. + * + * Please see https://clang.llvm.org/docs/ThreadSafetyAnalysis.html#mutex-h + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_EXCLUDES(x) \ SDL_THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(x)) +/** + * Wrapper around Clang thread safety analysis annotations. + * + * Please see https://clang.llvm.org/docs/ThreadSafetyAnalysis.html#mutex-h + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_ASSERT_CAPABILITY(x) \ SDL_THREAD_ANNOTATION_ATTRIBUTE__(assert_capability(x)) +/** + * Wrapper around Clang thread safety analysis annotations. + * + * Please see https://clang.llvm.org/docs/ThreadSafetyAnalysis.html#mutex-h + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_ASSERT_SHARED_CAPABILITY(x) \ SDL_THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_capability(x)) +/** + * Wrapper around Clang thread safety analysis annotations. + * + * Please see https://clang.llvm.org/docs/ThreadSafetyAnalysis.html#mutex-h + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_RETURN_CAPABILITY(x) \ SDL_THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x)) +/** + * Wrapper around Clang thread safety analysis annotations. + * + * Please see https://clang.llvm.org/docs/ThreadSafetyAnalysis.html#mutex-h + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_NO_THREAD_SAFETY_ANALYSIS \ SDL_THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis) diff --git a/libs/SDL3/include/SDL3/SDL_pixels.h b/libs/SDL3/include/SDL3/SDL_pixels.h index 42957797..1f4f9756 100644 --- a/libs/SDL3/include/SDL3/SDL_pixels.h +++ b/libs/SDL3/include/SDL3/SDL_pixels.h @@ -22,7 +22,59 @@ /** * # CategoryPixels * - * Pixel management. + * SDL offers facilities for pixel management. + * + * Largely these facilities deal with pixel _format_: what does this set of + * bits represent? + * + * If you mostly want to think of a pixel as some combination of red, green, + * blue, and maybe alpha intensities, this is all pretty straightforward, and + * in many cases, is enough information to build a perfectly fine game. + * + * However, the actual definition of a pixel is more complex than that: + * + * Pixels are a representation of a color in a particular color space. + * + * The first characteristic of a color space is the color type. SDL + * understands two different color types, RGB and YCbCr, or in SDL also + * referred to as YUV. + * + * RGB colors consist of red, green, and blue channels of color that are added + * together to represent the colors we see on the screen. + * + * https://en.wikipedia.org/wiki/RGB_color_model + * + * YCbCr colors represent colors as a Y luma brightness component and red and + * blue chroma color offsets. This color representation takes advantage of the + * fact that the human eye is more sensitive to brightness than the color in + * an image. The Cb and Cr components are often compressed and have lower + * resolution than the luma component. + * + * https://en.wikipedia.org/wiki/YCbCr + * + * When the color information in YCbCr is compressed, the Y pixels are left at + * full resolution and each Cr and Cb pixel represents an average of the color + * information in a block of Y pixels. The chroma location determines where in + * that block of pixels the color information is coming from. + * + * The color range defines how much of the pixel to use when converting a + * pixel into a color on the display. When the full color range is used, the + * entire numeric range of the pixel bits is significant. When narrow color + * range is used, for historical reasons, the pixel uses only a portion of the + * numeric range to represent colors. + * + * The color primaries and white point are a definition of the colors in the + * color space relative to the standard XYZ color space. + * + * https://en.wikipedia.org/wiki/CIE_1931_color_space + * + * The transfer characteristic, or opto-electrical transfer function (OETF), + * is the way a color is converted from mathematically linear space into a + * non-linear output signals. + * + * https://en.wikipedia.org/wiki/Rec._709#Transfer_characteristics + * + * The matrix coefficients are used to convert between YCbCr and RGB colors. */ #ifndef SDL_pixels_h_ @@ -161,25 +213,172 @@ typedef enum SDL_PackedLayout SDL_PACKEDLAYOUT_1010102 } SDL_PackedLayout; +/** + * A macro for defining custom FourCC pixel formats. + * + * For example, defining SDL_PIXELFORMAT_YV12 looks like this: + * + * ```c + * SDL_DEFINE_PIXELFOURCC('Y', 'V', '1', '2') + * ``` + * + * \param A the first character of the FourCC code. + * \param B the second character of the FourCC code. + * \param C the third character of the FourCC code. + * \param D the fourth character of the FourCC code. + * \returns a format value in the style of SDL_PixelFormat. + * + * \threadsafety It is safe to call this macro from any thread. + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_DEFINE_PIXELFOURCC(A, B, C, D) SDL_FOURCC(A, B, C, D) +/** + * A macro for defining custom non-FourCC pixel formats. + * + * For example, defining SDL_PIXELFORMAT_RGBA8888 looks like this: + * + * ```c + * SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_RGBA, SDL_PACKEDLAYOUT_8888, 32, 4) + * ``` + * + * \param type the type of the new format, probably a SDL_PixelType value. + * \param order the order of the new format, probably a SDL_BitmapOrder, + * SDL_PackedOrder, or SDL_ArrayOrder value. + * \param layout the layout of the new format, probably an SDL_PackedLayout + * value or zero. + * \param bits the number of bits per pixel of the new format. + * \param bytes the number of bytes per pixel of the new format. + * \returns a format value in the style of SDL_PixelFormat. + * + * \threadsafety It is safe to call this macro from any thread. + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_DEFINE_PIXELFORMAT(type, order, layout, bits, bytes) \ ((1 << 28) | ((type) << 24) | ((order) << 20) | ((layout) << 16) | \ ((bits) << 8) | ((bytes) << 0)) -#define SDL_PIXELFLAG(X) (((X) >> 28) & 0x0F) -#define SDL_PIXELTYPE(X) (((X) >> 24) & 0x0F) -#define SDL_PIXELORDER(X) (((X) >> 20) & 0x0F) -#define SDL_PIXELLAYOUT(X) (((X) >> 16) & 0x0F) -#define SDL_BITSPERPIXEL(X) \ - (SDL_ISPIXELFORMAT_FOURCC(X) ? 0 : (((X) >> 8) & 0xFF)) -#define SDL_BYTESPERPIXEL(X) \ - (SDL_ISPIXELFORMAT_FOURCC(X) ? \ - ((((X) == SDL_PIXELFORMAT_YUY2) || \ - ((X) == SDL_PIXELFORMAT_UYVY) || \ - ((X) == SDL_PIXELFORMAT_YVYU) || \ - ((X) == SDL_PIXELFORMAT_P010)) ? 2 : 1) : (((X) >> 0) & 0xFF)) +/** + * A macro to retrieve the flags of an SDL_PixelFormat. + * + * This macro is generally not needed directly by an app, which should use + * specific tests, like SDL_ISPIXELFORMAT_FOURCC, instead. + * + * \param format an SDL_PixelFormat to check. + * \returns the flags of `format`. + * + * \threadsafety It is safe to call this macro from any thread. + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_PIXELFLAG(format) (((format) >> 28) & 0x0F) +/** + * A macro to retrieve the type of an SDL_PixelFormat. + * + * This is usually a value from the SDL_PixelType enumeration. + * + * \param format an SDL_PixelFormat to check. + * \returns the type of `format`. + * + * \threadsafety It is safe to call this macro from any thread. + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_PIXELTYPE(format) (((format) >> 24) & 0x0F) + +/** + * A macro to retrieve the order of an SDL_PixelFormat. + * + * This is usually a value from the SDL_BitmapOrder, SDL_PackedOrder, or + * SDL_ArrayOrder enumerations, depending on the format type. + * + * \param format an SDL_PixelFormat to check. + * \returns the order of `format`. + * + * \threadsafety It is safe to call this macro from any thread. + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_PIXELORDER(format) (((format) >> 20) & 0x0F) + +/** + * A macro to retrieve the layout of an SDL_PixelFormat. + * + * This is usually a value from the SDL_PackedLayout enumeration, or zero if a + * layout doesn't make sense for the format type. + * + * \param format an SDL_PixelFormat to check. + * \returns the layout of `format`. + * + * \threadsafety It is safe to call this macro from any thread. + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_PIXELLAYOUT(format) (((format) >> 16) & 0x0F) + +/** + * A macro to determine an SDL_PixelFormat's bits per pixel. + * + * Note that this macro double-evaluates its parameter, so do not use + * expressions with side-effects here. + * + * FourCC formats will report zero here, as it rarely makes sense to measure + * them per-pixel. + * + * \param format an SDL_PixelFormat to check. + * \returns the bits-per-pixel of `format`. + * + * \threadsafety It is safe to call this macro from any thread. + * + * \since This macro is available since SDL 3.1.3. + * + * \sa SDL_BYTESPERPIXEL + */ +#define SDL_BITSPERPIXEL(format) \ + (SDL_ISPIXELFORMAT_FOURCC(format) ? 0 : (((format) >> 8) & 0xFF)) + +/** + * A macro to determine an SDL_PixelFormat's bytes per pixel. + * + * Note that this macro double-evaluates its parameter, so do not use + * expressions with side-effects here. + * + * FourCC formats do their best here, but many of them don't have a meaningful + * measurement of bytes per pixel. + * + * \param format an SDL_PixelFormat to check. + * \returns the bytes-per-pixel of `format`. + * + * \threadsafety It is safe to call this macro from any thread. + * + * \since This macro is available since SDL 3.1.3. + * + * \sa SDL_BITSPERPIXEL + */ +#define SDL_BYTESPERPIXEL(format) \ + (SDL_ISPIXELFORMAT_FOURCC(format) ? \ + ((((format) == SDL_PIXELFORMAT_YUY2) || \ + ((format) == SDL_PIXELFORMAT_UYVY) || \ + ((format) == SDL_PIXELFORMAT_YVYU) || \ + ((format) == SDL_PIXELFORMAT_P010)) ? 2 : 1) : (((format) >> 0) & 0xFF)) + + +/** + * A macro to determine if an SDL_PixelFormat is an indexed format. + * + * Note that this macro double-evaluates its parameter, so do not use + * expressions with side-effects here. + * + * \param format an SDL_PixelFormat to check. + * \returns true if the format is indexed, false otherwise. + * + * \threadsafety It is safe to call this macro from any thread. + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_ISPIXELFORMAT_INDEXED(format) \ (!SDL_ISPIXELFORMAT_FOURCC(format) && \ ((SDL_PIXELTYPE(format) == SDL_PIXELTYPE_INDEX1) || \ @@ -187,12 +386,38 @@ typedef enum SDL_PackedLayout (SDL_PIXELTYPE(format) == SDL_PIXELTYPE_INDEX4) || \ (SDL_PIXELTYPE(format) == SDL_PIXELTYPE_INDEX8))) +/** + * A macro to determine if an SDL_PixelFormat is a packed format. + * + * Note that this macro double-evaluates its parameter, so do not use + * expressions with side-effects here. + * + * \param format an SDL_PixelFormat to check. + * \returns true if the format is packed, false otherwise. + * + * \threadsafety It is safe to call this macro from any thread. + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_ISPIXELFORMAT_PACKED(format) \ (!SDL_ISPIXELFORMAT_FOURCC(format) && \ ((SDL_PIXELTYPE(format) == SDL_PIXELTYPE_PACKED8) || \ (SDL_PIXELTYPE(format) == SDL_PIXELTYPE_PACKED16) || \ (SDL_PIXELTYPE(format) == SDL_PIXELTYPE_PACKED32))) +/** + * A macro to determine if an SDL_PixelFormat is an array format. + * + * Note that this macro double-evaluates its parameter, so do not use + * expressions with side-effects here. + * + * \param format an SDL_PixelFormat to check. + * \returns true if the format is an array, false otherwise. + * + * \threadsafety It is safe to call this macro from any thread. + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_ISPIXELFORMAT_ARRAY(format) \ (!SDL_ISPIXELFORMAT_FOURCC(format) && \ ((SDL_PIXELTYPE(format) == SDL_PIXELTYPE_ARRAYU8) || \ @@ -201,25 +426,84 @@ typedef enum SDL_PackedLayout (SDL_PIXELTYPE(format) == SDL_PIXELTYPE_ARRAYF16) || \ (SDL_PIXELTYPE(format) == SDL_PIXELTYPE_ARRAYF32))) -#define SDL_ISPIXELFORMAT_ALPHA(format) \ - ((SDL_ISPIXELFORMAT_PACKED(format) && \ - ((SDL_PIXELORDER(format) == SDL_PACKEDORDER_ARGB) || \ - (SDL_PIXELORDER(format) == SDL_PACKEDORDER_RGBA) || \ - (SDL_PIXELORDER(format) == SDL_PACKEDORDER_ABGR) || \ - (SDL_PIXELORDER(format) == SDL_PACKEDORDER_BGRA)))) - +/** + * A macro to determine if an SDL_PixelFormat is a 10-bit format. + * + * Note that this macro double-evaluates its parameter, so do not use + * expressions with side-effects here. + * + * \param format an SDL_PixelFormat to check. + * \returns true if the format is 10-bit, false otherwise. + * + * \threadsafety It is safe to call this macro from any thread. + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_ISPIXELFORMAT_10BIT(format) \ (!SDL_ISPIXELFORMAT_FOURCC(format) && \ ((SDL_PIXELTYPE(format) == SDL_PIXELTYPE_PACKED32) && \ (SDL_PIXELLAYOUT(format) == SDL_PACKEDLAYOUT_2101010))) +/** + * A macro to determine if an SDL_PixelFormat is a floating point format. + * + * Note that this macro double-evaluates its parameter, so do not use + * expressions with side-effects here. + * + * \param format an SDL_PixelFormat to check. + * \returns true if the format is 10-bit, false otherwise. + * + * \threadsafety It is safe to call this macro from any thread. + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_ISPIXELFORMAT_FLOAT(format) \ (!SDL_ISPIXELFORMAT_FOURCC(format) && \ ((SDL_PIXELTYPE(format) == SDL_PIXELTYPE_ARRAYF16) || \ (SDL_PIXELTYPE(format) == SDL_PIXELTYPE_ARRAYF32))) -/* The flag is set to 1 because 0x1? is not in the printable ASCII range */ -#define SDL_ISPIXELFORMAT_FOURCC(format) \ +/** + * A macro to determine if an SDL_PixelFormat has an alpha channel. + * + * Note that this macro double-evaluates its parameter, so do not use + * expressions with side-effects here. + * + * \param format an SDL_PixelFormat to check. + * \returns true if the format has alpha, false otherwise. + * + * \threadsafety It is safe to call this macro from any thread. + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_ISPIXELFORMAT_ALPHA(format) \ + ((SDL_ISPIXELFORMAT_PACKED(format) && \ + ((SDL_PIXELORDER(format) == SDL_PACKEDORDER_ARGB) || \ + (SDL_PIXELORDER(format) == SDL_PACKEDORDER_RGBA) || \ + (SDL_PIXELORDER(format) == SDL_PACKEDORDER_ABGR) || \ + (SDL_PIXELORDER(format) == SDL_PACKEDORDER_BGRA))) || \ + (SDL_ISPIXELFORMAT_ARRAY(format) && \ + ((SDL_PIXELORDER(format) == SDL_ARRAYORDER_ARGB) || \ + (SDL_PIXELORDER(format) == SDL_ARRAYORDER_RGBA) || \ + (SDL_PIXELORDER(format) == SDL_ARRAYORDER_ABGR) || \ + (SDL_PIXELORDER(format) == SDL_ARRAYORDER_BGRA)))) + + +/** + * A macro to determine if an SDL_PixelFormat is a "FourCC" format. + * + * This covers custom and other unusual formats. + * + * Note that this macro double-evaluates its parameter, so do not use + * expressions with side-effects here. + * + * \param format an SDL_PixelFormat to check. + * \returns true if the format has alpha, false otherwise. + * + * \threadsafety It is safe to call this macro from any thread. + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_ISPIXELFORMAT_FOURCC(format) /* The flag is set to 1 because 0x1? is not in the printable ASCII range */ \ ((format) && (SDL_PIXELFLAG(format) != 1)) /* Note: If you modify this enum, update SDL_GetPixelFormatName() */ @@ -414,30 +698,6 @@ typedef enum SDL_PixelFormat #endif } SDL_PixelFormat; -/** - * Pixels are a representation of a color in a particular color space. - * - * The first characteristic of a color space is the color type. SDL understands two different color types, RGB and YCbCr, or in SDL also referred to as YUV. - * - * RGB colors consist of red, green, and blue channels of color that are added together to represent the colors we see on the screen. - * https://en.wikipedia.org/wiki/RGB_color_model - * - * YCbCr colors represent colors as a Y luma brightness component and red and blue chroma color offsets. This color representation takes advantage of the fact that the human eye is more sensitive to brightness than the color in an image. The Cb and Cr components are often compressed and have lower resolution than the luma component. - * https://en.wikipedia.org/wiki/YCbCr - * - * When the color information in YCbCr is compressed, the Y pixels are left at full resolution and each Cr and Cb pixel represents an average of the color information in a block of Y pixels. The chroma location determines where in that block of pixels the color information is coming from. - * - * The color range defines how much of the pixel to use when converting a pixel into a color on the display. When the full color range is used, the entire numeric range of the pixel bits is significant. When narrow color range is used, for historical reasons, the pixel uses only a portion of the numeric range to represent colors. - * - * The color primaries and white point are a definition of the colors in the color space relative to the standard XYZ color space. - * https://en.wikipedia.org/wiki/CIE_1931_color_space - * - * The transfer characteristic, or opto-electrical transfer function (OETF), is the way a color is converted from mathematically linear space into a non-linear output signals. - * https://en.wikipedia.org/wiki/Rec._709#Transfer_characteristics - * - * The matrix coefficients are used to convert between YCbCr and RGB colors. - */ - /** * Colorspace color type. * @@ -558,22 +818,177 @@ typedef enum SDL_ChromaLocation /* Colorspace definition */ + +/** + * A macro for defining custom SDL_Colorspace formats. + * + * For example, defining SDL_COLORSPACE_SRGB looks like this: + * + * ```c + * SDL_DEFINE_COLORSPACE(SDL_COLOR_TYPE_RGB, + * SDL_COLOR_RANGE_FULL, + * SDL_COLOR_PRIMARIES_BT709, + * SDL_TRANSFER_CHARACTERISTICS_SRGB, + * SDL_MATRIX_COEFFICIENTS_IDENTITY, + * SDL_CHROMA_LOCATION_NONE) + * ``` + * + * \param type the type of the new format, probably an SDL_ColorType value. + * \param range the range of the new format, probably a SDL_ColorRange value. + * \param primaries the primaries of the new format, probably an + * SDL_ColorPrimaries value. + * \param transfer the transfer characteristics of the new format, probably an + * SDL_TransferCharacteristics value. + * \param matrix the matrix coefficients of the new format, probably an + * SDL_MatrixCoefficients value. + * \param chroma the chroma sample location of the new format, probably an + * SDL_ChromaLocation value. + * \returns a format value in the style of SDL_Colorspace. + * + * \threadsafety It is safe to call this macro from any thread. + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_DEFINE_COLORSPACE(type, range, primaries, transfer, matrix, chroma) \ (((Uint32)(type) << 28) | ((Uint32)(range) << 24) | ((Uint32)(chroma) << 20) | \ ((Uint32)(primaries) << 10) | ((Uint32)(transfer) << 5) | ((Uint32)(matrix) << 0)) -#define SDL_COLORSPACETYPE(X) (SDL_ColorType)(((X) >> 28) & 0x0F) -#define SDL_COLORSPACERANGE(X) (SDL_ColorRange)(((X) >> 24) & 0x0F) -#define SDL_COLORSPACECHROMA(X) (SDL_ChromaLocation)(((X) >> 20) & 0x0F) -#define SDL_COLORSPACEPRIMARIES(X) (SDL_ColorPrimaries)(((X) >> 10) & 0x1F) -#define SDL_COLORSPACETRANSFER(X) (SDL_TransferCharacteristics)(((X) >> 5) & 0x1F) -#define SDL_COLORSPACEMATRIX(X) (SDL_MatrixCoefficients)((X) & 0x1F) +/** + * A macro to retrieve the type of an SDL_Colorspace. + * + * \param cspace an SDL_Colorspace to check. + * \returns the SDL_ColorType for `cspace`. + * + * \threadsafety It is safe to call this macro from any thread. + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_COLORSPACETYPE(cspace) (SDL_ColorType)(((cspace) >> 28) & 0x0F) -#define SDL_ISCOLORSPACE_MATRIX_BT601(X) (SDL_COLORSPACEMATRIX(X) == SDL_MATRIX_COEFFICIENTS_BT601 || SDL_COLORSPACEMATRIX(X) == SDL_MATRIX_COEFFICIENTS_BT470BG) -#define SDL_ISCOLORSPACE_MATRIX_BT709(X) (SDL_COLORSPACEMATRIX(X) == SDL_MATRIX_COEFFICIENTS_BT709) -#define SDL_ISCOLORSPACE_MATRIX_BT2020_NCL(X) (SDL_COLORSPACEMATRIX(X) == SDL_MATRIX_COEFFICIENTS_BT2020_NCL) -#define SDL_ISCOLORSPACE_LIMITED_RANGE(X) (SDL_COLORSPACERANGE(X) != SDL_COLOR_RANGE_FULL) -#define SDL_ISCOLORSPACE_FULL_RANGE(X) (SDL_COLORSPACERANGE(X) == SDL_COLOR_RANGE_FULL) +/** + * A macro to retrieve the range of an SDL_Colorspace. + * + * \param cspace an SDL_Colorspace to check. + * \returns the SDL_ColorRange of `cspace`. + * + * \threadsafety It is safe to call this macro from any thread. + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_COLORSPACERANGE(cspace) (SDL_ColorRange)(((cspace) >> 24) & 0x0F) + +/** + * A macro to retrieve the chroma sample location of an SDL_Colorspace. + * + * \param cspace an SDL_Colorspace to check. + * \returns the SDL_ChromaLocation of `cspace`. + * + * \threadsafety It is safe to call this macro from any thread. + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_COLORSPACECHROMA(cspace) (SDL_ChromaLocation)(((cspace) >> 20) & 0x0F) + +/** + * A macro to retrieve the primaries of an SDL_Colorspace. + * + * \param cspace an SDL_Colorspace to check. + * \returns the SDL_ColorPrimaries of `cspace`. + * + * \threadsafety It is safe to call this macro from any thread. + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_COLORSPACEPRIMARIES(cspace) (SDL_ColorPrimaries)(((cspace) >> 10) & 0x1F) + +/** + * A macro to retrieve the transfer characteristics of an SDL_Colorspace. + * + * \param cspace an SDL_Colorspace to check. + * \returns the SDL_TransferCharacteristics of `cspace`. + * + * \threadsafety It is safe to call this macro from any thread. + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_COLORSPACETRANSFER(cspace) (SDL_TransferCharacteristics)(((cspace) >> 5) & 0x1F) + +/** + * A macro to retrieve the matrix coefficients of an SDL_Colorspace. + * + * \param cspace an SDL_Colorspace to check. + * \returns the SDL_MatrixCoefficients of `cspace`. + * + * \threadsafety It is safe to call this macro from any thread. + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_COLORSPACEMATRIX(cspace) (SDL_MatrixCoefficients)((cspace) & 0x1F) + +/** + * A macro to determine if an SDL_Colorspace uses BT601 (or BT470BG) matrix + * coefficients. + * + * Note that this macro double-evaluates its parameter, so do not use + * expressions with side-effects here. + * + * \param cspace an SDL_Colorspace to check. + * \returns true if BT601 or BT470BG, false otherwise. + * + * \threadsafety It is safe to call this macro from any thread. + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_ISCOLORSPACE_MATRIX_BT601(cspace) (SDL_COLORSPACEMATRIX(cspace) == SDL_MATRIX_COEFFICIENTS_BT601 || SDL_COLORSPACEMATRIX(cspace) == SDL_MATRIX_COEFFICIENTS_BT470BG) + +/** + * A macro to determine if an SDL_Colorspace uses BT709 matrix coefficients. + * + * \param cspace an SDL_Colorspace to check. + * \returns true if BT709, false otherwise. + * + * \threadsafety It is safe to call this macro from any thread. + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_ISCOLORSPACE_MATRIX_BT709(cspace) (SDL_COLORSPACEMATRIX(cspace) == SDL_MATRIX_COEFFICIENTS_BT709) + +/** + * A macro to determine if an SDL_Colorspace uses BT2020_NCL matrix + * coefficients. + * + * \param cspace an SDL_Colorspace to check. + * \returns true if BT2020_NCL, false otherwise. + * + * \threadsafety It is safe to call this macro from any thread. + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_ISCOLORSPACE_MATRIX_BT2020_NCL(cspace) (SDL_COLORSPACEMATRIX(cspace) == SDL_MATRIX_COEFFICIENTS_BT2020_NCL) + +/** + * A macro to determine if an SDL_Colorspace has a limited range. + * + * \param cspace an SDL_Colorspace to check. + * \returns true if limited range, false otherwise. + * + * \threadsafety It is safe to call this macro from any thread. + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_ISCOLORSPACE_LIMITED_RANGE(cspace) (SDL_COLORSPACERANGE(cspace) != SDL_COLOR_RANGE_FULL) + +/** + * A macro to determine if an SDL_Colorspace has a full range. + * + * \param cspace an SDL_Colorspace to check. + * \returns true if full range, false otherwise. + * + * \threadsafety It is safe to call this macro from any thread. + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_ISCOLORSPACE_FULL_RANGE(cspace) (SDL_COLORSPACERANGE(cspace) == SDL_COLOR_RANGE_FULL) /** * Colorspace definitions. diff --git a/libs/SDL3/include/SDL3/SDL_platform_defines.h b/libs/SDL3/include/SDL3/SDL_platform_defines.h index 89ce02d4..82661bb3 100644 --- a/libs/SDL3/include/SDL3/SDL_platform_defines.h +++ b/libs/SDL3/include/SDL3/SDL_platform_defines.h @@ -112,16 +112,6 @@ #undef SDL_PLATFORM_LINUX #endif -#ifdef __NGAGE__ - -/** - * A preprocessor macro that is only defined if compiling for Nokia N-Gage. - * - * \since This macro is available since SDL 3.1.3. - */ -#define SDL_PLATFORM_NGAGE 1 -#endif - #if defined(__unix__) || defined(__unix) || defined(unix) /** @@ -367,7 +357,16 @@ #define WINAPI_FAMILY_WINRT 0 #endif /* HAVE_WINAPIFAMILY_H */ -#if HAVE_WINAPIFAMILY_H && HAVE_WINAPIFAMILY_H +#ifdef SDL_WIKI_DOCUMENTATION_SECTION + +/** + * A preprocessor macro that defined to 1 if compiling for Windows Phone. + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_WINAPI_FAMILY_PHONE (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) + +#elif HAVE_WINAPIFAMILY_H && HAVE_WINAPIFAMILY_H #define SDL_WINAPI_FAMILY_PHONE (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) #else #define SDL_WINAPI_FAMILY_PHONE 0 diff --git a/libs/SDL3/include/SDL3/SDL_process.h b/libs/SDL3/include/SDL3/SDL_process.h index 32042a3e..7cabeee0 100644 --- a/libs/SDL3/include/SDL3/SDL_process.h +++ b/libs/SDL3/include/SDL3/SDL_process.h @@ -54,6 +54,13 @@ extern "C" { #endif +/** + * An opaque handle representing a system process. + * + * \since This datatype is available since SDL 3.1.3. + * + * \sa SDL_CreateProcess + */ typedef struct SDL_Process SDL_Process; /** @@ -102,8 +109,8 @@ extern SDL_DECLSPEC SDL_Process *SDLCALL SDL_CreateProcess(const char * const *a * Description of where standard I/O should be directed when creating a * process. * - * If a standard I/O stream is set to SDL_PROCESS_STDIO_INHERIT, it will go to - * the same place as the application's I/O stream. This is the default for + * If a standard I/O stream is set to SDL_PROCESS_STDIO_INHERITED, it will go + * to the same place as the application's I/O stream. This is the default for * standard output and standard error. * * If a standard I/O stream is set to SDL_PROCESS_STDIO_NULL, it is connected @@ -298,7 +305,7 @@ extern SDL_DECLSPEC void * SDLCALL SDL_ReadProcess(SDL_Process *process, size_t * * Writing to this stream can return less data than expected if the process * hasn't read its input. It may be blocked waiting for its output to be read, - * so if you may need to call SDL_GetOutputStream() and read the output in + * if so you may need to call SDL_GetProcessOutput() and read the output in * parallel with writing input. * * \param process The process to get the input stream for. @@ -371,6 +378,12 @@ extern SDL_DECLSPEC bool SDLCALL SDL_KillProcess(SDL_Process *process, bool forc * normally, a negative signal if it terminated due to a signal, or -255 * otherwise. It will not be changed if the process is still running. * + * If you create a process with standard output piped to the application + * (`pipe_stdio` being true) then you should read all of the process output + * before calling SDL_WaitProcess(). If you don't do this the process might be + * blocked indefinitely waiting for output to be read and SDL_WaitProcess() + * will never return true; + * * \param process The process to wait for. * \param block If true, block until the process finishes; otherwise, report * on the process' status. diff --git a/libs/SDL3/include/SDL3/SDL_render.h b/libs/SDL3/include/SDL3/SDL_render.h index d89558ac..a466a33d 100644 --- a/libs/SDL3/include/SDL3/SDL_render.h +++ b/libs/SDL3/include/SDL3/SDL_render.h @@ -201,7 +201,7 @@ extern SDL_DECLSPEC const char * SDLCALL SDL_GetRenderDriver(int index); * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -229,7 +229,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_CreateWindowAndRenderer(const char *title, * \returns a valid rendering context or NULL if there was an error; call * SDL_GetError() for more information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -253,7 +253,7 @@ extern SDL_DECLSPEC SDL_Renderer * SDLCALL SDL_CreateRenderer(SDL_Window *window * displayed, required if this isn't a software renderer using a surface * - `SDL_PROP_RENDERER_CREATE_SURFACE_POINTER`: the surface where rendering * is displayed, if you want a software renderer without a window - * - `SDL_PROP_RENDERER_CREATE_OUTPUT_COLORSPACE_NUMBER`: an SDL_ColorSpace + * - `SDL_PROP_RENDERER_CREATE_OUTPUT_COLORSPACE_NUMBER`: an SDL_Colorspace * value describing the colorspace for output to the display, defaults to * SDL_COLORSPACE_SRGB. The direct3d11, direct3d12, and metal renderers * support SDL_COLORSPACE_SRGB_LINEAR, which is a linear color space and @@ -283,7 +283,7 @@ extern SDL_DECLSPEC SDL_Renderer * SDLCALL SDL_CreateRenderer(SDL_Window *window * \returns a valid rendering context or NULL if there was an error; call * SDL_GetError() for more information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -320,7 +320,7 @@ extern SDL_DECLSPEC SDL_Renderer * SDLCALL SDL_CreateRendererWithProperties(SDL_ * \returns a valid rendering context or NULL if there was an error; call * SDL_GetError() for more information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -386,21 +386,21 @@ extern SDL_DECLSPEC const char * SDLCALL SDL_GetRendererName(SDL_Renderer *rende * - `SDL_PROP_RENDERER_TEXTURE_FORMATS_POINTER`: a (const SDL_PixelFormat *) * array of pixel formats, terminated with SDL_PIXELFORMAT_UNKNOWN, * representing the available texture formats for this renderer. - * - `SDL_PROP_RENDERER_OUTPUT_COLORSPACE_NUMBER`: an SDL_ColorSpace value + * - `SDL_PROP_RENDERER_OUTPUT_COLORSPACE_NUMBER`: an SDL_Colorspace value * describing the colorspace for output to the display, defaults to * SDL_COLORSPACE_SRGB. * - `SDL_PROP_RENDERER_HDR_ENABLED_BOOLEAN`: true if the output colorspace is * SDL_COLORSPACE_SRGB_LINEAR and the renderer is showing on a display with * HDR enabled. This property can change dynamically when - * SDL_EVENT_DISPLAY_HDR_STATE_CHANGED is sent. + * SDL_EVENT_WINDOW_HDR_STATE_CHANGED is sent. * - `SDL_PROP_RENDERER_SDR_WHITE_POINT_FLOAT`: the value of SDR white in the * SDL_COLORSPACE_SRGB_LINEAR colorspace. When HDR is enabled, this value is * automatically multiplied into the color scale. This property can change - * dynamically when SDL_EVENT_DISPLAY_HDR_STATE_CHANGED is sent. + * dynamically when SDL_EVENT_WINDOW_HDR_STATE_CHANGED is sent. * - `SDL_PROP_RENDERER_HDR_HEADROOM_FLOAT`: the additional high dynamic range * that can be displayed, in terms of the SDR white point. When HDR is not * enabled, this will be 1.0. This property can change dynamically when - * SDL_EVENT_DISPLAY_HDR_STATE_CHANGED is sent. + * SDL_EVENT_WINDOW_HDR_STATE_CHANGED is sent. * * With the direct3d renderer: * @@ -441,6 +441,11 @@ extern SDL_DECLSPEC const char * SDLCALL SDL_GetRendererName(SDL_Renderer *rende * swapchain images, or potential frames in flight, used by the Vulkan * renderer * + * With the gpu renderer: + * + * - `SDL_PROP_RENDERER_GPU_DEVICE_POINTER`: the SDL_GPUDevice associated with + * the renderer + * * \param renderer the rendering context. * \returns a valid property ID on success or 0 on failure; call * SDL_GetError() for more information. @@ -474,6 +479,7 @@ extern SDL_DECLSPEC SDL_PropertiesID SDLCALL SDL_GetRendererProperties(SDL_Rende #define SDL_PROP_RENDERER_VULKAN_GRAPHICS_QUEUE_FAMILY_INDEX_NUMBER "SDL.renderer.vulkan.graphics_queue_family_index" #define SDL_PROP_RENDERER_VULKAN_PRESENT_QUEUE_FAMILY_INDEX_NUMBER "SDL.renderer.vulkan.present_queue_family_index" #define SDL_PROP_RENDERER_VULKAN_SWAPCHAIN_IMAGE_COUNT_NUMBER "SDL.renderer.vulkan.swapchain_image_count" +#define SDL_PROP_RENDERER_GPU_DEVICE_POINTER "SDL.renderer.gpu.device" /** * Get the output size in pixels of a rendering context. @@ -487,7 +493,7 @@ extern SDL_DECLSPEC SDL_PropertiesID SDLCALL SDL_GetRendererProperties(SDL_Rende * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -509,7 +515,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetRenderOutputSize(SDL_Renderer *renderer, * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -531,7 +537,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetCurrentRenderOutputSize(SDL_Renderer *re * was active, the format was unsupported, or the width or height * were out of range; call SDL_GetError() for more information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -561,7 +567,7 @@ extern SDL_DECLSPEC SDL_Texture * SDLCALL SDL_CreateTexture(SDL_Renderer *render * \returns the created texture or NULL on failure; call SDL_GetError() for * more information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -576,7 +582,7 @@ extern SDL_DECLSPEC SDL_Texture * SDLCALL SDL_CreateTextureFromSurface(SDL_Rende * * These are the supported properties: * - * - `SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER`: an SDL_ColorSpace value + * - `SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER`: an SDL_Colorspace value * describing the texture colorspace, defaults to SDL_COLORSPACE_SRGB_LINEAR * for floating point textures, SDL_COLORSPACE_HDR10 for 10-bit textures, * SDL_COLORSPACE_SRGB for other RGB textures and SDL_COLORSPACE_JPEG for @@ -671,7 +677,7 @@ extern SDL_DECLSPEC SDL_Texture * SDLCALL SDL_CreateTextureFromSurface(SDL_Rende * was active, the format was unsupported, or the width or height * were out of range; call SDL_GetError() for more information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -713,7 +719,7 @@ extern SDL_DECLSPEC SDL_Texture * SDLCALL SDL_CreateTextureWithProperties(SDL_Re * * The following read-only properties are provided by SDL: * - * - `SDL_PROP_TEXTURE_COLORSPACE_NUMBER`: an SDL_ColorSpace value describing + * - `SDL_PROP_TEXTURE_COLORSPACE_NUMBER`: an SDL_Colorspace value describing * the texture colorspace. * - `SDL_PROP_TEXTURE_FORMAT_NUMBER`: one of the enumerated values in * SDL_PixelFormat. @@ -753,14 +759,8 @@ extern SDL_DECLSPEC SDL_Texture * SDLCALL SDL_CreateTextureWithProperties(SDL_Re * * With the vulkan renderer: * - * - `SDL_PROP_TEXTURE_VULKAN_TEXTURE_POINTER`: the VkImage associated with - * the texture - * - `SDL_PROP_TEXTURE_VULKAN_TEXTURE_U_POINTER`: the VkImage associated with - * the U plane of a YUV texture - * - `SDL_PROP_TEXTURE_VULKAN_TEXTURE_V_POINTER`: the VkImage associated with - * the V plane of a YUV texture - * - `SDL_PROP_TEXTURE_VULKAN_TEXTURE_UV_POINTER`: the VkImage associated with - * the UV plane of a NV12/NV21 texture + * - `SDL_PROP_TEXTURE_VULKAN_TEXTURE_NUMBER`: the VkImage associated with the + * texture * * With the opengl renderer: * @@ -792,11 +792,6 @@ extern SDL_DECLSPEC SDL_Texture * SDLCALL SDL_CreateTextureWithProperties(SDL_Re * - `SDL_PROP_TEXTURE_OPENGLES2_TEXTURE_TARGET_NUMBER`: the GLenum for the * texture target (`GL_TEXTURE_2D`, `GL_TEXTURE_EXTERNAL_OES`, etc) * - * With the vulkan renderer: - * - * - `SDL_PROP_TEXTURE_VULKAN_TEXTURE_NUMBER`: the VkImage associated with the - * texture - * * \param texture the texture to query. * \returns a valid property ID on success or 0 on failure; call * SDL_GetError() for more information. @@ -858,7 +853,7 @@ extern SDL_DECLSPEC SDL_Renderer * SDLCALL SDL_GetRendererFromTexture(SDL_Textur * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. */ @@ -883,7 +878,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetTextureSize(SDL_Texture *texture, float * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -913,7 +908,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetTextureColorMod(SDL_Texture *texture, Ui * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -934,7 +929,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetTextureColorModFloat(SDL_Texture *textur * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -954,7 +949,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetTextureColorMod(SDL_Texture *texture, Ui * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -980,7 +975,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetTextureColorModFloat(SDL_Texture *textur * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -1006,7 +1001,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetTextureAlphaMod(SDL_Texture *texture, Ui * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -1024,7 +1019,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetTextureAlphaModFloat(SDL_Texture *textur * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -1042,7 +1037,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetTextureAlphaMod(SDL_Texture *texture, Ui * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -1063,7 +1058,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetTextureAlphaModFloat(SDL_Texture *textur * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -1079,7 +1074,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetTextureBlendMode(SDL_Texture *texture, S * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -1099,7 +1094,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetTextureBlendMode(SDL_Texture *texture, S * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -1115,7 +1110,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetTextureScaleMode(SDL_Texture *texture, S * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -1146,7 +1141,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetTextureScaleMode(SDL_Texture *texture, S * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -1180,7 +1175,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_UpdateTexture(SDL_Texture *texture, const S * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -1212,7 +1207,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_UpdateYUVTexture(SDL_Texture *texture, * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -1247,7 +1242,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_UpdateNVTexture(SDL_Texture *texture, * created with `SDL_TEXTUREACCESS_STREAMING`; call SDL_GetError() * for more information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -1285,7 +1280,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_LockTexture(SDL_Texture *texture, * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -1307,7 +1302,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_LockTextureToSurface(SDL_Texture *texture, * * \param texture a texture locked by SDL_LockTexture(). * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -1329,7 +1324,7 @@ extern SDL_DECLSPEC void SDLCALL SDL_UnlockTexture(SDL_Texture *texture); * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -1346,7 +1341,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetRenderTarget(SDL_Renderer *renderer, SDL * \param renderer the rendering context. * \returns the current render target or NULL for the default render target. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -1385,7 +1380,7 @@ extern SDL_DECLSPEC SDL_Texture * SDLCALL SDL_GetRenderTarget(SDL_Renderer *rend * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -1408,7 +1403,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetRenderLogicalPresentation(SDL_Renderer * * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -1430,7 +1425,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetRenderLogicalPresentation(SDL_Renderer * * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -1456,7 +1451,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetRenderLogicalPresentationRect(SDL_Render * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -1485,7 +1480,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_RenderCoordinatesFromWindow(SDL_Renderer *r * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -1515,7 +1510,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_RenderCoordinatesToWindow(SDL_Renderer *ren * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -1538,7 +1533,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_ConvertEventToRenderCoordinates(SDL_Rendere * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -1555,7 +1550,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetRenderViewport(SDL_Renderer *renderer, c * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -1575,7 +1570,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetRenderViewport(SDL_Renderer *renderer, S * \returns true if the viewport was set to a specific rectangle, or false if * it was set to NULL (the entire target). * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -1600,7 +1595,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_RenderViewportSet(SDL_Renderer *renderer); * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. */ @@ -1615,7 +1610,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetRenderSafeArea(SDL_Renderer *renderer, S * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -1633,7 +1628,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetRenderClipRect(SDL_Renderer *renderer, c * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -1649,7 +1644,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetRenderClipRect(SDL_Renderer *renderer, S * \returns true if clipping is enabled or false if not; call SDL_GetError() * for more information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -1675,7 +1670,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_RenderClipEnabled(SDL_Renderer *renderer); * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -1692,7 +1687,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetRenderScale(SDL_Renderer *renderer, floa * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -1716,7 +1711,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetRenderScale(SDL_Renderer *renderer, floa * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -1741,7 +1736,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetRenderDrawColor(SDL_Renderer *renderer, * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -1765,7 +1760,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetRenderDrawColorFloat(SDL_Renderer *rende * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -1789,7 +1784,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetRenderDrawColor(SDL_Renderer *renderer, * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -1814,7 +1809,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetRenderDrawColorFloat(SDL_Renderer *rende * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -1830,7 +1825,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetRenderColorScale(SDL_Renderer *renderer, * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -1848,7 +1843,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetRenderColorScale(SDL_Renderer *renderer, * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -1864,7 +1859,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetRenderDrawBlendMode(SDL_Renderer *render * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -1884,7 +1879,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetRenderDrawBlendMode(SDL_Renderer *render * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -1901,7 +1896,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_RenderClear(SDL_Renderer *renderer); * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -1918,7 +1913,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_RenderPoint(SDL_Renderer *renderer, float x * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -1937,7 +1932,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_RenderPoints(SDL_Renderer *renderer, const * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -1955,7 +1950,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_RenderLine(SDL_Renderer *renderer, float x1 * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -1972,7 +1967,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_RenderLines(SDL_Renderer *renderer, const S * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -1990,7 +1985,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_RenderRect(SDL_Renderer *renderer, const SD * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -2008,7 +2003,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_RenderRects(SDL_Renderer *renderer, const S * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -2026,7 +2021,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_RenderFillRect(SDL_Renderer *renderer, cons * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -2047,7 +2042,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_RenderFillRects(SDL_Renderer *renderer, con * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -2076,7 +2071,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_RenderTexture(SDL_Renderer *renderer, SDL_T * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -2087,6 +2082,36 @@ extern SDL_DECLSPEC bool SDLCALL SDL_RenderTextureRotated(SDL_Renderer *renderer double angle, const SDL_FPoint *center, SDL_FlipMode flip); +/** + * Copy a portion of the source texture to the current rendering target, with + * affine transform, at subpixel precision. + * + * \param renderer the renderer which should copy parts of a texture. + * \param texture the source texture. + * \param srcrect a pointer to the source rectangle, or NULL for the entire + * texture. + * \param origin a pointer to a point indicating where the top-left corner of + * srcrect should be mapped to, or NULL for the rendering + * target's origin. + * \param right a pointer to a point indicating where the top-right corner of + * srcrect should be mapped to, or NULL for the rendering + * target's top-right corner. + * \param down a pointer to a point indicating where the bottom-left corner of + * srcrect should be mapped to, or NULL for the rendering target's + * bottom-left corner. + * \returns true on success or false on failure; call SDL_GetError() for more + * information. + * + * \threadsafety You may only call this function from the main thread. + * + * \since This function is available since SDL 3.2.0. + * + * \sa SDL_RenderTexture + */ +extern SDL_DECLSPEC bool SDLCALL SDL_RenderTextureAffine(SDL_Renderer *renderer, SDL_Texture *texture, + const SDL_FRect *srcrect, const SDL_FPoint *origin, + const SDL_FPoint *right, const SDL_FPoint *down); + /** * Tile a portion of the texture to the current rendering target at subpixel * precision. @@ -2106,7 +2131,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_RenderTextureRotated(SDL_Renderer *renderer * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -2140,7 +2165,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_RenderTextureTiled(SDL_Renderer *renderer, * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -2164,7 +2189,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_RenderTexture9Grid(SDL_Renderer *renderer, * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -2196,7 +2221,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_RenderGeometry(SDL_Renderer *renderer, * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -2225,7 +2250,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_RenderGeometryRaw(SDL_Renderer *renderer, * \returns a new SDL_Surface on success or NULL on failure; call * SDL_GetError() for more information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. */ @@ -2262,7 +2287,7 @@ extern SDL_DECLSPEC SDL_Surface * SDLCALL SDL_RenderReadPixels(SDL_Renderer *ren * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -2289,7 +2314,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_RenderPresent(SDL_Renderer *renderer); * * \param texture the texture to destroy. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -2306,7 +2331,7 @@ extern SDL_DECLSPEC void SDLCALL SDL_DestroyTexture(SDL_Texture *texture); * * \param renderer the rendering context. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -2341,7 +2366,7 @@ extern SDL_DECLSPEC void SDLCALL SDL_DestroyRenderer(SDL_Renderer *renderer); * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. */ @@ -2357,7 +2382,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_FlushRenderer(SDL_Renderer *renderer); * \returns a `CAMetalLayer *` on success, or NULL if the renderer isn't a * Metal renderer. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -2380,7 +2405,7 @@ extern SDL_DECLSPEC void * SDLCALL SDL_GetRenderMetalLayer(SDL_Renderer *rendere * \returns an `id` on success, or NULL if the * renderer isn't a Metal renderer or there was an error. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -2436,7 +2461,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_AddVulkanRenderSemaphores(SDL_Renderer *ren * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -2456,7 +2481,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetRenderVSync(SDL_Renderer *renderer, int * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * * \since This function is available since SDL 3.1.3. * @@ -2507,14 +2532,43 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetRenderVSync(SDL_Renderer *renderer, int * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \threadsafety You may only call this function from the main thread. + * \threadsafety This function should only be called on the main thread. * - * \since This function is available since SDL 3.2.0. + * \since This function is available since SDL 3.1.6. * + * \sa SDL_RenderDebugTextFormat * \sa SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE */ extern SDL_DECLSPEC bool SDLCALL SDL_RenderDebugText(SDL_Renderer *renderer, float x, float y, const char *str); +/** + * Draw debug text to an SDL_Renderer. + * + * This function will render a printf()-style format string to a renderer. + * Note that this is a convinence function for debugging, with severe + * limitations, and is not intended to be used for production apps and games. + * + * For the full list of limitations and other useful information, see + * SDL_RenderDebugText. + * + * \param renderer the renderer which should draw the text. + * \param x the x coordinate where the top-left corner of the text will draw. + * \param y the y coordinate where the top-left corner of the text will draw. + * \param fmt the format string to draw. + * \param ... additional parameters matching % tokens in the `fmt` string, if + * any. + * \returns true on success or false on failure; call SDL_GetError() for more + * information. + * + * \threadsafety This function should only be called on the main thread. + * + * \since This function is available since SDL 3.2.0. + * + * \sa SDL_RenderDebugText + * \sa SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE + */ +extern SDL_DECLSPEC bool SDLCALL SDL_RenderDebugTextFormat(SDL_Renderer *renderer, float x, float y, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) SDL_PRINTF_VARARG_FUNC(4); + /* Ends C function definitions when using C++ */ #ifdef __cplusplus } diff --git a/libs/SDL3/include/SDL3/SDL_scancode.h b/libs/SDL3/include/SDL3/SDL_scancode.h index deda12fe..1259f800 100644 --- a/libs/SDL3/include/SDL3/SDL_scancode.h +++ b/libs/SDL3/include/SDL3/SDL_scancode.h @@ -23,6 +23,11 @@ * # CategoryScancode * * Defines keyboard scancodes. + * + * Please refer to the Best Keyboard Practices document for details on what + * this information means and how best to use it. + * + * https://wiki.libsdl.org/SDL3/BestKeyboardPractices */ #ifndef SDL_scancode_h_ diff --git a/libs/SDL3/include/SDL3/SDL_sensor.h b/libs/SDL3/include/SDL3/SDL_sensor.h index 13862913..008c8d68 100644 --- a/libs/SDL3/include/SDL3/SDL_sensor.h +++ b/libs/SDL3/include/SDL3/SDL_sensor.h @@ -44,6 +44,11 @@ extern "C" { /* *INDENT-ON* */ #endif +/** + * The opaque structure used to identify an opened SDL sensor. + * + * \since This struct is available since SDL 3.1.3. + */ typedef struct SDL_Sensor SDL_Sensor; /** diff --git a/libs/SDL3/include/SDL3/SDL_stdinc.h b/libs/SDL3/include/SDL3/SDL_stdinc.h index 2f96a9f9..4a9c525a 100644 --- a/libs/SDL3/include/SDL3/SDL_stdinc.h +++ b/libs/SDL3/include/SDL3/SDL_stdinc.h @@ -88,14 +88,66 @@ void *alloca(size_t); # endif #endif -#ifdef SIZE_MAX +#ifdef SDL_WIKI_DOCUMENTATION_SECTION + +/** + * The largest value that a `size_t` can hold for the target platform. + * + * `size_t` is generally the same size as a pointer in modern times, but this + * can get weird on very old and very esoteric machines. For example, on a + * 16-bit Intel 286, you might have a 32-bit "far" pointer (16-bit segment + * plus 16-bit offset), but `size_t` is 16 bits, because it can only deal with + * the offset into an individual segment. + * + * In modern times, it's generally expected to cover an entire linear address + * space. But be careful! + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_SIZE_MAX SIZE_MAX + +#elif defined(SIZE_MAX) # define SDL_SIZE_MAX SIZE_MAX #else # define SDL_SIZE_MAX ((size_t) -1) #endif #ifndef SDL_COMPILE_TIME_ASSERT -#if defined(__cplusplus) +#ifdef SDL_WIKI_DOCUMENTATION_SECTION + +/** + * A compile-time assertion. + * + * This can check constant values _known to the compiler at build time_ for + * correctness, and end the compile with the error if they fail. + * + * Often times these are used to verify basic truths, like the size of a + * datatype is what is expected: + * + * ```c + * SDL_COMPILE_TIME_ASSERT(uint32_size, sizeof(Uint32) == 4); + * ``` + * + * The `name` parameter must be a valid C symbol, and must be unique across + * all compile-time asserts in the same compilation unit (one run of the + * compiler), or the build might fail with cryptic errors on some targets. + * This is used with a C language trick that works on older compilers that + * don't support better assertion techniques. + * + * If you need an assertion that operates at runtime, on variable data, you + * should try SDL_assert instead. + * + * \param name a unique identifier for this assertion. + * \param x the value to test. Must be a boolean value. + * + * \threadsafety This macro doesn't generate any code to run. + * + * \since This macro is available since SDL 3.1.3. + * + * \sa SDL_assert + */ +#define SDL_COMPILE_TIME_ASSERT(name, x) FailToCompileIf_x_IsFalse(x) +#elif defined(__cplusplus) /* Keep C++ case alone: Some versions of gcc will define __STDC_VERSION__ even when compiling in C++ mode. */ #if (__cplusplus >= 201103L) #define SDL_COMPILE_TIME_ASSERT(name, x) static_assert(x, #x) @@ -114,18 +166,10 @@ void *alloca(size_t); #endif /** - * Check if the compiler supports a given builtin. - * Supported by virtually all clang versions and recent gcc. Use this - * instead of checking the clang version if possible. - */ -#ifdef __has_builtin -#define SDL_HAS_BUILTIN(x) __has_builtin(x) -#else -#define SDL_HAS_BUILTIN(x) 0 -#endif - -/** - * The number of elements in an array. + * The number of elements in a static array. + * + * This will compile but return incorrect results for a pointer to an array; + * it has to be an array the compiler knows the size of. * * This macro looks like it double-evaluates the argument, but it does so * inside of `sizeof`, so there are no side-effects here, as expressions do @@ -144,6 +188,8 @@ void *alloca(size_t); * #define LOG_ERROR(X) OutputDebugString(SDL_STRINGIFY_ARG(__FUNCTION__) ": " X "\n")` * ``` * + * \param arg the text to turn into a string literal. + * * \since This macro is available since SDL 3.1.3. */ #define SDL_STRINGIFY_ARG(arg) #arg @@ -434,14 +480,191 @@ typedef Sint64 SDL_Time; /* @} *//* Floating-point constants */ +#ifdef SDL_WIKI_DOCUMENTATION_SECTION + +/** + * A printf-formatting string for an Sint64 value. + * + * Use it like this: + * + * ```c + * SDL_Log("There are %" SDL_PRIs64 " bottles of beer on the wall.", bottles); + * ``` + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_PRIs64 "lld" + +/** + * A printf-formatting string for a Uint64 value. + * + * Use it like this: + * + * ```c + * SDL_Log("There are %" SDL_PRIu64 " bottles of beer on the wall.", bottles); + * ``` + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_PRIu64 "llu" + +/** + * A printf-formatting string for a Uint64 value as lower-case hexadecimal. + * + * Use it like this: + * + * ```c + * SDL_Log("There are %" SDL_PRIx64 " bottles of beer on the wall.", bottles); + * ``` + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_PRIx64 "llx" + +/** + * A printf-formatting string for a Uint64 value as upper-case hexadecimal. + * + * Use it like this: + * + * ```c + * SDL_Log("There are %" SDL_PRIX64 " bottles of beer on the wall.", bottles); + * ``` + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_PRIX64 "llX" + +/** + * A printf-formatting string for an Sint32 value. + * + * Use it like this: + * + * ```c + * SDL_Log("There are %" SDL_PRIs32 " bottles of beer on the wall.", bottles); + * ``` + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_PRIs32 "d" + +/** + * A printf-formatting string for a Uint32 value. + * + * Use it like this: + * + * ```c + * SDL_Log("There are %" SDL_PRIu32 " bottles of beer on the wall.", bottles); + * ``` + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_PRIu32 "u" + +/** + * A printf-formatting string for a Uint32 value as lower-case hexadecimal. + * + * Use it like this: + * + * ```c + * SDL_Log("There are %" SDL_PRIx32 " bottles of beer on the wall.", bottles); + * ``` + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_PRIx32 "x" + +/** + * A printf-formatting string for a Uint32 value as upper-case hexadecimal. + * + * Use it like this: + * + * ```c + * SDL_Log("There are %" SDL_PRIX32 " bottles of beer on the wall.", bottles); + * ``` + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_PRIX32 "X" + +/** + * A printf-formatting string prefix for a `long long` value. + * + * This is just the prefix! You probably actually want SDL_PRILLd, SDL_PRILLu, + * SDL_PRILLx, or SDL_PRILLX instead. + * + * Use it like this: + * + * ```c + * SDL_Log("There are %" SDL_PRILL_PREFIX "d bottles of beer on the wall.", bottles); + * ``` + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_PRILL_PREFIX "ll" + +/** + * A printf-formatting string for a `long long` value. + * + * Use it like this: + * + * ```c + * SDL_Log("There are %" SDL_PRILLd " bottles of beer on the wall.", bottles); + * ``` + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_PRILLd SDL_PRILL_PREFIX "d" + +/** + * A printf-formatting string for a `unsigned long long` value. + * + * Use it like this: + * + * ```c + * SDL_Log("There are %" SDL_PRILLu " bottles of beer on the wall.", bottles); + * ``` + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_PRILLu SDL_PRILL_PREFIX "u" + +/** + * A printf-formatting string for an `unsigned long long` value as lower-case + * hexadecimal. + * + * Use it like this: + * + * ```c + * SDL_Log("There are %" SDL_PRILLx " bottles of beer on the wall.", bottles); + * ``` + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_PRILLx SDL_PRILL_PREFIX "x" + +/** + * A printf-formatting string for an `unsigned long long` value as upper-case + * hexadecimal. + * + * Use it like this: + * + * ```c + * SDL_Log("There are %" SDL_PRILLX " bottles of beer on the wall.", bottles); + * ``` + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_PRILLX SDL_PRILL_PREFIX "X" +#endif /* SDL_WIKI_DOCUMENTATION_SECTION */ + /* Make sure we have macros for printing width-based integers. * should define these but this is not true all platforms. * (for example win32) */ #ifndef SDL_PRIs64 #if defined(SDL_PLATFORM_WINDOWS) #define SDL_PRIs64 "I64d" -#elif defined(PRIs64) -#define SDL_PRIs64 PRIs64 +#elif defined(PRId64) +#define SDL_PRIs64 PRId64 #elif defined(__LP64__) && !defined(SDL_PLATFORM_APPLE) && !defined(__EMSCRIPTEN__) #define SDL_PRIs64 "ld" #else @@ -530,7 +753,295 @@ SDL_COMPILE_TIME_ASSERT(longlong_size64, sizeof(long long) == 8); /* using I64 f #endif /* Annotations to help code analysis tools */ -#ifdef SDL_DISABLE_ANALYZE_MACROS +#ifdef SDL_WIKI_DOCUMENTATION_SECTION + +/** + * Macro that annotates function params with input buffer size. + * + * If we were to annotate `memcpy`: + * + * ```c + * void *memcpy(void *dst, SDL_IN_BYTECAP(len) const void *src, size_t len); + * ``` + * + * This notes that `src` should be `len` bytes in size and is only read by the + * function. The compiler or other analysis tools can warn when this doesn't + * appear to be the case. + * + * On compilers without this annotation mechanism, this is defined to nothing. + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_IN_BYTECAP(x) _In_bytecount_(x) + +/** + * Macro that annotates function params with input/output string buffer size. + * + * If we were to annotate `strlcat`: + * + * ```c + * size_t strlcat(SDL_INOUT_Z_CAP(maxlen) char *dst, const char *src, size_t maxlen); + * ``` + * + * This notes that `dst` is a null-terminated C string, should be `maxlen` + * bytes in size, and is both read from and written to by the function. The + * compiler or other analysis tools can warn when this doesn't appear to be + * the case. + * + * On compilers without this annotation mechanism, this is defined to nothing. + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_INOUT_Z_CAP(x) _Inout_z_cap_(x) + +/** + * Macro that annotates function params with output string buffer size. + * + * If we were to annotate `snprintf`: + * + * ```c + * int snprintf(SDL_OUT_Z_CAP(maxlen) char *text, size_t maxlen, const char *fmt, ...); + * ``` + * + * This notes that `text` is a null-terminated C string, should be `maxlen` + * bytes in size, and is only written to by the function. The compiler or + * other analysis tools can warn when this doesn't appear to be the case. + * + * On compilers without this annotation mechanism, this is defined to nothing. + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_OUT_Z_CAP(x) _Out_z_cap_(x) + +/** + * Macro that annotates function params with output buffer size. + * + * If we were to annotate `wcsncpy`: + * + * ```c + * char *wcscpy(SDL_OUT_CAP(bufsize) wchar_t *dst, const wchar_t *src, size_t bufsize); + * ``` + * + * This notes that `dst` should have a capacity of `bufsize` wchar_t in size, + * and is only written to by the function. The compiler or other analysis + * tools can warn when this doesn't appear to be the case. + * + * This operates on counts of objects, not bytes. Use SDL_OUT_BYTECAP for + * bytes. + * + * On compilers without this annotation mechanism, this is defined to nothing. + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_OUT_CAP(x) _Out_cap_(x) + +/** + * Macro that annotates function params with output buffer size. + * + * If we were to annotate `memcpy`: + * + * ```c + * void *memcpy(SDL_OUT_BYTECAP(bufsize) void *dst, const void *src, size_t bufsize); + * ``` + * + * This notes that `dst` should have a capacity of `bufsize` bytes in size, + * and is only written to by the function. The compiler or other analysis + * tools can warn when this doesn't appear to be the case. + * + * On compilers without this annotation mechanism, this is defined to nothing. + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_OUT_BYTECAP(x) _Out_bytecap_(x) + +/** + * Macro that annotates function params with output buffer string size. + * + * If we were to annotate `strcpy`: + * + * ```c + * char *strcpy(SDL_OUT_Z_BYTECAP(bufsize) char *dst, const char *src, size_t bufsize); + * ``` + * + * This notes that `dst` should have a capacity of `bufsize` bytes in size, + * and a zero-terminated string is written to it by the function. The compiler + * or other analysis tools can warn when this doesn't appear to be the case. + * + * On compilers without this annotation mechanism, this is defined to nothing. + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_OUT_Z_BYTECAP(x) _Out_z_bytecap_(x) + +/** + * Macro that annotates function params as printf-style format strings. + * + * If we were to annotate `fprintf`: + * + * ```c + * int fprintf(FILE *f, SDL_PRINTF_FORMAT_STRING const char *fmt, ...); + * ``` + * + * This notes that `fmt` should be a printf-style format string. The compiler + * or other analysis tools can warn when this doesn't appear to be the case. + * + * On compilers without this annotation mechanism, this is defined to nothing. + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_PRINTF_FORMAT_STRING _Printf_format_string_ + +/** + * Macro that annotates function params as scanf-style format strings. + * + * If we were to annotate `fscanf`: + * + * ```c + * int fscanf(FILE *f, SDL_SCANF_FORMAT_STRING const char *fmt, ...); + * ``` + * + * This notes that `fmt` should be a scanf-style format string. The compiler + * or other analysis tools can warn when this doesn't appear to be the case. + * + * On compilers without this annotation mechanism, this is defined to nothing. + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_SCANF_FORMAT_STRING _Scanf_format_string_impl_ + +/** + * Macro that annotates a vararg function that operates like printf. + * + * If we were to annotate `fprintf`: + * + * ```c + * int fprintf(FILE *f, const char *fmt, ...) SDL_PRINTF_VARARG_FUNC(2); + * ``` + * + * This notes that the second parameter should be a printf-style format + * string, followed by `...`. The compiler or other analysis tools can warn + * when this doesn't appear to be the case. + * + * On compilers without this annotation mechanism, this is defined to nothing. + * + * This can (and should) be used with SDL_PRINTF_FORMAT_STRING as well, which + * between them will cover at least Visual Studio, GCC, and Clang. + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_PRINTF_VARARG_FUNC( fmtargnumber ) __attribute__ (( format( __printf__, fmtargnumber, fmtargnumber+1 ))) + +/** + * Macro that annotates a va_list function that operates like printf. + * + * If we were to annotate `vfprintf`: + * + * ```c + * int vfprintf(FILE *f, const char *fmt, va_list ap) SDL_PRINTF_VARARG_FUNCV(2); + * ``` + * + * This notes that the second parameter should be a printf-style format + * string, followed by a va_list. The compiler or other analysis tools can + * warn when this doesn't appear to be the case. + * + * On compilers without this annotation mechanism, this is defined to nothing. + * + * This can (and should) be used with SDL_PRINTF_FORMAT_STRING as well, which + * between them will cover at least Visual Studio, GCC, and Clang. + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_PRINTF_VARARG_FUNCV( fmtargnumber ) __attribute__(( format( __printf__, fmtargnumber, 0 ))) + +/** + * Macro that annotates a vararg function that operates like scanf. + * + * If we were to annotate `fscanf`: + * + * ```c + * int fscanf(FILE *f, const char *fmt, ...) SDL_PRINTF_VARARG_FUNCV(2); + * ``` + * + * This notes that the second parameter should be a scanf-style format string, + * followed by `...`. The compiler or other analysis tools can warn when this + * doesn't appear to be the case. + * + * On compilers without this annotation mechanism, this is defined to nothing. + * + * This can (and should) be used with SDL_SCANF_FORMAT_STRING as well, which + * between them will cover at least Visual Studio, GCC, and Clang. + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_SCANF_VARARG_FUNC( fmtargnumber ) __attribute__ (( format( __scanf__, fmtargnumber, fmtargnumber+1 ))) + +/** + * Macro that annotates a va_list function that operates like scanf. + * + * If we were to annotate `vfscanf`: + * + * ```c + * int vfscanf(FILE *f, const char *fmt, va_list ap) SDL_PRINTF_VARARG_FUNCV(2); + * ``` + * + * This notes that the second parameter should be a scanf-style format string, + * followed by a va_list. The compiler or other analysis tools can warn when + * this doesn't appear to be the case. + * + * On compilers without this annotation mechanism, this is defined to nothing. + * + * This can (and should) be used with SDL_SCANF_FORMAT_STRING as well, which + * between them will cover at least Visual Studio, GCC, and Clang. + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_SCANF_VARARG_FUNCV( fmtargnumber ) __attribute__(( format( __scanf__, fmtargnumber, 0 ))) + +/** + * Macro that annotates a vararg function that operates like wprintf. + * + * If we were to annotate `fwprintf`: + * + * ```c + * int fwprintf(FILE *f, const wchar_t *fmt, ...) SDL_WPRINTF_VARARG_FUNC(2); + * ``` + * + * This notes that the second parameter should be a wprintf-style format wide + * string, followed by `...`. The compiler or other analysis tools can warn + * when this doesn't appear to be the case. + * + * On compilers without this annotation mechanism, this is defined to nothing. + * + * This can (and should) be used with SDL_PRINTF_FORMAT_STRING as well, which + * between them will cover at least Visual Studio, GCC, and Clang. + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_WPRINTF_VARARG_FUNC( fmtargnumber ) /* __attribute__ (( format( __wprintf__, fmtargnumber, fmtargnumber+1 ))) */ + +/** + * Macro that annotates a va_list function that operates like wprintf. + * + * If we were to annotate `vfwprintf`: + * + * ```c + * int vfwprintf(FILE *f, const wchar_t *fmt, va_list ap) SDL_WPRINTF_VARARG_FUNC(2); + * ``` + * + * This notes that the second parameter should be a wprintf-style format wide + * string, followed by a va_list. The compiler or other analysis tools can + * warn when this doesn't appear to be the case. + * + * On compilers without this annotation mechanism, this is defined to nothing. + * + * This can (and should) be used with SDL_PRINTF_FORMAT_STRING as well, which + * between them will cover at least Visual Studio, GCC, and Clang. + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_WPRINTF_VARARG_FUNCV( fmtargnumber ) /* __attribute__ (( format( __wprintf__, fmtargnumber, 0 ))) */ + +#elif defined(SDL_DISABLE_ANALYZE_MACROS) #define SDL_IN_BYTECAP(x) #define SDL_INOUT_Z_CAP(x) #define SDL_OUT_Z_CAP(x) @@ -679,7 +1190,51 @@ extern "C" { } while (0) -#ifndef SDL_DISABLE_ALLOCA +#ifdef SDL_WIKI_DOCUMENTATION_SECTION + +/** + * Allocate memory on the stack (maybe). + * + * If SDL knows how to access alloca() on the current platform, it will use it + * to stack-allocate memory here. If it doesn't, it will use SDL_malloc() to + * heap-allocate memory. + * + * Since this might not be stack memory at all, it's important that you check + * the returned pointer for NULL, and that you call SDL_stack_free on the + * memory when done with it. Since this might be stack memory, it's important + * that you don't allocate large amounts of it, or allocate in a loop without + * returning from the function, so the stack doesn't overflow. + * + * \param type the datatype of the memory to allocate. + * \param count the number of `type` objects to allocate. + * \returns newly-allocated memory, or NULL on failure. + * + * \threadsafety It is safe to call this macro from any thread. + * + * \since This macro is available since SDL 3.1.3. + * + * \sa SDL_stack_free + */ +#define SDL_stack_alloc(type, count) (type*)alloca(sizeof(type)*(count)) + +/** + * Free memory previously allocated with SDL_stack_alloc. + * + * If SDL used alloca() to allocate this memory, this macro does nothing and + * the allocated memory will be automatically released when the function that + * called SDL_stack_alloc() returns. If SDL used SDL_malloc(), it will + * SDL_free the memory immediately. + * + * \param data the pointer, from SDL_stack_alloc(), to free. + * + * \threadsafety It is safe to call this macro from any thread. + * + * \since This macro is available since SDL 3.1.3. + * + * \sa SDL_stack_alloc + */ +#define SDL_stack_free(data) +#elif !defined(SDL_DISABLE_ALLOCA) #define SDL_stack_alloc(type, count) (type*)alloca(sizeof(type)*(count)) #define SDL_stack_free(data) #else @@ -987,7 +1542,8 @@ extern SDL_DECLSPEC void SDLCALL SDL_aligned_free(void *mem); /** * Get the number of outstanding (unfreed) allocations. * - * \returns the number of allocations. + * \returns the number of allocations or -1 if allocation counting is + * disabled. * * \threadsafety It is safe to call this function from any thread. * @@ -1462,11 +2018,76 @@ extern SDL_DECLSPEC void SDLCALL SDL_qsort_r(void *base, size_t nmemb, size_t si */ extern SDL_DECLSPEC void * SDLCALL SDL_bsearch_r(const void *key, const void *base, size_t nmemb, size_t size, SDL_CompareCallback_r compare, void *userdata); +/** + * Compute the absolute value of `x`. + * + * \param x an integer value. + * \returns the absolute value of x. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.1.3. + */ extern SDL_DECLSPEC int SDLCALL SDL_abs(int x); -/* NOTE: these double-evaluate their arguments, so you should never have side effects in the parameters */ +/** + * Return the lesser of two values. + * + * This is a helper macro that might be more clear than writing out the + * comparisons directly, and works with any type that can be compared with the + * `<` operator. However, it double-evaluates both its parameters, so do not + * use expressions with side-effects here. + * + * \param x the first value to compare. + * \param y the second value to compare. + * \returns the lesser of `x` and `y`. + * + * \threadsafety It is safe to call this macro from any thread. + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_min(x, y) (((x) < (y)) ? (x) : (y)) + +/** + * Return the greater of two values. + * + * This is a helper macro that might be more clear than writing out the + * comparisons directly, and works with any type that can be compared with the + * `>` operator. However, it double-evaluates both its parameters, so do not + * use expressions with side-effects here. + * + * \param x the first value to compare. + * \param y the second value to compare. + * \returns the lesser of `x` and `y`. + * + * \threadsafety It is safe to call this macro from any thread. + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_max(x, y) (((x) > (y)) ? (x) : (y)) + +/** + * Return a value clamped to a range. + * + * If `x` is outside the range a values between `a` and `b`, the returned + * value will be `a` or `b` as appropriate. Otherwise, `x` is returned. + * + * This macro will produce incorrect results if `b` is less than `a`. + * + * This is a helper macro that might be more clear than writing out the + * comparisons directly, and works with any type that can be compared with the + * `<` and `>` operators. However, it double-evaluates all its parameters, so + * do not use expressions with side-effects here. + * + * \param x the value to compare. + * \param a the low end value. + * \param b the high end value. + * \returns x, clamped between a and b. + * + * \threadsafety It is safe to call this macro from any thread. + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_clamp(x, a, b) (((x) < (a)) ? (a) : (((x) > (b)) ? (b) : (x))) /** @@ -1705,8 +2326,72 @@ extern SDL_DECLSPEC int SDLCALL SDL_toupper(int x); */ extern SDL_DECLSPEC int SDLCALL SDL_tolower(int x); +/** + * Calculate a CRC-16 value. + * + * https://en.wikipedia.org/wiki/Cyclic_redundancy_check + * + * This function can be called multiple times, to stream data to be + * checksummed in blocks. Each call must provide the previous CRC-16 return + * value to be updated with the next block. The first call to this function + * for a set of blocks should pass in a zero CRC value. + * + * \param crc the current checksum for this data set, or 0 for a new data set. + * \param data a new block of data to add to the checksum. + * \param len the size, in bytes, of the new block of data. + * \returns a CRC-16 checksum value of all blocks in the data set. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.1.3. + */ extern SDL_DECLSPEC Uint16 SDLCALL SDL_crc16(Uint16 crc, const void *data, size_t len); + +/** + * Calculate a CRC-32 value. + * + * https://en.wikipedia.org/wiki/Cyclic_redundancy_check + * + * This function can be called multiple times, to stream data to be + * checksummed in blocks. Each call must provide the previous CRC-32 return + * value to be updated with the next block. The first call to this function + * for a set of blocks should pass in a zero CRC value. + * + * \param crc the current checksum for this data set, or 0 for a new data set. + * \param data a new block of data to add to the checksum. + * \param len the size, in bytes, of the new block of data. + * \returns a CRC-32 checksum value of all blocks in the data set. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.1.3. + */ extern SDL_DECLSPEC Uint32 SDLCALL SDL_crc32(Uint32 crc, const void *data, size_t len); + +/** + * Calculate a 32-bit MurmurHash3 value for a block of data. + * + * https://en.wikipedia.org/wiki/MurmurHash + * + * A seed may be specified, which changes the final results consistently, but + * this does not work like SDL_crc16 and SDL_crc32: you can't feed a previous + * result from this function back into itself as the next seed value to + * calculate a hash in chunks; it won't produce the same hash as it would if + * the same data was provided in a single call. + * + * If you aren't sure what to provide for a seed, zero is fine. Murmur3 is not + * cryptographically secure, so it shouldn't be used for hashing top-secret + * data. + * + * \param data the data to be hashed. + * \param len the size of data, in bytes. + * \param seed a value that alters the final hash value. + * \returns a Murmur3 32-bit hash value. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.1.3. + */ extern SDL_DECLSPEC Uint32 SDLCALL SDL_murmur3_32(const void *data, size_t len, Uint32 seed); /** @@ -1737,12 +2422,37 @@ extern SDL_DECLSPEC void * SDLCALL SDL_memcpy(SDL_OUT_BYTECAP(len) void *dst, SD #define SDL_memcpy memcpy #endif + +/** + * A macro to copy memory between objects, with basic type checking. + * + * SDL_memcpy and SDL_memmove do not care where you copy memory to and from, + * which can lead to bugs. This macro aims to avoid most of those bugs by + * making sure that the source and destination are both pointers to objects + * that are the same size. It does not check that the objects are the same + * _type_, just that the copy will not overflow either object. + * + * The size check happens at compile time, and the compiler will throw an + * error if the objects are different sizes. + * + * Generally this is intended to copy a single object, not an array. + * + * This macro looks like it double-evaluates its parameters, but the extras + * them are in `sizeof` sections, which generate no code nor side-effects. + * + * \param dst a pointer to the destination object. Must not be NULL. + * \param src a pointer to the source object. Must not be NULL. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.1.3. + */ #define SDL_copyp(dst, src) \ { SDL_COMPILE_TIME_ASSERT(SDL_copyp, sizeof (*(dst)) == sizeof (*(src))); } \ SDL_memcpy((dst), (src), sizeof(*(src))) /** - * Copy memory. + * Copy memory ranges that might overlap. * * It is okay for the memory regions to overlap. If you are confident that the * regions never overlap, using SDL_memcpy() may improve performance. @@ -1768,7 +2478,44 @@ extern SDL_DECLSPEC void * SDLCALL SDL_memmove(SDL_OUT_BYTECAP(len) void *dst, S #define SDL_memmove memmove #endif +/** + * Initialize all bytes of buffer of memory to a specific value. + * + * This function will set `len` bytes, pointed to by `dst`, to the value + * specified in `c`. + * + * Despite `c` being an `int` instead of a `char`, this only operates on + * bytes; `c` must be a value between 0 and 255, inclusive. + * + * \param dst the destination memory region. Must not be NULL. + * \param c the byte value to set. + * \param len the length, in bytes, to set in `dst`. + * \returns `dst`. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.1.3. + */ extern SDL_DECLSPEC void * SDLCALL SDL_memset(SDL_OUT_BYTECAP(len) void *dst, int c, size_t len); + +/** + * Initialize all 32-bit words of buffer of memory to a specific value. + * + * This function will set a buffer of `dwords` Uint32 values, pointed to by + * `dst`, to the value specified in `val`. + * + * Unlike SDL_memset, this sets 32-bit values, not bytes, so it's not limited + * to a range of 0-255. + * + * \param dst the destination memory region. Must not be NULL. + * \param val the Uint32 value to set. + * \param dwords the number of Uint32 values to set in `dst`. + * \returns `dst`. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.1.3. + */ extern SDL_DECLSPEC void * SDLCALL SDL_memset4(void *dst, Uint32 val, size_t dwords); /* Take advantage of compiler optimizations for memset */ @@ -1779,13 +2526,136 @@ extern SDL_DECLSPEC void * SDLCALL SDL_memset4(void *dst, Uint32 val, size_t dwo #define SDL_memset memset #endif +/** + * Clear an object's memory to zero. + * + * This is wrapper over SDL_memset that handles calculating the object size, + * so there's no chance of copy/paste errors, and the code is cleaner. + * + * This requires an object, not a pointer to an object, nor an array. + * + * \param x the object to clear. + * + * \threadsafety It is safe to call this macro from any thread. + * + * \since This macro is available since SDL 3.1.3. + * + * \sa SDL_zerop + * \sa SDL_zeroa + */ #define SDL_zero(x) SDL_memset(&(x), 0, sizeof((x))) + +/** + * Clear an object's memory to zero, using a pointer. + * + * This is wrapper over SDL_memset that handles calculating the object size, + * so there's no chance of copy/paste errors, and the code is cleaner. + * + * This requires a pointer to an object, not an object itself, nor an array. + * + * \param x a pointer to the object to clear. + * + * \threadsafety It is safe to call this macro from any thread. + * + * \since This macro is available since SDL 3.1.3. + * + * \sa SDL_zero + * \sa SDL_zeroa + */ #define SDL_zerop(x) SDL_memset((x), 0, sizeof(*(x))) + +/** + * Clear an array's memory to zero. + * + * This is wrapper over SDL_memset that handles calculating the array size, so + * there's no chance of copy/paste errors, and the code is cleaner. + * + * This requires an array, not an object, nor a pointer to an object. + * + * \param x an array to clear. + * + * \threadsafety It is safe to call this macro from any thread. + * + * \since This macro is available since SDL 3.1.3. + * + * \sa SDL_zero + * \sa SDL_zeroa + */ #define SDL_zeroa(x) SDL_memset((x), 0, sizeof((x))) + +/** + * Compare two buffers of memory. + * + * \param s1 the first buffer to compare. NULL is not permitted! + * \param s2 the second buffer to compare. NULL is not permitted! + * \param len the number of bytes to compare between the buffers. + * \returns less than zero if s1 is "less than" s2, greater than zero if s1 is + * "greater than" s2, and zero if the buffers match exactly for `len` + * bytes. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.1.3. + */ extern SDL_DECLSPEC int SDLCALL SDL_memcmp(const void *s1, const void *s2, size_t len); +/** + * This works exactly like wcslen() but doesn't require access to a C runtime. + * + * Counts the number of wchar_t values in `wstr`, excluding the null + * terminator. + * + * Like SDL_strlen only counts bytes and not codepoints in a UTF-8 string, + * this counts wchar_t values in a string, even if the string's encoding is of + * variable width, like UTF-16. + * + * Also be aware that wchar_t is different sizes on different platforms (4 + * bytes on Linux, 2 on Windows, etc). + * + * \param wstr The null-terminated wide string to read. Must not be NULL. + * \returns the length (in wchar_t values, excluding the null terminator) of + * `wstr`. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.1.3. + * + * \sa SDL_wcsnlen + * \sa SDL_utf8strlen + * \sa SDL_utf8strnlen + */ extern SDL_DECLSPEC size_t SDLCALL SDL_wcslen(const wchar_t *wstr); + +/** + * This works exactly like wcsnlen() but doesn't require access to a C + * runtime. + * + * Counts up to a maximum of `maxlen` wchar_t values in `wstr`, excluding the + * null terminator. + * + * Like SDL_strnlen only counts bytes and not codepoints in a UTF-8 string, + * this counts wchar_t values in a string, even if the string's encoding is of + * variable width, like UTF-16. + * + * Also be aware that wchar_t is different sizes on different platforms (4 + * bytes on Linux, 2 on Windows, etc). + * + * Also, `maxlen` is a count of wide characters, not bytes! + * + * \param wstr The null-terminated wide string to read. Must not be NULL. + * \param maxlen The maximum amount of wide characters to count. + * \returns the length (in wide characters, excluding the null terminator) of + * `wstr` but never more than `maxlen`. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.1.3. + * + * \sa SDL_wcslen + * \sa SDL_utf8strlen + * \sa SDL_utf8strnlen + */ extern SDL_DECLSPEC size_t SDLCALL SDL_wcsnlen(const wchar_t *wstr, size_t maxlen); /** @@ -1804,7 +2674,7 @@ extern SDL_DECLSPEC size_t SDLCALL SDL_wcsnlen(const wchar_t *wstr, size_t maxle * \param src The null-terminated wide string to copy. Must not be NULL, and * must not overlap with `dst`. * \param maxlen The length (in wide characters) of the destination buffer. - * \returns The length (in wide characters, excluding the null terminator) of + * \returns the length (in wide characters, excluding the null terminator) of * `src`. * * \threadsafety It is safe to call this function from any thread. @@ -1833,7 +2703,7 @@ extern SDL_DECLSPEC size_t SDLCALL SDL_wcslcpy(SDL_OUT_Z_CAP(maxlen) wchar_t *ds * \param src The second null-terminated wide string. Must not be NULL, and * must not overlap with `dst`. * \param maxlen The length (in wide characters) of the destination buffer. - * \returns The length (in wide characters, excluding the null terminator) of + * \returns the length (in wide characters, excluding the null terminator) of * the string in `dst` plus the length of `src`. * * \threadsafety It is safe to call this function from any thread. @@ -1844,8 +2714,67 @@ extern SDL_DECLSPEC size_t SDLCALL SDL_wcslcpy(SDL_OUT_Z_CAP(maxlen) wchar_t *ds */ extern SDL_DECLSPEC size_t SDLCALL SDL_wcslcat(SDL_INOUT_Z_CAP(maxlen) wchar_t *dst, const wchar_t *src, size_t maxlen); +/** + * Allocate a copy of a wide string. + * + * This allocates enough space for a null-terminated copy of `wstr`, using + * SDL_malloc, and then makes a copy of the string into this space. + * + * The returned string is owned by the caller, and should be passed to + * SDL_free when no longer needed. + * + * \param wstr the string to copy. + * \returns a pointer to the newly-allocated wide string. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.1.3. + */ extern SDL_DECLSPEC wchar_t * SDLCALL SDL_wcsdup(const wchar_t *wstr); + +/** + * Search a wide string for the first instance of a specific substring. + * + * The search ends once it finds the requested substring, or a null terminator + * byte to end the string. + * + * Note that this looks for strings of _wide characters_, not _codepoints_, so + * it's legal to search for malformed and incomplete UTF-16 sequences. + * + * \param haystack the wide string to search. Must not be NULL. + * \param needle the wide string to search for. Must not be NULL. + * \returns a pointer to the first instance of `needle` in the string, or NULL + * if not found. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.1.3. + */ extern SDL_DECLSPEC wchar_t * SDLCALL SDL_wcsstr(const wchar_t *haystack, const wchar_t *needle); + +/** + * Search a wide string, up to n wide chars, for the first instance of a + * specific substring. + * + * The search ends once it finds the requested substring, or a null terminator + * value to end the string, or `maxlen` wide character have been examined. It + * is possible to use this function on a wide string without a null + * terminator. + * + * Note that this looks for strings of _wide characters_, not _codepoints_, so + * it's legal to search for malformed and incomplete UTF-16 sequences. + * + * \param haystack the wide string to search. Must not be NULL. + * \param needle the wide string to search for. Must not be NULL. + * \param maxlen the maximum number of wide characters to search in + * `haystack`. + * \returns a pointer to the first instance of `needle` in the string, or NULL + * if not found. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.1.3. + */ extern SDL_DECLSPEC wchar_t * SDLCALL SDL_wcsnstr(const wchar_t *haystack, const wchar_t *needle, size_t maxlen); /** @@ -1987,7 +2916,7 @@ extern SDL_DECLSPEC int SDLCALL SDL_wcsncasecmp(const wchar_t *str1, const wchar * to 36 inclusive. If 0, the base will be inferred from the * number's prefix (0x for hexadecimal, 0 for octal, decimal * otherwise). - * \returns The parsed `long`, or 0 if no number could be parsed. + * \returns the parsed `long`, or 0 if no number could be parsed. * * \threadsafety It is safe to call this function from any thread. * @@ -1997,7 +2926,48 @@ extern SDL_DECLSPEC int SDLCALL SDL_wcsncasecmp(const wchar_t *str1, const wchar */ extern SDL_DECLSPEC long SDLCALL SDL_wcstol(const wchar_t *str, wchar_t **endp, int base); +/** + * This works exactly like strlen() but doesn't require access to a C runtime. + * + * Counts the bytes in `str`, excluding the null terminator. + * + * If you need the length of a UTF-8 string, consider using SDL_utf8strlen(). + * + * \param str The null-terminated string to read. Must not be NULL. + * \returns the length (in bytes, excluding the null terminator) of `src`. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.1.3. + * + * \sa SDL_strnlen + * \sa SDL_utf8strlen + * \sa SDL_utf8strnlen + */ extern SDL_DECLSPEC size_t SDLCALL SDL_strlen(const char *str); + +/** + * This works exactly like strnlen() but doesn't require access to a C + * runtime. + * + * Counts up to a maximum of `maxlen` bytes in `str`, excluding the null + * terminator. + * + * If you need the length of a UTF-8 string, consider using SDL_utf8strnlen(). + * + * \param str The null-terminated string to read. Must not be NULL. + * \param maxlen The maximum amount of bytes to count. + * \returns the length (in bytes, excluding the null terminator) of `src` but + * never more than `maxlen`. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.1.3. + * + * \sa SDL_strlen + * \sa SDL_utf8strlen + * \sa SDL_utf8strnlen + */ extern SDL_DECLSPEC size_t SDLCALL SDL_strnlen(const char *str, size_t maxlen); /** @@ -2017,7 +2987,7 @@ extern SDL_DECLSPEC size_t SDLCALL SDL_strnlen(const char *str, size_t maxlen); * \param src The null-terminated string to copy. Must not be NULL, and must * not overlap with `dst`. * \param maxlen The length (in characters) of the destination buffer. - * \returns The length (in characters, excluding the null terminator) of + * \returns the length (in characters, excluding the null terminator) of * `src`. * * \threadsafety It is safe to call this function from any thread. @@ -2047,7 +3017,7 @@ extern SDL_DECLSPEC size_t SDLCALL SDL_strlcpy(SDL_OUT_Z_CAP(maxlen) char *dst, * must not overlap with `dst`. * \param dst_bytes The length (in bytes) of the destination buffer. Must not * be 0. - * \returns The number of bytes written, excluding the null terminator. + * \returns the number of bytes written, excluding the null terminator. * * \threadsafety It is safe to call this function from any thread. * @@ -2074,7 +3044,7 @@ extern SDL_DECLSPEC size_t SDLCALL SDL_utf8strlcpy(SDL_OUT_Z_CAP(dst_bytes) char * \param src The second null-terminated string. Must not be NULL, and must * not overlap with `dst`. * \param maxlen The length (in characters) of the destination buffer. - * \returns The length (in characters, excluding the null terminator) of the + * \returns the length (in characters, excluding the null terminator) of the * string in `dst` plus the length of `src`. * * \threadsafety It is safe to call this function from any thread. @@ -2085,8 +3055,68 @@ extern SDL_DECLSPEC size_t SDLCALL SDL_utf8strlcpy(SDL_OUT_Z_CAP(dst_bytes) char */ extern SDL_DECLSPEC size_t SDLCALL SDL_strlcat(SDL_INOUT_Z_CAP(maxlen) char *dst, const char *src, size_t maxlen); +/** + * Allocate a copy of a string. + * + * This allocates enough space for a null-terminated copy of `str`, using + * SDL_malloc, and then makes a copy of the string into this space. + * + * The returned string is owned by the caller, and should be passed to + * SDL_free when no longer needed. + * + * \param str the string to copy. + * \returns a pointer to the newly-allocated string. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.1.3. + */ extern SDL_DECLSPEC SDL_MALLOC char * SDLCALL SDL_strdup(const char *str); + +/** + * Allocate a copy of a string, up to n characters. + * + * This allocates enough space for a null-terminated copy of `str`, up to + * `maxlen` bytes, using SDL_malloc, and then makes a copy of the string into + * this space. + * + * If the string is longer than `maxlen` bytes, the returned string will be + * `maxlen` bytes long, plus a null-terminator character that isn't included + * in the count. + * + * The returned string is owned by the caller, and should be passed to + * SDL_free when no longer needed. + * + * \param str the string to copy. + * \param maxlen the maximum length of the copied string, not counting the + * null-terminator character. + * \returns a pointer to the newly-allocated string. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.1.3. + */ extern SDL_DECLSPEC SDL_MALLOC char * SDLCALL SDL_strndup(const char *str, size_t maxlen); + +/** + * Reverse a string's contents. + * + * This reverses a null-terminated string in-place. Only the content of the + * string is reversed; the null-terminator character remains at the end of the + * reversed string. + * + * **WARNING**: This function reverses the _bytes_ of the string, not the + * codepoints. If `str` is a UTF-8 string with Unicode codepoints > 127, this + * will ruin the string data. You should only use this function on strings + * that are completely comprised of low ASCII characters. + * + * \param str the string to reverse. + * \returns `str`. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.1.3. + */ extern SDL_DECLSPEC char * SDLCALL SDL_strrev(char *str); /** @@ -2131,20 +3161,372 @@ extern SDL_DECLSPEC char * SDLCALL SDL_strupr(char *str); */ extern SDL_DECLSPEC char * SDLCALL SDL_strlwr(char *str); +/** + * Search a string for the first instance of a specific byte. + * + * The search ends once it finds the requested byte value, or a null + * terminator byte to end the string. + * + * Note that this looks for _bytes_, not _characters_, so you cannot match + * against a Unicode codepoint > 255, regardless of character encoding. + * + * \param str the string to search. Must not be NULL. + * \param c the byte value to search for. + * \returns a pointer to the first instance of `c` in the string, or NULL if + * not found. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.1.3. + */ extern SDL_DECLSPEC char * SDLCALL SDL_strchr(const char *str, int c); + +/** + * Search a string for the last instance of a specific byte. + * + * The search must go until it finds a null terminator byte to end the string. + * + * Note that this looks for _bytes_, not _characters_, so you cannot match + * against a Unicode codepoint > 255, regardless of character encoding. + * + * \param str the string to search. Must not be NULL. + * \param c the byte value to search for. + * \returns a pointer to the last instance of `c` in the string, or NULL if + * not found. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.1.3. + */ extern SDL_DECLSPEC char * SDLCALL SDL_strrchr(const char *str, int c); + +/** + * Search a string for the first instance of a specific substring. + * + * The search ends once it finds the requested substring, or a null terminator + * byte to end the string. + * + * Note that this looks for strings of _bytes_, not _characters_, so it's + * legal to search for malformed and incomplete UTF-8 sequences. + * + * \param haystack the string to search. Must not be NULL. + * \param needle the string to search for. Must not be NULL. + * \returns a pointer to the first instance of `needle` in the string, or NULL + * if not found. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.1.3. + */ extern SDL_DECLSPEC char * SDLCALL SDL_strstr(const char *haystack, const char *needle); + +/** + * Search a string, up to n bytes, for the first instance of a specific + * substring. + * + * The search ends once it finds the requested substring, or a null terminator + * byte to end the string, or `maxlen` bytes have been examined. It is + * possible to use this function on a string without a null terminator. + * + * Note that this looks for strings of _bytes_, not _characters_, so it's + * legal to search for malformed and incomplete UTF-8 sequences. + * + * \param haystack the string to search. Must not be NULL. + * \param needle the string to search for. Must not be NULL. + * \param maxlen the maximum number of bytes to search in `haystack`. + * \returns a pointer to the first instance of `needle` in the string, or NULL + * if not found. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.1.3. + */ extern SDL_DECLSPEC char * SDLCALL SDL_strnstr(const char *haystack, const char *needle, size_t maxlen); + +/** + * Search a UTF-8 string for the first instance of a specific substring, + * case-insensitively. + * + * This will work with Unicode strings, using a technique called + * "case-folding" to handle the vast majority of case-sensitive human + * languages regardless of system locale. It can deal with expanding values: a + * German Eszett character can compare against two ASCII 's' chars and be + * considered a match, for example. A notable exception: it does not handle + * the Turkish 'i' character; human language is complicated! + * + * Since this handles Unicode, it expects the strings to be well-formed UTF-8 + * and not a null-terminated string of arbitrary bytes. Bytes that are not + * valid UTF-8 are treated as Unicode character U+FFFD (REPLACEMENT + * CHARACTER), which is to say two strings of random bits may turn out to + * match if they convert to the same amount of replacement characters. + * + * \param haystack the string to search. Must not be NULL. + * \param needle the string to search for. Must not be NULL. + * \returns a pointer to the first instance of `needle` in the string, or NULL + * if not found. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.1.3. + */ extern SDL_DECLSPEC char * SDLCALL SDL_strcasestr(const char *haystack, const char *needle); -extern SDL_DECLSPEC char * SDLCALL SDL_strtok_r(char *s1, const char *s2, char **saveptr); + +/** + * This works exactly like strtok_r() but doesn't require access to a C + * runtime. + * + * Break a string up into a series of tokens. + * + * To start tokenizing a new string, `str` should be the non-NULL address of + * the string to start tokenizing. Future calls to get the next token from the + * same string should specify a NULL. + * + * Note that this function will overwrite pieces of `str` with null chars to + * split it into tokens. This function cannot be used with const/read-only + * strings! + * + * `saveptr` just needs to point to a `char *` that can be overwritten; SDL + * will use this to save tokenizing state between calls. It is initialized if + * `str` is non-NULL, and used to resume tokenizing when `str` is NULL. + * + * \param str the string to tokenize, or NULL to continue tokenizing. + * \param delim the delimiter string that separates tokens. + * \param saveptr pointer to a char *, used for ongoing state. + * \returns A pointer to the next token, or NULL if no tokens remain. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.1.3. + */ +extern SDL_DECLSPEC char * SDLCALL SDL_strtok_r(char *str, const char *delim, char **saveptr); + +/** + * Count the number of codepoints in a UTF-8 string. + * + * Counts the _codepoints_, not _bytes_, in `str`, excluding the null + * terminator. + * + * If you need to count the bytes in a string instead, consider using + * SDL_strlen(). + * + * Since this handles Unicode, it expects the strings to be well-formed UTF-8 + * and not a null-terminated string of arbitrary bytes. Bytes that are not + * valid UTF-8 are treated as Unicode character U+FFFD (REPLACEMENT + * CHARACTER), so a malformed or incomplete UTF-8 sequence might increase the + * count by several replacement characters. + * + * \param str The null-terminated UTF-8 string to read. Must not be NULL. + * \returns The length (in codepoints, excluding the null terminator) of + * `src`. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.1.3. + * + * \sa SDL_utf8strnlen + * \sa SDL_strlen + */ extern SDL_DECLSPEC size_t SDLCALL SDL_utf8strlen(const char *str); + +/** + * Count the number of codepoints in a UTF-8 string, up to n bytes. + * + * Counts the _codepoints_, not _bytes_, in `str`, excluding the null + * terminator. + * + * If you need to count the bytes in a string instead, consider using + * SDL_strnlen(). + * + * The counting stops at `bytes` bytes (not codepoints!). This seems + * counterintuitive, but makes it easy to express the total size of the + * string's buffer. + * + * Since this handles Unicode, it expects the strings to be well-formed UTF-8 + * and not a null-terminated string of arbitrary bytes. Bytes that are not + * valid UTF-8 are treated as Unicode character U+FFFD (REPLACEMENT + * CHARACTER), so a malformed or incomplete UTF-8 sequence might increase the + * count by several replacement characters. + * + * \param str The null-terminated UTF-8 string to read. Must not be NULL. + * \param bytes The maximum amount of bytes to count. + * \returns The length (in codepoints, excluding the null terminator) of `src` + * but never more than `maxlen`. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.1.3. + * + * \sa SDL_utf8strlen + * \sa SDL_strnlen + */ extern SDL_DECLSPEC size_t SDLCALL SDL_utf8strnlen(const char *str, size_t bytes); +/** + * Convert an integer into a string. + * + * This requires a radix to specified for string format. Specifying 10 + * produces a decimal number, 16 hexidecimal, etc. Must be in the range of 2 + * to 36. + * + * Note that this function will overflow a buffer if `str` is not large enough + * to hold the output! It may be safer to use SDL_snprintf to clamp output, or + * SDL_asprintf to allocate a buffer. Otherwise, it doesn't hurt to allocate + * much more space than you expect to use (and don't forget possible negative + * signs, null terminator bytes, etc). + * + * \param value the integer to convert. + * \param str the buffer to write the string into. + * \param radix the radix to use for string generation. + * \returns `str`. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.1.3. + * + * \sa SDL_uitoa + * \sa SDL_ltoa + * \sa SDL_lltoa + */ extern SDL_DECLSPEC char * SDLCALL SDL_itoa(int value, char *str, int radix); + +/** + * Convert an unsigned integer into a string. + * + * This requires a radix to specified for string format. Specifying 10 + * produces a decimal number, 16 hexidecimal, etc. Must be in the range of 2 + * to 36. + * + * Note that this function will overflow a buffer if `str` is not large enough + * to hold the output! It may be safer to use SDL_snprintf to clamp output, or + * SDL_asprintf to allocate a buffer. Otherwise, it doesn't hurt to allocate + * much more space than you expect to use (and don't forget null terminator + * bytes, etc). + * + * \param value the unsigned integer to convert. + * \param str the buffer to write the string into. + * \param radix the radix to use for string generation. + * \returns `str`. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.1.3. + * + * \sa SDL_itoa + * \sa SDL_ultoa + * \sa SDL_ulltoa + */ extern SDL_DECLSPEC char * SDLCALL SDL_uitoa(unsigned int value, char *str, int radix); + +/** + * Convert a long integer into a string. + * + * This requires a radix to specified for string format. Specifying 10 + * produces a decimal number, 16 hexidecimal, etc. Must be in the range of 2 + * to 36. + * + * Note that this function will overflow a buffer if `str` is not large enough + * to hold the output! It may be safer to use SDL_snprintf to clamp output, or + * SDL_asprintf to allocate a buffer. Otherwise, it doesn't hurt to allocate + * much more space than you expect to use (and don't forget possible negative + * signs, null terminator bytes, etc). + * + * \param value the long integer to convert. + * \param str the buffer to write the string into. + * \param radix the radix to use for string generation. + * \returns `str`. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.1.3. + * + * \sa SDL_ultoa + * \sa SDL_itoa + * \sa SDL_lltoa + */ extern SDL_DECLSPEC char * SDLCALL SDL_ltoa(long value, char *str, int radix); + +/** + * Convert an unsigned long integer into a string. + * + * This requires a radix to specified for string format. Specifying 10 + * produces a decimal number, 16 hexidecimal, etc. Must be in the range of 2 + * to 36. + * + * Note that this function will overflow a buffer if `str` is not large enough + * to hold the output! It may be safer to use SDL_snprintf to clamp output, or + * SDL_asprintf to allocate a buffer. Otherwise, it doesn't hurt to allocate + * much more space than you expect to use (and don't forget null terminator + * bytes, etc). + * + * \param value the unsigned long integer to convert. + * \param str the buffer to write the string into. + * \param radix the radix to use for string generation. + * \returns `str`. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.1.3. + * + * \sa SDL_ltoa + * \sa SDL_uitoa + * \sa SDL_ulltoa + */ extern SDL_DECLSPEC char * SDLCALL SDL_ultoa(unsigned long value, char *str, int radix); + +/** + * Convert a long long integer into a string. + * + * This requires a radix to specified for string format. Specifying 10 + * produces a decimal number, 16 hexidecimal, etc. Must be in the range of 2 + * to 36. + * + * Note that this function will overflow a buffer if `str` is not large enough + * to hold the output! It may be safer to use SDL_snprintf to clamp output, or + * SDL_asprintf to allocate a buffer. Otherwise, it doesn't hurt to allocate + * much more space than you expect to use (and don't forget possible negative + * signs, null terminator bytes, etc). + * + * \param value the long long integer to convert. + * \param str the buffer to write the string into. + * \param radix the radix to use for string generation. + * \returns `str`. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.1.3. + * + * \sa SDL_ulltoa + * \sa SDL_itoa + * \sa SDL_ltoa + */ extern SDL_DECLSPEC char * SDLCALL SDL_lltoa(long long value, char *str, int radix); + +/** + * Convert an unsigned long long integer into a string. + * + * This requires a radix to specified for string format. Specifying 10 + * produces a decimal number, 16 hexidecimal, etc. Must be in the range of 2 + * to 36. + * + * Note that this function will overflow a buffer if `str` is not large enough + * to hold the output! It may be safer to use SDL_snprintf to clamp output, or + * SDL_asprintf to allocate a buffer. Otherwise, it doesn't hurt to allocate + * much more space than you expect to use (and don't forget null terminator + * bytes, etc). + * + * \param value the unsigned long long integer to convert. + * \param str the buffer to write the string into. + * \param radix the radix to use for string generation. + * \returns `str`. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.1.3. + * + * \sa SDL_lltoa + * \sa SDL_uitoa + * \sa SDL_ultoa + */ extern SDL_DECLSPEC char * SDLCALL SDL_ulltoa(unsigned long long value, char *str, int radix); /** @@ -2154,7 +3536,7 @@ extern SDL_DECLSPEC char * SDLCALL SDL_ulltoa(unsigned long long value, char *st * `(int)SDL_strtol(str, NULL, 10)`. * * \param str The null-terminated string to read. Must not be NULL. - * \returns The parsed `int`. + * \returns the parsed `int`. * * \threadsafety It is safe to call this function from any thread. * @@ -2177,7 +3559,7 @@ extern SDL_DECLSPEC int SDLCALL SDL_atoi(const char *str); * NULL)`. * * \param str The null-terminated string to read. Must not be NULL. - * \returns The parsed `double`. + * \returns the parsed `double`. * * \threadsafety It is safe to call this function from any thread. * @@ -2209,7 +3591,7 @@ extern SDL_DECLSPEC double SDLCALL SDL_atof(const char *str); * to 36 inclusive. If 0, the base will be inferred from the * number's prefix (0x for hexadecimal, 0 for octal, decimal * otherwise). - * \returns The parsed `long`, or 0 if no number could be parsed. + * \returns the parsed `long`, or 0 if no number could be parsed. * * \threadsafety It is safe to call this function from any thread. * @@ -2243,7 +3625,7 @@ extern SDL_DECLSPEC long SDLCALL SDL_strtol(const char *str, char **endp, int ba * to 36 inclusive. If 0, the base will be inferred from the * number's prefix (0x for hexadecimal, 0 for octal, decimal * otherwise). - * \returns The parsed `unsigned long`, or 0 if no number could be parsed. + * \returns the parsed `unsigned long`, or 0 if no number could be parsed. * * \threadsafety It is safe to call this function from any thread. * @@ -2276,7 +3658,7 @@ extern SDL_DECLSPEC unsigned long SDLCALL SDL_strtoul(const char *str, char **en * to 36 inclusive. If 0, the base will be inferred from the * number's prefix (0x for hexadecimal, 0 for octal, decimal * otherwise). - * \returns The parsed `long long`, or 0 if no number could be parsed. + * \returns the parsed `long long`, or 0 if no number could be parsed. * * \threadsafety It is safe to call this function from any thread. * @@ -2309,7 +3691,7 @@ extern SDL_DECLSPEC long long SDLCALL SDL_strtoll(const char *str, char **endp, * to 36 inclusive. If 0, the base will be inferred from the * number's prefix (0x for hexadecimal, 0 for octal, decimal * otherwise). - * \returns The parsed `unsigned long long`, or 0 if no number could be + * \returns the parsed `unsigned long long`, or 0 if no number could be * parsed. * * \threadsafety It is safe to call this function from any thread. @@ -2336,11 +3718,11 @@ extern SDL_DECLSPEC unsigned long long SDLCALL SDL_strtoull(const char *str, cha * - Whether or not INF and NAN can be parsed is unspecified. * - The precision of the result is unspecified. * - * \param str The null-terminated string to read. Must not be NULL. - * \param endp If not NULL, the address of the first invalid character (i.e. + * \param str the null-terminated string to read. Must not be NULL. + * \param endp if not NULL, the address of the first invalid character (i.e. * the next character after the parsed number) will be written to * this pointer. - * \returns The parsed `double`, or 0 if no number could be parsed. + * \returns the parsed `double`, or 0 if no number could be parsed. * * \threadsafety It is safe to call this function from any thread. * @@ -2578,7 +3960,7 @@ extern SDL_DECLSPEC Uint32 SDLCALL SDL_StepUTF8(const char **pstr, size_t *pslen * * \threadsafety It is safe to call this function from any thread. * - * \since This function is available since SDL 3.2.0. + * \since This function is available since SDL 3.1.6. */ extern SDL_DECLSPEC Uint32 SDLCALL SDL_StepBackUTF8(const char *start, const char **pstr); @@ -2611,14 +3993,196 @@ extern SDL_DECLSPEC Uint32 SDLCALL SDL_StepBackUTF8(const char *start, const cha */ extern SDL_DECLSPEC char * SDLCALL SDL_UCS4ToUTF8(Uint32 codepoint, char *dst); - +/** + * This works exactly like sscanf() but doesn't require access to a C runtime. + * + * Scan a string, matching a format string, converting each '%' item and + * storing it to pointers provided through variable arguments. + * + * \param text the string to scan. Must not be NULL. + * \param fmt a printf-style format string. Must not be NULL. + * \param ... a list of pointers to values to be filled in with scanned items. + * \returns the number of items that matched the format string. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.1.3. + */ extern SDL_DECLSPEC int SDLCALL SDL_sscanf(const char *text, SDL_SCANF_FORMAT_STRING const char *fmt, ...) SDL_SCANF_VARARG_FUNC(2); + +/** + * This works exactly like vsscanf() but doesn't require access to a C + * runtime. + * + * Functions identically to SDL_sscanf(), except it takes a `va_list` instead + * of using `...` variable arguments. + * + * \param text the string to scan. Must not be NULL. + * \param fmt a printf-style format string. Must not be NULL. + * \param ap a `va_list` of pointers to values to be filled in with scanned + * items. + * \returns the number of items that matched the format string. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.1.3. + */ extern SDL_DECLSPEC int SDLCALL SDL_vsscanf(const char *text, SDL_SCANF_FORMAT_STRING const char *fmt, va_list ap) SDL_SCANF_VARARG_FUNCV(2); + +/** + * This works exactly like snprintf() but doesn't require access to a C + * runtime. + * + * Format a string of up to `maxlen`-1 bytes, converting each '%' item with + * values provided through variable arguments. + * + * While some C runtimes differ on how to deal with too-large strings, this + * function null-terminates the output, by treating the null-terminator as + * part of the `maxlen` count. Note that if `maxlen` is zero, however, no + * bytes will be written at all. + * + * This function returns the number of _bytes_ (not _characters_) that should + * be written, excluding the null-terminator character. If this returns a + * number >= `maxlen`, it means the output string was truncated. A negative + * return value means an error occurred. + * + * Referencing the output string's pointer with a format item is undefined + * behavior. + * + * \param text the buffer to write the string into. Must not be NULL. + * \param maxlen the maximum bytes to write, including the null-terminator. + * \param fmt a printf-style format string. Must not be NULL. + * \param ... a list of values to be used with the format string. + * \returns the number of bytes that should be written, not counting the + * null-terminator char, or a negative value on error. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.1.3. + */ extern SDL_DECLSPEC int SDLCALL SDL_snprintf(SDL_OUT_Z_CAP(maxlen) char *text, size_t maxlen, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) SDL_PRINTF_VARARG_FUNC(3); + +/** + * This works exactly like swprintf() but doesn't require access to a C + * runtime. + * + * Format a wide string of up to `maxlen`-1 wchar_t values, converting each + * '%' item with values provided through variable arguments. + * + * While some C runtimes differ on how to deal with too-large strings, this + * function null-terminates the output, by treating the null-terminator as + * part of the `maxlen` count. Note that if `maxlen` is zero, however, no wide + * characters will be written at all. + * + * This function returns the number of _wide characters_ (not _codepoints_) + * that should be written, excluding the null-terminator character. If this + * returns a number >= `maxlen`, it means the output string was truncated. A + * negative return value means an error occurred. + * + * Referencing the output string's pointer with a format item is undefined + * behavior. + * + * \param text the buffer to write the wide string into. Must not be NULL. + * \param maxlen the maximum wchar_t values to write, including the + * null-terminator. + * \param fmt a printf-style format string. Must not be NULL. + * \param ... a list of values to be used with the format string. + * \returns the number of wide characters that should be written, not counting + * the null-terminator char, or a negative value on error. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.1.3. + */ extern SDL_DECLSPEC int SDLCALL SDL_swprintf(SDL_OUT_Z_CAP(maxlen) wchar_t *text, size_t maxlen, SDL_PRINTF_FORMAT_STRING const wchar_t *fmt, ...) SDL_WPRINTF_VARARG_FUNC(3); + +/** + * This works exactly like vsnprintf() but doesn't require access to a C + * runtime. + * + * Functions identically to SDL_snprintf(), except it takes a `va_list` + * instead of using `...` variable arguments. + * + * \param text the buffer to write the string into. Must not be NULL. + * \param maxlen the maximum bytes to write, including the null-terminator. + * \param fmt a printf-style format string. Must not be NULL. + * \param ap a `va_list` values to be used with the format string. + * \returns the number of bytes that should be written, not counting the + * null-terminator char, or a negative value on error. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.1.3. + */ extern SDL_DECLSPEC int SDLCALL SDL_vsnprintf(SDL_OUT_Z_CAP(maxlen) char *text, size_t maxlen, SDL_PRINTF_FORMAT_STRING const char *fmt, va_list ap) SDL_PRINTF_VARARG_FUNCV(3); + +/** + * This works exactly like vswprintf() but doesn't require access to a C + * runtime. + * + * Functions identically to SDL_swprintf(), except it takes a `va_list` + * instead of using `...` variable arguments. + * + * \param text the buffer to write the string into. Must not be NULL. + * \param maxlen the maximum wide characters to write, including the + * null-terminator. + * \param fmt a printf-style format wide string. Must not be NULL. + * \param ap a `va_list` values to be used with the format string. + * \returns the number of wide characters that should be written, not counting + * the null-terminator char, or a negative value on error. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.1.3. + */ extern SDL_DECLSPEC int SDLCALL SDL_vswprintf(SDL_OUT_Z_CAP(maxlen) wchar_t *text, size_t maxlen, SDL_PRINTF_FORMAT_STRING const wchar_t *fmt, va_list ap) SDL_WPRINTF_VARARG_FUNCV(3); + +/** + * This works exactly like asprintf() but doesn't require access to a C + * runtime. + * + * Functions identically to SDL_snprintf(), except it allocates a buffer large + * enough to hold the output string on behalf of the caller. + * + * On success, this function returns the number of bytes (not characters) + * comprising the output string, not counting the null-terminator character, + * and sets `*strp` to the newly-allocated string. + * + * On error, this function returns a negative number, and the value of `*strp` + * is undefined. + * + * The returned string is owned by the caller, and should be passed to + * SDL_free when no longer needed. + * + * \param strp on output, is set to the new string. Must not be NULL. + * \param fmt a printf-style format string. Must not be NULL. + * \param ... a list of values to be used with the format string. + * \returns the number of bytes in the newly-allocated string, not counting + * the null-terminator char, or a negative value on error. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.1.3. + */ extern SDL_DECLSPEC int SDLCALL SDL_asprintf(char **strp, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) SDL_PRINTF_VARARG_FUNC(2); + +/** + * This works exactly like vasprintf() but doesn't require access to a C + * runtime. + * + * Functions identically to SDL_asprintf(), except it takes a `va_list` + * instead of using `...` variable arguments. + * + * \param strp on output, is set to the new string. Must not be NULL. + * \param fmt a printf-style format string. Must not be NULL. + * \param ap a `va_list` values to be used with the format string. + * \returns the number of bytes in the newly-allocated string, not counting + * the null-terminator char, or a negative value on error. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.1.3. + */ extern SDL_DECLSPEC int SDLCALL SDL_vasprintf(char **strp, SDL_PRINTF_FORMAT_STRING const char *fmt, va_list ap) SDL_PRINTF_VARARG_FUNCV(2); /** @@ -2808,11 +4372,27 @@ extern SDL_DECLSPEC float SDLCALL SDL_randf_r(Uint64 *state); */ extern SDL_DECLSPEC Uint32 SDLCALL SDL_rand_bits_r(Uint64 *state); - #ifndef SDL_PI_D + +/** + * The value of Pi, as a double-precision floating point literal. + * + * \since This macro is available since SDL 3.1.3. + * + * \sa SDL_PI_F + */ #define SDL_PI_D 3.141592653589793238462643383279502884 /**< pi (double) */ #endif + #ifndef SDL_PI_F + +/** + * The value of Pi, as a single-precision floating point literal. + * + * \since This macro is available since SDL 3.1.3. + * + * \sa SDL_PI_D + */ #define SDL_PI_F 3.141592653589793238462643383279502884F /**< pi (float) */ #endif @@ -3997,7 +5577,7 @@ extern SDL_DECLSPEC double SDLCALL SDL_sin(double x); * Range: `-1 <= y <= 1` * * This function operates on single-precision floating point values, use - * SDL_sinf for double-precision floats. + * SDL_sin for double-precision floats. * * This function may use a different approximation across different versions, * platforms and configurations. i.e, it can return a different value given @@ -4129,12 +5709,13 @@ extern SDL_DECLSPEC double SDLCALL SDL_tan(double x); */ extern SDL_DECLSPEC float SDLCALL SDL_tanf(float x); -/* The SDL implementation of iconv() returns these error codes */ -#define SDL_ICONV_ERROR (size_t)-1 -#define SDL_ICONV_E2BIG (size_t)-2 -#define SDL_ICONV_EILSEQ (size_t)-3 -#define SDL_ICONV_EINVAL (size_t)-4 - +/** + * An opaque handle representing string encoding conversion state. + * + * \since This datatype is available since SDL 3.1.3. + * + * \sa SDL_iconv_open + */ typedef struct SDL_iconv_data_t *SDL_iconv_t; /** @@ -4173,7 +5754,22 @@ extern SDL_DECLSPEC int SDLCALL SDL_iconv_close(SDL_iconv_t cd); * This function converts text between encodings, reading from and writing to * a buffer. * - * It returns the number of succesful conversions. + * It returns the number of succesful conversions on success. On error, + * SDL_ICONV_E2BIG is returned when the output buffer is too small, or + * SDL_ICONV_EILSEQ is returned when an invalid input sequence is encountered, + * or SDL_ICONV_EINVAL is returned when an incomplete input sequence is + * encountered. + * + * On exit: + * + * - inbuf will point to the beginning of the next multibyte sequence. On + * error, this is the location of the problematic input sequence. On + * success, this is the end of the input sequence. + * - inbytesleft will be set to the number of bytes left to convert, which + * will be 0 on success. + * - outbuf will point to the location where to store the next output byte. + * - outbytesleft will be set to the number of bytes left in the output + * buffer. * * \param cd The character set conversion context, created in * SDL_iconv_open(). @@ -4182,21 +5778,7 @@ extern SDL_DECLSPEC int SDLCALL SDL_iconv_close(SDL_iconv_t cd); * \param inbytesleft The number of bytes in the input buffer. * \param outbuf Address of variable that points to the output buffer. * \param outbytesleft The number of bytes in the output buffer. - * \returns the number of conversions on success, else SDL_ICONV_E2BIG is - * returned when the output buffer is too small, or SDL_ICONV_EILSEQ - * is returned when an invalid input sequence is encountered, or - * SDL_ICONV_EINVAL is returned when an incomplete input sequence is - * encountered. - * - * On exit: - * - * - inbuf will point to the beginning of the next multibyte - * sequence. On error, this is the location of the problematic - * input sequence. On success, this is the end of the input - * sequence. - inbytesleft will be set to the number of bytes left - * to convert, which will be 0 on success. - outbuf will point to - * the location where to store the next output byte. - outbytesleft - * will be set to the number of bytes left in the output buffer. + * \returns the number of conversions on success, or a negative error code. * * \since This function is available since SDL 3.1.3. * @@ -4208,6 +5790,12 @@ extern SDL_DECLSPEC size_t SDLCALL SDL_iconv(SDL_iconv_t cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft); +#define SDL_ICONV_ERROR (size_t)-1 /**< Generic error. Check SDL_GetError()? */ +#define SDL_ICONV_E2BIG (size_t)-2 /**< Output buffer was too small. */ +#define SDL_ICONV_EILSEQ (size_t)-3 /**< Invalid input sequence was encountered. */ +#define SDL_ICONV_EINVAL (size_t)-4 /**< Incomplete input sequence was encountered. */ + + /** * Helper function to convert a string's encoding in one call. * @@ -4238,12 +5826,65 @@ extern SDL_DECLSPEC char * SDLCALL SDL_iconv_string(const char *tocode, const char *inbuf, size_t inbytesleft); -/* Some helper macros for common cases... */ +/* Some helper macros for common SDL_iconv_string cases... */ + +/** + * Convert a UTF-8 string to the current locale's character encoding. + * + * This is a helper macro that might be more clear than calling + * SDL_iconv_string directly. However, it double-evaluates its parameter, so + * do not use an expression with side-effects here. + * + * \param S the string to convert. + * \returns a new string, converted to the new encoding, or NULL on error. + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_iconv_utf8_locale(S) SDL_iconv_string("", "UTF-8", S, SDL_strlen(S)+1) + +/** + * Convert a UTF-8 string to UCS-2. + * + * This is a helper macro that might be more clear than calling + * SDL_iconv_string directly. However, it double-evaluates its parameter, so + * do not use an expression with side-effects here. + * + * \param S the string to convert. + * \returns a new string, converted to the new encoding, or NULL on error. + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_iconv_utf8_ucs2(S) (Uint16 *)SDL_iconv_string("UCS-2", "UTF-8", S, SDL_strlen(S)+1) + +/** + * Convert a UTF-8 string to UCS-4. + * + * This is a helper macro that might be more clear than calling + * SDL_iconv_string directly. However, it double-evaluates its parameter, so + * do not use an expression with side-effects here. + * + * \param S the string to convert. + * \returns a new string, converted to the new encoding, or NULL on error. + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_iconv_utf8_ucs4(S) (Uint32 *)SDL_iconv_string("UCS-4", "UTF-8", S, SDL_strlen(S)+1) + +/** + * Convert a wchar_t string to UTF-8. + * + * This is a helper macro that might be more clear than calling + * SDL_iconv_string directly. However, it double-evaluates its parameter, so + * do not use an expression with side-effects here. + * + * \param S the string to convert. + * \returns a new string, converted to the new encoding, or NULL on error. + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_iconv_wchar_utf8(S) SDL_iconv_string("UTF-8", "WCHAR_T", (char *)S, (SDL_wcslen(S)+1)*sizeof(wchar_t)) + /* force builds using Clang's static analysis tools to use literal C runtime here, since there are possibly tests that are ineffective otherwise. */ #if defined(__clang_analyzer__) && !defined(SDL_DISABLE_ANALYZE_MACROS) @@ -4357,9 +5998,9 @@ SDL_FORCE_INLINE bool SDL_size_mul_check_overflow_builtin(size_t a, size_t b, si /** * Add two integers, checking for overflow. * - * If `a + b` would overflow, return -1. + * If `a + b` would overflow, return false. * - * Otherwise store `a + b` via ret and return 0. + * Otherwise store `a + b` via ret and return true. * * \param a the first addend. * \param b the second addend. diff --git a/libs/SDL3/include/SDL3/SDL_surface.h b/libs/SDL3/include/SDL3/SDL_surface.h index bdea0791..5f95643c 100644 --- a/libs/SDL3/include/SDL3/SDL_surface.h +++ b/libs/SDL3/include/SDL3/SDL_surface.h @@ -31,7 +31,7 @@ * * There is also a simple .bmp loader, SDL_LoadBMP(). SDL itself does not * provide loaders for various other file formats, but there are several - * excellent external libraries that do, including it's own satellite library, + * excellent external libraries that do, including its own satellite library, * SDL_image: * * https://github.com/libsdl-org/SDL_image @@ -115,6 +115,11 @@ typedef enum SDL_FlipMode * remaining bytes to reach the pitch are used as padding to reach a desired * alignment, and have undefined contents. * + * When a surface holds YUV format data, the planes are assumed to be + * contiguous without padding between them, e.g. a 32x32 surface in NV12 + * format with a pitch of 32 would consist of 32x32 bytes of Y plane followed + * by 32x16 bytes of UV plane. + * * \since This struct is available since SDL 3.1.3. * * \sa SDL_CreateSurface @@ -192,7 +197,6 @@ extern SDL_DECLSPEC SDL_Surface * SDLCALL SDL_CreateSurfaceFrom(int width, int h * * \since This function is available since SDL 3.1.3. * - * \sa SDL_CreateStackSurface * \sa SDL_CreateSurface * \sa SDL_CreateSurfaceFrom */ @@ -237,7 +241,7 @@ extern SDL_DECLSPEC SDL_PropertiesID SDLCALL SDL_GetSurfaceProperties(SDL_Surfac * interpreted in color operations. * * \param surface the SDL_Surface structure to update. - * \param colorspace an SDL_ColorSpace value describing the surface + * \param colorspace an SDL_Colorspace value describing the surface * colorspace. * \returns true on success or false on failure; call SDL_GetError() for more * information. @@ -352,7 +356,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_AddSurfaceAlternateImage(SDL_Surface *surfa * Return whether a surface has alternate versions available. * * \param surface the SDL_Surface structure to query. - * \returns true if alternate versions are available or true otherwise. + * \returns true if alternate versions are available or false otherwise. * * \since This function is available since SDL 3.1.3. * @@ -856,7 +860,6 @@ extern SDL_DECLSPEC SDL_Surface * SDLCALL SDL_ConvertSurface(SDL_Surface *surfac * \since This function is available since SDL 3.1.3. * * \sa SDL_ConvertSurface - * \sa SDL_ConvertSurface * \sa SDL_DestroySurface */ extern SDL_DECLSPEC SDL_Surface * SDLCALL SDL_ConvertSurfaceAndColorspace(SDL_Surface *surface, SDL_PixelFormat format, SDL_Palette *palette, SDL_Colorspace colorspace, SDL_PropertiesID props); @@ -872,7 +875,7 @@ extern SDL_DECLSPEC SDL_Surface * SDLCALL SDL_ConvertSurfaceAndColorspace(SDL_Su * \param dst_format an SDL_PixelFormat value of the `dst` pixels format. * \param dst a pointer to be filled in with new pixel data. * \param dst_pitch the pitch of the destination pixels, in bytes. - * \returns false on success or false on failure; call SDL_GetError() for more + * \returns true on success or false on failure; call SDL_GetError() for more * information. * * \since This function is available since SDL 3.1.3. @@ -888,20 +891,20 @@ extern SDL_DECLSPEC bool SDLCALL SDL_ConvertPixels(int width, int height, SDL_Pi * \param width the width of the block to copy, in pixels. * \param height the height of the block to copy, in pixels. * \param src_format an SDL_PixelFormat value of the `src` pixels format. - * \param src_colorspace an SDL_ColorSpace value describing the colorspace of + * \param src_colorspace an SDL_Colorspace value describing the colorspace of * the `src` pixels. * \param src_properties an SDL_PropertiesID with additional source color * properties, or 0. * \param src a pointer to the source pixels. * \param src_pitch the pitch of the source pixels, in bytes. * \param dst_format an SDL_PixelFormat value of the `dst` pixels format. - * \param dst_colorspace an SDL_ColorSpace value describing the colorspace of + * \param dst_colorspace an SDL_Colorspace value describing the colorspace of * the `dst` pixels. * \param dst_properties an SDL_PropertiesID with additional destination color * properties, or 0. * \param dst a pointer to be filled in with new pixel data. * \param dst_pitch the pitch of the destination pixels, in bytes. - * \returns false on success or false on failure; call SDL_GetError() for more + * \returns true on success or false on failure; call SDL_GetError() for more * information. * * \since This function is available since SDL 3.1.3. diff --git a/libs/SDL3/include/SDL3/SDL_system.h b/libs/SDL3/include/SDL3/SDL_system.h index 0fe0dcea..4e7181df 100644 --- a/libs/SDL3/include/SDL3/SDL_system.h +++ b/libs/SDL3/include/SDL3/SDL_system.h @@ -129,7 +129,29 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetDXGIOutputInfo(SDL_DisplayID displayID, * Platform specific functions for UNIX */ +/* this is defined in Xlib's headers, just need a simple declaration here. */ typedef union _XEvent XEvent; + +/** + * A callback to be used with SDL_SetX11EventHook. + * + * This callback may modify the event, and should return true if the event + * should continue to be processed, or false to prevent further processing. + * + * As this is processing an event directly from the X11 event loop, this + * callback should do the minimum required work and return quickly. + * + * \param userdata the app-defined pointer provided to SDL_SetX11EventHook. + * \param xevent a pointer to an Xlib XEvent union to process. + * \returns true to let event continue on, false to drop it. + * + * \threadsafety This may only be called (by SDL) from the thread handling the + * X11 event loop. + * + * \since This datatype is available since SDL 3.1.3. + * + * \sa SDL_SetX11EventHook + */ typedef bool (SDLCALL *SDL_X11EventHook)(void *userdata, XEvent *xevent); /** @@ -380,6 +402,13 @@ extern SDL_DECLSPEC void SDLCALL SDL_SendAndroidBackButton(void); * \since This macro is available since SDL 3.1.3. */ #define SDL_ANDROID_EXTERNAL_STORAGE_READ 0x01 + +/** + * See the official Android developer guide for more information: + * http://developer.android.com/guide/topics/data/data-storage.html + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_ANDROID_EXTERNAL_STORAGE_WRITE 0x02 /** @@ -468,7 +497,17 @@ extern SDL_DECLSPEC const char * SDLCALL SDL_GetAndroidExternalStoragePath(void) */ extern SDL_DECLSPEC const char * SDLCALL SDL_GetAndroidCachePath(void); - +/** + * Callback that presents a response from a SDL_RequestAndroidPermission call. + * + * \param userdata an app-controlled pointer that is passed to the callback. + * \param permission the Android-specific permission name that was requested. + * \param granted true if permission is granted, false if denied. + * + * \since This datatype is available since SDL 3.1.3. + * + * \sa SDL_RequestAndroidPermission + */ typedef void (SDLCALL *SDL_RequestAndroidPermissionCallback)(void *userdata, const char *permission, bool granted); /** @@ -595,7 +634,7 @@ typedef enum SDL_Sandbox * \returns the application sandbox environment or SDL_SANDBOX_NONE if the * application is not running in a sandbox environment. * - * \since This function is available since SDL 3.2.0. + * \since This function is available since SDL 3.1.6. */ extern SDL_DECLSPEC SDL_Sandbox SDLCALL SDL_GetSandbox(void); diff --git a/libs/SDL3/include/SDL3/SDL_test.h b/libs/SDL3/include/SDL3/SDL_test.h index e8468739..2cf83d80 100644 --- a/libs/SDL3/include/SDL3/SDL_test.h +++ b/libs/SDL3/include/SDL3/SDL_test.h @@ -20,8 +20,6 @@ */ /** - * \file SDL_test.h - * * Include file for SDL test framework. * * This code is a part of the SDL test library, not the main SDL library. diff --git a/libs/SDL3/include/SDL3/SDL_test_common.h b/libs/SDL3/include/SDL3/SDL_test_common.h index aeffeb93..8b909311 100644 --- a/libs/SDL3/include/SDL3/SDL_test_common.h +++ b/libs/SDL3/include/SDL3/SDL_test_common.h @@ -20,8 +20,6 @@ */ /** - * \file SDL_test_common.h - * * Common functions of SDL test framework. * * This code is a part of the SDL test library, not the main SDL library. diff --git a/libs/SDL3/include/SDL3/SDL_test_compare.h b/libs/SDL3/include/SDL3/SDL_test_compare.h index 24bc9ee6..db2ae6c6 100644 --- a/libs/SDL3/include/SDL3/SDL_test_compare.h +++ b/libs/SDL3/include/SDL3/SDL_test_compare.h @@ -20,8 +20,6 @@ */ /** - * \file SDL_test_compare.h - * * Comparison function of SDL test framework. * * This code is a part of the SDL test library, not the main SDL library. diff --git a/libs/SDL3/include/SDL3/SDL_test_font.h b/libs/SDL3/include/SDL3/SDL_test_font.h index 9d7b346e..bad949e0 100644 --- a/libs/SDL3/include/SDL3/SDL_test_font.h +++ b/libs/SDL3/include/SDL3/SDL_test_font.h @@ -20,8 +20,6 @@ */ /* - * \file SDL_test_font.h - * * Font related functions of SDL test framework. * * This code is a part of the SDL test library, not the main SDL library. diff --git a/libs/SDL3/include/SDL3/SDL_test_harness.h b/libs/SDL3/include/SDL3/SDL_test_harness.h index cea0db46..925dc78c 100644 --- a/libs/SDL3/include/SDL3/SDL_test_harness.h +++ b/libs/SDL3/include/SDL3/SDL_test_harness.h @@ -20,8 +20,6 @@ */ /** - * \file SDL_test_harness.h - * * Test suite related functions of SDL test framework. * * This code is a part of the SDL test library, not the main SDL library. diff --git a/libs/SDL3/include/SDL3/SDL_test_log.h b/libs/SDL3/include/SDL3/SDL_test_log.h index ff798d63..625d545d 100644 --- a/libs/SDL3/include/SDL3/SDL_test_log.h +++ b/libs/SDL3/include/SDL3/SDL_test_log.h @@ -20,8 +20,6 @@ */ /** - * \file SDL_test_log.h - * * Logging related functions of SDL test framework. * * This code is a part of the SDL test library, not the main SDL library. diff --git a/libs/SDL3/include/SDL3/SDL_test_md5.h b/libs/SDL3/include/SDL3/SDL_test_md5.h index d751aab9..a2954398 100644 --- a/libs/SDL3/include/SDL3/SDL_test_md5.h +++ b/libs/SDL3/include/SDL3/SDL_test_md5.h @@ -20,8 +20,6 @@ */ /** - * \file SDL_test_md5.h - * * MD5 related functions of SDL test framework. * * This code is a part of the SDL test library, not the main SDL library. diff --git a/libs/SDL3/include/SDL3/SDL_test_memory.h b/libs/SDL3/include/SDL3/SDL_test_memory.h index 16b2f633..d1d0ef34 100644 --- a/libs/SDL3/include/SDL3/SDL_test_memory.h +++ b/libs/SDL3/include/SDL3/SDL_test_memory.h @@ -20,8 +20,6 @@ */ /** - * \file SDL_test_memory.h - * * Memory tracking related functions of SDL test framework. * * This code is a part of the SDL test library, not the main SDL library. diff --git a/libs/SDL3/include/SDL3/SDL_thread.h b/libs/SDL3/include/SDL3/SDL_thread.h index 0eb48158..6bac1e40 100644 --- a/libs/SDL3/include/SDL3/SDL_thread.h +++ b/libs/SDL3/include/SDL3/SDL_thread.h @@ -102,6 +102,24 @@ typedef enum SDL_ThreadPriority { SDL_THREAD_PRIORITY_TIME_CRITICAL } SDL_ThreadPriority; +/** + * The SDL thread state. + * + * SDL stores the current state of a thread in an atomic int. The current + * state of a thread can be checked by calling SDL_GetThreadState. + * + * \since This enum is available since SDL 3.1.3. + * + * \sa SDL_GetThreadState + */ +typedef enum SDL_ThreadState +{ + SDL_THREAD_STATE_ALIVE, + SDL_THREAD_STATE_DETACHED, + SDL_THREAD_STATE_ZOMBIE, + SDL_THREAD_STATE_CLEANED, +} SDL_ThreadState; + /** * The function passed to SDL_CreateThread() as the new thread's entry point. * @@ -422,6 +440,19 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetCurrentThreadPriority(SDL_ThreadPriority */ extern SDL_DECLSPEC void SDLCALL SDL_WaitThread(SDL_Thread *thread, int *status); +/** + * Get the current state of a thread. + * + * \param thread the thread whose status you want to check. + * \returns the current state of a thread as defined in the SDL_ThreadState + * enum. + * + * \since This function is available since SDL 3.2.0. + * + * \sa SDL_ThreadState + */ +extern SDL_DECLSPEC SDL_ThreadState SDLCALL SDL_GetThreadState(SDL_Thread *thread); + /** * Let a thread clean up on exit without intervention. * diff --git a/libs/SDL3/include/SDL3/SDL_timer.h b/libs/SDL3/include/SDL3/SDL_timer.h index cb01870a..121ba3f5 100644 --- a/libs/SDL3/include/SDL3/SDL_timer.h +++ b/libs/SDL3/include/SDL3/SDL_timer.h @@ -38,16 +38,137 @@ extern "C" { #endif /* SDL time constants */ + +/** + * Number of milliseconds in a second. + * + * This is always 1000. + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_MS_PER_SECOND 1000 + +/** + * Number of microseconds in a second. + * + * This is always 1000000. + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_US_PER_SECOND 1000000 + +/** + * Number of nanoseconds in a second. + * + * This is always 1000000000. + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_NS_PER_SECOND 1000000000LL + +/** + * Number of nanoseconds in a millisecond. + * + * This is always 1000000. + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_NS_PER_MS 1000000 + +/** + * Number of nanoseconds in a microsecond. + * + * This is always 1000. + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_NS_PER_US 1000 + +/** + * Convert seconds to nanoseconds. + * + * This only converts whole numbers, not fractional seconds. + * + * \param S the number of seconds to convert. + * \returns S, expressed in nanoseconds. + * + * \threadsafety It is safe to call this macro from any thread. + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_SECONDS_TO_NS(S) (((Uint64)(S)) * SDL_NS_PER_SECOND) + +/** + * Convert nanoseconds to seconds. + * + * This performs a division, so the results can be dramatically different if + * `NS` is an integer or floating point value. + * + * \param NS the number of nanoseconds to convert. + * \returns NS, expressed in seconds. + * + * \threadsafety It is safe to call this macro from any thread. + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_NS_TO_SECONDS(NS) ((NS) / SDL_NS_PER_SECOND) + +/** + * Convert milliseconds to nanoseconds. + * + * This only converts whole numbers, not fractional milliseconds. + * + * \param MS the number of milliseconds to convert. + * \returns MS, expressed in nanoseconds. + * + * \threadsafety It is safe to call this macro from any thread. + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_MS_TO_NS(MS) (((Uint64)(MS)) * SDL_NS_PER_MS) + +/** + * Convert nanoseconds to milliseconds. + * + * This performs a division, so the results can be dramatically different if + * `NS` is an integer or floating point value. + * + * \param NS the number of nanoseconds to convert. + * \returns NS, expressed in milliseconds. + * + * \threadsafety It is safe to call this macro from any thread. + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_NS_TO_MS(NS) ((NS) / SDL_NS_PER_MS) + +/** + * Convert microseconds to nanoseconds. + * + * This only converts whole numbers, not fractional microseconds. + * + * \param US the number of microseconds to convert. + * \returns US, expressed in nanoseconds. + * + * \threadsafety It is safe to call this macro from any thread. + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_US_TO_NS(US) (((Uint64)(US)) * SDL_NS_PER_US) + +/** + * Convert nanoseconds to microseconds. + * + * This performs a division, so the results can be dramatically different if + * `NS` is an integer or floating point value. + * + * \param NS the number of nanoseconds to convert. + * \returns NS, expressed in microseconds. + * + * \threadsafety It is safe to call this macro from any thread. + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_NS_TO_US(NS) ((NS) / SDL_NS_PER_US) /** @@ -147,7 +268,7 @@ extern SDL_DECLSPEC void SDLCALL SDL_DelayNS(Uint64 ns); * * \threadsafety It is safe to call this function from any thread. * - * \since This function is available since SDL 3.2.0. + * \since This function is available since SDL 3.1.6. */ extern SDL_DECLSPEC void SDLCALL SDL_DelayPrecise(Uint64 ns); diff --git a/libs/SDL3/include/SDL3/SDL_touch.h b/libs/SDL3/include/SDL3/SDL_touch.h index 3eb47d52..6fbb08e6 100644 --- a/libs/SDL3/include/SDL3/SDL_touch.h +++ b/libs/SDL3/include/SDL3/SDL_touch.h @@ -38,21 +38,49 @@ extern "C" { #endif +/** + * A unique ID for a touch device. + * + * This ID is valid for the time the device is connected to the system, and is + * never reused for the lifetime of the application. + * + * The value 0 is an invalid ID. + * + * \since This datatype is available since SDL 3.1.3. + */ typedef Uint64 SDL_TouchID; + +/** + * A unique ID for a single finger on a touch device. + * + * This ID is valid for the time the finger (stylus, etc) is touching and will + * be unique for all fingers currently in contact, so this ID tracks the + * lifetime of a single continuous touch. This value may represent an index, a + * pointer, or some other unique ID, depending on the platform. + * + * The value 0 is an invalid ID. + * + * \since This datatype is available since SDL 3.1.3. + */ typedef Uint64 SDL_FingerID; +/** + * An enum that describes the type of a touch device. + * + * \since This enum is available since SDL 3.1.3. + */ typedef enum SDL_TouchDeviceType { SDL_TOUCH_DEVICE_INVALID = -1, - SDL_TOUCH_DEVICE_DIRECT, /* touch screen with window-relative coordinates */ - SDL_TOUCH_DEVICE_INDIRECT_ABSOLUTE, /* trackpad with absolute device coordinates */ - SDL_TOUCH_DEVICE_INDIRECT_RELATIVE /* trackpad with screen cursor-relative coordinates */ + SDL_TOUCH_DEVICE_DIRECT, /**< touch screen with window-relative coordinates */ + SDL_TOUCH_DEVICE_INDIRECT_ABSOLUTE, /**< trackpad with absolute device coordinates */ + SDL_TOUCH_DEVICE_INDIRECT_RELATIVE /**< trackpad with screen cursor-relative coordinates */ } SDL_TouchDeviceType; /** * Data about a single finger in a multitouch event. * - * Each touch even is a collection of fingers that are simultaneously in + * Each touch event is a collection of fingers that are simultaneously in * contact with the touch device (so a "touch" can be a "multitouch," in * reality), and this struct reports details of the specific fingers. * @@ -68,10 +96,18 @@ typedef struct SDL_Finger float pressure; /**< the quantity of pressure applied, normalized (0...1) */ } SDL_Finger; -/* Used as the device ID for mouse events simulated with touch input */ +/** + * The SDL_MouseID for mouse events simulated with touch input. + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_TOUCH_MOUSEID ((SDL_MouseID)-1) -/* Used as the SDL_TouchID for touch events simulated with mouse input */ +/** + * The SDL_TouchID for touch events simulated with mouse input. + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_MOUSE_TOUCHID ((SDL_TouchID)-1) diff --git a/libs/SDL3/include/SDL3/SDL_tray.h b/libs/SDL3/include/SDL3/SDL_tray.h new file mode 100644 index 00000000..a9f48283 --- /dev/null +++ b/libs/SDL3/include/SDL3/SDL_tray.h @@ -0,0 +1,437 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2024 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/** + * # CategoryTray + * + * System tray menu support. + */ + +#ifndef SDL_tray_h_ +#define SDL_tray_h_ + +#include +#include +#include +#include + +#include +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct SDL_Tray SDL_Tray; +typedef struct SDL_TrayMenu SDL_TrayMenu; +typedef struct SDL_TrayEntry SDL_TrayEntry; + +/** + * Flags that control the creation of system tray entries. + * + * Some of these flags are required; exactly one of them must be specified at + * the time a tray entry is created. Other flags are optional; zero or more of + * those can be OR'ed together with the required flag. + * + * \since This datatype is available since SDL 3.0.0. + * + * \sa SDL_InsertTrayEntryAt + */ +typedef Uint32 SDL_TrayEntryFlags; + +#define SDL_TRAYENTRY_BUTTON 0x00000001u /**< Make the entry a simple button. Required. */ +#define SDL_TRAYENTRY_CHECKBOX 0x00000002u /**< Make the entry a checkbox. Required. */ +#define SDL_TRAYENTRY_SUBMENU 0x00000004u /**< Prepare the entry to have a submenu. Required */ +#define SDL_TRAYENTRY_DISABLED 0x80000000u /**< Make the entry disabled. Optional. */ +#define SDL_TRAYENTRY_CHECKED 0x40000000u /**< Make the entry checked. This is valid only for checkboxes. Optional. */ + +/** + * A callback that is invoked when a tray entry is selected. + * + * \param userdata an optional pointer to pass extra data to the callback when + * it will be invoked. + * \param entry the tray entry that was selected. + * + * \sa SDL_SetTrayEntryCallback + */ +typedef void (SDLCALL *SDL_TrayCallback)(void *userdata, SDL_TrayEntry *entry); + +/** + * Create an icon to be placed in the operating system's tray, or equivalent. + * + * Many platforms advise not using a system tray unless persistence is a + * necessary feature. Avoid needlessly creating a tray icon, as the user may + * feel like it clutters their interface. + * + * Using tray icons require the video subsystem. + * + * \param icon a surface to be used as icon. May be NULL. + * \param tooltip a tooltip to be displayed when the mouse hovers the icon. + * Not supported on all platforms. May be NULL. + * \returns The newly created system tray icon. + * + * \since This function is available since SDL 3.2.0. + * + * \sa SDL_CreateTrayMenu + * \sa SDL_GetTrayMenu + * \sa SDL_DestroyTray + */ +extern SDL_DECLSPEC SDL_Tray *SDLCALL SDL_CreateTray(SDL_Surface *icon, const char *tooltip); + +/** + * Updates the system tray icon's icon. + * + * \param tray the tray icon to be updated. + * \param icon the new icon. May be NULL. + * + * \since This function is available since SDL 3.2.0. + * + * \sa SDL_CreateTray + */ +extern SDL_DECLSPEC void SDLCALL SDL_SetTrayIcon(SDL_Tray *tray, SDL_Surface *icon); + +/** + * Updates the system tray icon's tooltip. + * + * \param tray the tray icon to be updated. + * \param tooltip the new tooltip. May be NULL. + * + * \since This function is available since SDL 3.2.0. + * + * \sa SDL_CreateTray + */ +extern SDL_DECLSPEC void SDLCALL SDL_SetTrayTooltip(SDL_Tray *tray, const char *tooltip); + +/** + * Create a menu for a system tray. + * + * This should be called at most once per tray icon. + * + * This function does the same thing as SDL_CreateTraySubmenu(), except that + * it takes a SDL_Tray instead of a SDL_TrayEntry. + * + * A menu does not need to be destroyed; it will be destroyed with the tray. + * + * \param tray the tray to bind the menu to. + * \returns the newly created menu. + * + * \since This function is available since SDL 3.2.0. + * + * \sa SDL_CreateTray + * \sa SDL_GetTrayMenu + * \sa SDL_GetTrayMenuParentTray + */ +extern SDL_DECLSPEC SDL_TrayMenu *SDLCALL SDL_CreateTrayMenu(SDL_Tray *tray); + +/** + * Create a submenu for a system tray entry. + * + * This should be called at most once per tray entry. + * + * This function does the same thing as SDL_CreateTrayMenu, except that it + * takes a SDL_TrayEntry instead of a SDL_Tray. + * + * A menu does not need to be destroyed; it will be destroyed with the tray. + * + * \param entry the tray entry to bind the menu to. + * \returns the newly created menu. + * + * \since This function is available since SDL 3.2.0. + * + * \sa SDL_InsertTrayEntryAt + * \sa SDL_GetTraySubmenu + * \sa SDL_GetTrayMenuParentEntry + */ +extern SDL_DECLSPEC SDL_TrayMenu *SDLCALL SDL_CreateTraySubmenu(SDL_TrayEntry *entry); + +/** + * Gets a previously created tray menu. + * + * You should have called SDL_CreateTrayMenu() on the tray object. This + * function allows you to fetch it again later. + * + * This function does the same thing as SDL_GetTraySubmenu(), except that it + * takes a SDL_Tray instead of a SDL_TrayEntry. + * + * A menu does not need to be destroyed; it will be destroyed with the tray. + * + * \param tray the tray entry to bind the menu to. + * \returns the newly created menu. + * + * \since This function is available since SDL 3.2.0. + * + * \sa SDL_CreateTray + * \sa SDL_CreateTrayMenu + */ +extern SDL_DECLSPEC SDL_TrayMenu *SDLCALL SDL_GetTrayMenu(SDL_Tray *tray); + +/** + * Gets a previously created tray entry submenu. + * + * You should have called SDL_CreateTraySubenu() on the entry object. This + * function allows you to fetch it again later. + * + * This function does the same thing as SDL_GetTrayMenu(), except that it + * takes a SDL_TrayEntry instead of a SDL_Tray. + * + * A menu does not need to be destroyed; it will be destroyed with the tray. + * + * \param entry the tray entry to bind the menu to. + * \returns the newly created menu. + * + * \since This function is available since SDL 3.2.0. + * + * \sa SDL_InsertTrayEntryAt + * \sa SDL_CreateTraySubmenu + */ +extern SDL_DECLSPEC SDL_TrayMenu *SDLCALL SDL_GetTraySubmenu(SDL_TrayEntry *entry); + +/** + * Returns a list of entries in the menu, in order. + * + * \param menu The menu to get entries from. + * \param size An optional pointer to obtain the number of entries in the + * menu. + * \returns the entries within the given menu. The pointer becomes invalid + * when any function that inserts or deletes entries in the menu is + * called. + * + * \since This function is available since SDL 3.2.0. + * + * \sa SDL_RemoveTrayEntry + * \sa SDL_InsertTrayEntryAt + */ +extern SDL_DECLSPEC const SDL_TrayEntry **SDLCALL SDL_GetTrayEntries(SDL_TrayMenu *menu, int *size); + +/** + * Removes a tray entry. + * + * \param entry The entry to be deleted. + * + * \since This function is available since SDL 3.2.0. + * + * \sa SDL_GetTrayEntries + * \sa SDL_InsertTrayEntryAt + */ +extern SDL_DECLSPEC void SDLCALL SDL_RemoveTrayEntry(SDL_TrayEntry *entry); + +/** + * Insert a tray entry at a given position. + * + * If label is NULL, the entry will be a separator. Many functions won't work + * for an entry that is a separator. + * + * An entry does not need to be destroyed; it will be destroyed with the tray. + * + * \param menu the menu to append the entry to. + * \param pos the desired position for the new entry. Entries at or following + * this place will be moved. If pos is -1, the entry is appended. + * \param label the text to be displayed on the entry, or NULL for a + * separator. + * \param flags a combination of flags, some of which are mandatory. + * \returns the newly created entry, or NULL if pos is out of bounds. + * + * \since This function is available since SDL 3.2.0. + * + * \sa SDL_TrayEntryFlags + * \sa SDL_GetTrayEntries + * \sa SDL_RemoveTrayEntry + * \sa SDL_GetTrayEntryParent + */ +extern SDL_DECLSPEC SDL_TrayEntry *SDLCALL SDL_InsertTrayEntryAt(SDL_TrayMenu *menu, int pos, const char *label, SDL_TrayEntryFlags flags); + +/** + * Sets the label of an entry. + * + * An entry cannot change between a separator and an ordinary entry; that is, + * it is not possible to set a non-NULL label on an entry that has a NULL + * label (separators), or to set a NULL label to an entry that has a non-NULL + * label. The function will silently fail if that happens. + * + * \param entry the entry to be updated. + * \param label the new label for the entry. + * + * \since This function is available since SDL 3.2.0. + * + * \sa SDL_GetTrayEntries + * \sa SDL_InsertTrayEntryAt + * \sa SDL_GetTrayEntryLabel + */ +extern SDL_DECLSPEC void SDLCALL SDL_SetTrayEntryLabel(SDL_TrayEntry *entry, const char *label); + +/** + * Gets the label of an entry. + * + * If the returned value is NULL, the entry is a separator. + * + * \param entry the entry to be read. + * \returns the label of the entry. + * + * \since This function is available since SDL 3.2.0. + * + * \sa SDL_GetTrayEntries + * \sa SDL_InsertTrayEntryAt + * \sa SDL_SetTrayEntryLabel + */ +extern SDL_DECLSPEC const char *SDLCALL SDL_GetTrayEntryLabel(SDL_TrayEntry *entry); + +/** + * Sets whether or not an entry is checked. + * + * The entry must have been created with the SDL_TRAYENTRY_CHECKBOX flag. + * + * \param entry the entry to be updated. + * \param checked SDL_TRUE if the entry should be checked; SDL_FALSE + * otherwise. + * + * \since This function is available since SDL 3.2.0. + * + * \sa SDL_GetTrayEntries + * \sa SDL_InsertTrayEntryAt + * \sa SDL_GetTrayEntryChecked + */ +extern SDL_DECLSPEC void SDLCALL SDL_SetTrayEntryChecked(SDL_TrayEntry *entry, bool checked); + +/** + * Gets whether or not an entry is checked. + * + * The entry must have been created with the SDL_TRAYENTRY_CHECKBOX flag. + * + * \param entry the entry to be read. + * \returns SDL_TRUE if the entry is checked; SDL_FALSE otherwise. + * + * \since This function is available since SDL 3.2.0. + * + * \sa SDL_GetTrayEntries + * \sa SDL_InsertTrayEntryAt + * \sa SDL_SetTrayEntryChecked + */ +extern SDL_DECLSPEC bool SDLCALL SDL_GetTrayEntryChecked(SDL_TrayEntry *entry); + +/** + * Sets whether or not an entry is enabled. + * + * \param entry the entry to be updated. + * \param enabled SDL_TRUE if the entry should be enabled; SDL_FALSE + * otherwise. + * + * \since This function is available since SDL 3.2.0. + * + * \sa SDL_GetTrayEntries + * \sa SDL_InsertTrayEntryAt + * \sa SDL_GetTrayEntryEnabled + */ +extern SDL_DECLSPEC void SDLCALL SDL_SetTrayEntryEnabled(SDL_TrayEntry *entry, bool enabled); + +/** + * Gets whether or not an entry is enabled. + * + * \param entry the entry to be read. + * \returns SDL_TRUE if the entry is enabled; SDL_FALSE otherwise. + * + * \since This function is available since SDL 3.2.0. + * + * \sa SDL_GetTrayEntries + * \sa SDL_InsertTrayEntryAt + * \sa SDL_SetTrayEntryEnabled + */ +extern SDL_DECLSPEC bool SDLCALL SDL_GetTrayEntryEnabled(SDL_TrayEntry *entry); + +/** + * Sets a callback to be invoked when the entry is selected. + * + * \param entry the entry to be updated. + * \param callback a callback to be invoked when the entry is selected. + * \param userdata an optional pointer to pass extra data to the callback when + * it will be invoked. + * + * \since This function is available since SDL 3.2.0. + * + * \sa SDL_GetTrayEntries + * \sa SDL_InsertTrayEntryAt + */ +extern SDL_DECLSPEC void SDLCALL SDL_SetTrayEntryCallback(SDL_TrayEntry *entry, SDL_TrayCallback callback, void *userdata); + +/** + * Destroys a tray object. + * + * This also destroys all associated menus and entries. + * + * \param tray the tray icon to be destroyed. + * + * \since This function is available since SDL 3.2.0. + * + * \sa SDL_CreateTray + */ +extern SDL_DECLSPEC void SDLCALL SDL_DestroyTray(SDL_Tray *tray); + +/** + * Gets the menu contianing a certain tray entry. + * + * \param entry the entry for which to get the parent menu. + * \returns the parent menu. + * + * \since This function is available since SDL 3.2.0. + * + * \sa SDL_InsertTrayEntryAt + */ +extern SDL_DECLSPEC SDL_TrayMenu *SDLCALL SDL_GetTrayEntryParent(SDL_TrayEntry *entry); + +/** + * Gets the entry for which the menu is a submenu, if the current menu is a + * submenu. + * + * Either this function or SDL_GetTrayMenuParentTray() will return non-NULL + * for any given menu. + * + * \param menu the menu for which to get the parent entry. + * \returns the parent entry, or NULL if this menu is not a submenu. + * + * \since This function is available since SDL 3.2.0. + * + * \sa SDL_CreateTraySubmenu + * \sa SDL_GetTrayMenuParentTray + */ +extern SDL_DECLSPEC SDL_TrayEntry *SDLCALL SDL_GetTrayMenuParentEntry(SDL_TrayMenu *menu); + +/** + * Gets the tray for which this menu is the first-level menu, if the current + * menu isn't a submenu. + * + * Either this function or SDL_GetTrayMenuParentEntry() will return non-NULL + * for any given menu. + * + * \param menu the menu for which to get the parent enttrayry. + * \returns the parent tray, or NULL if this menu is a submenu. + * + * \since This function is available since SDL 3.2.0. + * + * \sa SDL_CreateTrayMenu + * \sa SDL_GetTrayMenuParentEntry + */ +extern SDL_DECLSPEC SDL_Tray *SDLCALL SDL_GetTrayMenuParentTray(SDL_TrayMenu *menu); + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +} +#endif +#include + +#endif /* SDL_tray_h_ */ diff --git a/libs/SDL3/include/SDL3/SDL_version.h b/libs/SDL3/include/SDL3/SDL_version.h index f61b5f33..427612b0 100644 --- a/libs/SDL3/include/SDL3/SDL_version.h +++ b/libs/SDL3/include/SDL3/SDL_version.h @@ -62,7 +62,7 @@ extern "C" { * * \since This macro is available since SDL 3.1.3. */ -#define SDL_MICRO_VERSION 6 +#define SDL_MICRO_VERSION 7 /** * This macro turns the version numbers into a numeric value. diff --git a/libs/SDL3/include/SDL3/SDL_video.h b/libs/SDL3/include/SDL3/SDL_video.h index 45c9690c..388d1e7a 100644 --- a/libs/SDL3/include/SDL3/SDL_video.h +++ b/libs/SDL3/include/SDL3/SDL_video.h @@ -112,7 +112,15 @@ typedef enum SDL_SystemTheme SDL_SYSTEM_THEME_DARK /**< Dark colored system theme */ } SDL_SystemTheme; -/* Internal display mode data */ +/** + * Internal display mode data. + * + * This lives as a field in SDL_DisplayMode, as opaque data. + * + * \since This struct is available since SDL 3.1.3. + * + * \sa SDL_DisplayMode + */ typedef struct SDL_DisplayModeData SDL_DisplayModeData; /** @@ -206,27 +214,87 @@ typedef Uint64 SDL_WindowFlags; /** - * Used to indicate that you don't care what the window position is. + * A magic value used with SDL_WINDOWPOS_UNDEFINED. + * + * Generally this macro isn't used directly, but rather through + * SDL_WINDOWPOS_UNDEFINED or SDL_WINDOWPOS_UNDEFINED_DISPLAY. * * \since This macro is available since SDL 3.1.3. */ #define SDL_WINDOWPOS_UNDEFINED_MASK 0x1FFF0000u -#define SDL_WINDOWPOS_UNDEFINED_DISPLAY(X) (SDL_WINDOWPOS_UNDEFINED_MASK|(X)) -#define SDL_WINDOWPOS_UNDEFINED SDL_WINDOWPOS_UNDEFINED_DISPLAY(0) -#define SDL_WINDOWPOS_ISUNDEFINED(X) \ - (((X)&0xFFFF0000) == SDL_WINDOWPOS_UNDEFINED_MASK) /** - * Used to indicate that the window position should be centered. + * Used to indicate that you don't care what the window position is. + * + * If you _really_ don't care, SDL_WINDOWPOS_UNDEFINED is the same, but always + * uses the primary display instead of specifying one. + * + * \param X the SDL_DisplayID of the display to use. + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_WINDOWPOS_UNDEFINED_DISPLAY(X) (SDL_WINDOWPOS_UNDEFINED_MASK|(X)) + +/** + * Used to indicate that you don't care what the window position/display is. + * + * This always uses the primary display. + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_WINDOWPOS_UNDEFINED SDL_WINDOWPOS_UNDEFINED_DISPLAY(0) + +/** + * A macro to test if the window position is marked as "undefined." + * + * \param X the window position value. + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_WINDOWPOS_ISUNDEFINED(X) (((X)&0xFFFF0000) == SDL_WINDOWPOS_UNDEFINED_MASK) + +/** + * A magic value used with SDL_WINDOWPOS_CENTERED. + * + * Generally this macro isn't used directly, but rather through + * SDL_WINDOWPOS_CENTERED or SDL_WINDOWPOS_CENTERED_DISPLAY. * * \since This macro is available since SDL 3.1.3. */ #define SDL_WINDOWPOS_CENTERED_MASK 0x2FFF0000u + +/** + * Used to indicate that the window position should be centered. + * + * SDL_WINDOWPOS_CENTERED is the same, but always uses the primary display + * instead of specifying one. + * + * \param X the SDL_DisplayID of the display to use. + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_WINDOWPOS_CENTERED_DISPLAY(X) (SDL_WINDOWPOS_CENTERED_MASK|(X)) + +/** + * Used to indicate that the window position should be centered. + * + * This always uses the primary display. + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_WINDOWPOS_CENTERED SDL_WINDOWPOS_CENTERED_DISPLAY(0) + +/** + * A macro to test if the window position is marked as "centered." + * + * \param X the window position value. + * + * \since This macro is available since SDL 3.1.3. + */ #define SDL_WINDOWPOS_ISCENTERED(X) \ (((X)&0xFFFF0000) == SDL_WINDOWPOS_CENTERED_MASK) + /** * Window flash operation. * @@ -249,14 +317,38 @@ typedef enum SDL_FlashOperation typedef struct SDL_GLContextState *SDL_GLContext; /** - * Opaque EGL types. + * Opaque type for an EGL display. * * \since This datatype is available since SDL 3.1.3. */ typedef void *SDL_EGLDisplay; + +/** + * Opaque type for an EGL config. + * + * \since This datatype is available since SDL 3.1.3. + */ typedef void *SDL_EGLConfig; + +/** + * Opaque type for an EGL surface. + * + * \since This datatype is available since SDL 3.1.3. + */ typedef void *SDL_EGLSurface; + +/** + * An EGL attribute, used when creating an EGL context. + * + * \since This datatype is available since SDL 3.1.3. + */ typedef intptr_t SDL_EGLAttrib; + +/** + * An EGL integer attribute, used when creating an EGL surface. + * + * \since This datatype is available since SDL 3.1.3. + */ typedef int SDL_EGLint; /** @@ -419,6 +511,8 @@ typedef Uint32 SDL_GLContextResetNotification; * * \returns the number of built in video drivers. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetVideoDriver @@ -438,6 +532,8 @@ extern SDL_DECLSPEC int SDLCALL SDL_GetNumVideoDrivers(void); * \param index the index of a video driver. * \returns the name of the video driver with the given **index**. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetNumVideoDrivers @@ -454,6 +550,8 @@ extern SDL_DECLSPEC const char * SDLCALL SDL_GetVideoDriver(int index); * \returns the name of the current video driver or NULL if no driver has been * initialized. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetNumVideoDrivers @@ -466,6 +564,8 @@ extern SDL_DECLSPEC const char * SDLCALL SDL_GetCurrentVideoDriver(void); * * \returns the current system theme, light, dark, or unknown. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. */ extern SDL_DECLSPEC SDL_SystemTheme SDLCALL SDL_GetSystemTheme(void); @@ -479,6 +579,8 @@ extern SDL_DECLSPEC SDL_SystemTheme SDLCALL SDL_GetSystemTheme(void); * call SDL_GetError() for more information. This should be freed * with SDL_free() when it is no longer needed. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. */ extern SDL_DECLSPEC SDL_DisplayID * SDLCALL SDL_GetDisplays(int *count); @@ -489,6 +591,8 @@ extern SDL_DECLSPEC SDL_DisplayID * SDLCALL SDL_GetDisplays(int *count); * \returns the instance ID of the primary display on success or 0 on failure; * call SDL_GetError() for more information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetDisplays @@ -517,6 +621,8 @@ extern SDL_DECLSPEC SDL_DisplayID SDLCALL SDL_GetPrimaryDisplay(void); * \returns a valid property ID on success or 0 on failure; call * SDL_GetError() for more information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. */ extern SDL_DECLSPEC SDL_PropertiesID SDLCALL SDL_GetDisplayProperties(SDL_DisplayID displayID); @@ -531,6 +637,8 @@ extern SDL_DECLSPEC SDL_PropertiesID SDLCALL SDL_GetDisplayProperties(SDL_Displa * \returns the name of a display or NULL on failure; call SDL_GetError() for * more information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetDisplays @@ -547,6 +655,8 @@ extern SDL_DECLSPEC const char * SDLCALL SDL_GetDisplayName(SDL_DisplayID displa * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetDisplayUsableBounds @@ -571,6 +681,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetDisplayBounds(SDL_DisplayID displayID, S * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetDisplayBounds @@ -585,6 +697,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetDisplayUsableBounds(SDL_DisplayID displa * \returns the SDL_DisplayOrientation enum value of the display, or * `SDL_ORIENTATION_UNKNOWN` if it isn't available. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetDisplays @@ -598,6 +712,8 @@ extern SDL_DECLSPEC SDL_DisplayOrientation SDLCALL SDL_GetNaturalDisplayOrientat * \returns the SDL_DisplayOrientation enum value of the display, or * `SDL_ORIENTATION_UNKNOWN` if it isn't available. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetDisplays @@ -616,6 +732,8 @@ extern SDL_DECLSPEC SDL_DisplayOrientation SDLCALL SDL_GetCurrentDisplayOrientat * \returns the content scale of the display, or 0.0f on failure; call * SDL_GetError() for more information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetDisplays @@ -642,6 +760,8 @@ extern SDL_DECLSPEC float SDLCALL SDL_GetDisplayContentScale(SDL_DisplayID displ * single allocation that should be freed with SDL_free() when it is * no longer needed. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetDisplays @@ -665,17 +785,19 @@ extern SDL_DECLSPEC SDL_DisplayMode ** SDLCALL SDL_GetFullscreenDisplayModes(SDL * for the desktop refresh rate. * \param include_high_density_modes boolean to include high density modes in * the search. - * \param mode a pointer filled in with the closest display mode equal to or - * larger than the desired mode. + * \param closest a pointer filled in with the closest display mode equal to + * or larger than the desired mode. * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetDisplays * \sa SDL_GetFullscreenDisplayModes */ -extern SDL_DECLSPEC bool SDLCALL SDL_GetClosestFullscreenDisplayMode(SDL_DisplayID displayID, int w, int h, float refresh_rate, bool include_high_density_modes, SDL_DisplayMode *mode); +extern SDL_DECLSPEC bool SDLCALL SDL_GetClosestFullscreenDisplayMode(SDL_DisplayID displayID, int w, int h, float refresh_rate, bool include_high_density_modes, SDL_DisplayMode *closest); /** * Get information about the desktop's display mode. @@ -689,6 +811,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetClosestFullscreenDisplayMode(SDL_Display * \returns a pointer to the desktop display mode or NULL on failure; call * SDL_GetError() for more information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetCurrentDisplayMode @@ -708,6 +832,8 @@ extern SDL_DECLSPEC const SDL_DisplayMode * SDLCALL SDL_GetDesktopDisplayMode(SD * \returns a pointer to the desktop display mode or NULL on failure; call * SDL_GetError() for more information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetDesktopDisplayMode @@ -722,6 +848,8 @@ extern SDL_DECLSPEC const SDL_DisplayMode * SDLCALL SDL_GetCurrentDisplayMode(SD * \returns the instance ID of the display containing the point or 0 on * failure; call SDL_GetError() for more information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetDisplayBounds @@ -737,6 +865,8 @@ extern SDL_DECLSPEC SDL_DisplayID SDLCALL SDL_GetDisplayForPoint(const SDL_Point * closest to the center of the rect on success or 0 on failure; call * SDL_GetError() for more information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetDisplayBounds @@ -752,6 +882,8 @@ extern SDL_DECLSPEC SDL_DisplayID SDLCALL SDL_GetDisplayForRect(const SDL_Rect * * on success or 0 on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetDisplayBounds @@ -770,6 +902,8 @@ extern SDL_DECLSPEC SDL_DisplayID SDLCALL SDL_GetDisplayForWindow(SDL_Window *wi * \returns the pixel density or 0.0f on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetWindowDisplayScale @@ -794,6 +928,8 @@ extern SDL_DECLSPEC float SDLCALL SDL_GetWindowPixelDensity(SDL_Window *window); * \returns the display scale, or 0.0f on failure; call SDL_GetError() for * more information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. */ extern SDL_DECLSPEC float SDLCALL SDL_GetWindowDisplayScale(SDL_Window *window); @@ -823,6 +959,8 @@ extern SDL_DECLSPEC float SDLCALL SDL_GetWindowDisplayScale(SDL_Window *window); * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetWindowFullscreenMode @@ -838,6 +976,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowFullscreenMode(SDL_Window *window, * \returns a pointer to the exclusive fullscreen mode to use or NULL for * borderless fullscreen desktop mode. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_SetWindowFullscreenMode @@ -854,6 +994,8 @@ extern SDL_DECLSPEC const SDL_DisplayMode * SDLCALL SDL_GetWindowFullscreenMode( * SDL_GetError() for more information. This should be freed with * SDL_free() when it is no longer needed. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. */ extern SDL_DECLSPEC void * SDLCALL SDL_GetWindowICCProfile(SDL_Window *window, size_t *size); @@ -866,6 +1008,8 @@ extern SDL_DECLSPEC void * SDLCALL SDL_GetWindowICCProfile(SDL_Window *window, s * SDL_PIXELFORMAT_UNKNOWN on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. */ extern SDL_DECLSPEC SDL_PixelFormat SDLCALL SDL_GetWindowPixelFormat(SDL_Window *window); @@ -880,6 +1024,8 @@ extern SDL_DECLSPEC SDL_PixelFormat SDLCALL SDL_GetWindowPixelFormat(SDL_Window * allocation that should be freed with SDL_free() when it is no * longer needed. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. */ extern SDL_DECLSPEC SDL_Window ** SDLCALL SDL_GetWindows(int *count); @@ -959,6 +1105,8 @@ extern SDL_DECLSPEC SDL_Window ** SDLCALL SDL_GetWindows(int *count); * \returns the window that was created or NULL on failure; call * SDL_GetError() for more information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_CreateWindowAndRenderer @@ -1019,6 +1167,8 @@ extern SDL_DECLSPEC SDL_Window * SDLCALL SDL_CreateWindow(const char *title, int * \returns the window that was created or NULL on failure; call * SDL_GetError() for more information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_CreateWindow @@ -1137,6 +1287,8 @@ extern SDL_DECLSPEC SDL_Window * SDLCALL SDL_CreatePopupWindow(SDL_Window *paren * \returns the window that was created or NULL on failure; call * SDL_GetError() for more information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_CreateProperties @@ -1190,6 +1342,8 @@ extern SDL_DECLSPEC SDL_Window * SDLCALL SDL_CreateWindowWithProperties(SDL_Prop * \returns the ID of the window on success or 0 on failure; call * SDL_GetError() for more information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetWindowFromID @@ -1206,6 +1360,8 @@ extern SDL_DECLSPEC SDL_WindowID SDLCALL SDL_GetWindowID(SDL_Window *window); * \returns the window associated with `id` or NULL if it doesn't exist; call * SDL_GetError() for more information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetWindowID @@ -1219,6 +1375,8 @@ extern SDL_DECLSPEC SDL_Window * SDLCALL SDL_GetWindowFromID(SDL_WindowID id); * \returns the parent of the window on success or NULL if the window has no * parent. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_CreatePopupWindow @@ -1313,6 +1471,8 @@ extern SDL_DECLSPEC SDL_Window * SDLCALL SDL_GetWindowParent(SDL_Window *window) * the window * - `SDL_PROP_WINDOW_WAYLAND_SURFACE_POINTER`: the wl_surface associated with * the window + * - `SDL_PROP_WINDOW_WAYLAND_VIEWPORT_POINTER`: the wp_viewport associated + * with the window * - `SDL_PROP_WINDOW_WAYLAND_EGL_WINDOW_POINTER`: the wl_egl_window * associated with the window * - `SDL_PROP_WINDOW_WAYLAND_XDG_SURFACE_POINTER`: the xdg_surface associated @@ -1339,6 +1499,8 @@ extern SDL_DECLSPEC SDL_Window * SDLCALL SDL_GetWindowParent(SDL_Window *window) * \returns a valid property ID on success or 0 on failure; call * SDL_GetError() for more information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. */ extern SDL_DECLSPEC SDL_PropertiesID SDLCALL SDL_GetWindowProperties(SDL_Window *window); @@ -1368,6 +1530,7 @@ extern SDL_DECLSPEC SDL_PropertiesID SDLCALL SDL_GetWindowProperties(SDL_Window #define SDL_PROP_WINDOW_WIN32_INSTANCE_POINTER "SDL.window.win32.instance" #define SDL_PROP_WINDOW_WAYLAND_DISPLAY_POINTER "SDL.window.wayland.display" #define SDL_PROP_WINDOW_WAYLAND_SURFACE_POINTER "SDL.window.wayland.surface" +#define SDL_PROP_WINDOW_WAYLAND_VIEWPORT_POINTER "SDL.window.wayland.viewport" #define SDL_PROP_WINDOW_WAYLAND_EGL_WINDOW_POINTER "SDL.window.wayland.egl_window" #define SDL_PROP_WINDOW_WAYLAND_XDG_SURFACE_POINTER "SDL.window.wayland.xdg_surface" #define SDL_PROP_WINDOW_WAYLAND_XDG_TOPLEVEL_POINTER "SDL.window.wayland.xdg_toplevel" @@ -1384,6 +1547,8 @@ extern SDL_DECLSPEC SDL_PropertiesID SDLCALL SDL_GetWindowProperties(SDL_Window * \param window the window to query. * \returns a mask of the SDL_WindowFlags associated with `window`. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_CreateWindow @@ -1406,6 +1571,8 @@ extern SDL_DECLSPEC SDL_WindowFlags SDLCALL SDL_GetWindowFlags(SDL_Window *windo * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetWindowTitle @@ -1419,6 +1586,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowTitle(SDL_Window *window, const ch * \returns the title of the window in UTF-8 format or "" if there is no * title. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_SetWindowTitle @@ -1443,6 +1612,8 @@ extern SDL_DECLSPEC const char * SDLCALL SDL_GetWindowTitle(SDL_Window *window); * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. */ extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowIcon(SDL_Window *window, SDL_Surface *icon); @@ -1450,13 +1621,12 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowIcon(SDL_Window *window, SDL_Surfa /** * Request that the window's position be set. * - * If, at the time of this request, the window is in a fixed-size state such - * as maximized, this request may be deferred until the window returns to a - * resizable state. + * If the window is in an exclusive fullscreen or maximized state, this + * request has no effect. * * This can be used to reposition fullscreen-desktop windows onto a different - * display, however, exclusive fullscreen windows are locked to a specific - * display and can only be repositioned programmatically via + * display, however, as exclusive fullscreen windows are locked to a specific + * display, they can only be repositioned programmatically via * SDL_SetWindowFullscreenMode(). * * On some windowing systems this request is asynchronous and the new @@ -1480,6 +1650,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowIcon(SDL_Window *window, SDL_Surfa * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetWindowPosition @@ -1504,6 +1676,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowPosition(SDL_Window *window, int x * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_SetWindowPosition @@ -1513,12 +1687,11 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetWindowPosition(SDL_Window *window, int * /** * Request that the size of a window's client area be set. * - * If, at the time of this request, the window in a fixed-size state, such as - * maximized or fullscreen, the request will be deferred until the window - * exits this state and becomes resizable again. + * If the window is in a fullscreen or maximized state, this request has no + * effect. * - * To change the fullscreen mode of a window, use - * SDL_SetWindowFullscreenMode() + * To change the exclusive fullscreen mode of a window, use + * SDL_SetWindowFullscreenMode(). * * On some windowing systems, this request is asynchronous and the new window * size may not have have been applied immediately upon the return of this @@ -1538,6 +1711,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetWindowPosition(SDL_Window *window, int * * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetWindowSize @@ -1559,6 +1734,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowSize(SDL_Window *window, int w, in * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetRenderOutputSize @@ -1583,6 +1760,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetWindowSize(SDL_Window *window, int *w, i * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. */ extern SDL_DECLSPEC bool SDLCALL SDL_GetWindowSafeArea(SDL_Window *window, SDL_Rect *rect); @@ -1619,6 +1798,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetWindowSafeArea(SDL_Window *window, SDL_R * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetWindowAspectRatio @@ -1637,6 +1818,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowAspectRatio(SDL_Window *window, fl * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_SetWindowAspectRatio @@ -1672,6 +1855,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetWindowAspectRatio(SDL_Window *window, fl * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetWindowSize @@ -1689,6 +1874,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetWindowBordersSize(SDL_Window *window, in * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_CreateWindow @@ -1705,6 +1892,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetWindowSizeInPixels(SDL_Window *window, i * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetWindowMinimumSize @@ -1723,6 +1912,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowMinimumSize(SDL_Window *window, in * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetWindowMaximumSize @@ -1739,6 +1930,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetWindowMinimumSize(SDL_Window *window, in * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetWindowMaximumSize @@ -1757,6 +1950,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowMaximumSize(SDL_Window *window, in * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetWindowMinimumSize @@ -1778,6 +1973,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetWindowMaximumSize(SDL_Window *window, in * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetWindowFlags @@ -1798,6 +1995,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowBordered(SDL_Window *window, bool * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetWindowFlags @@ -1815,6 +2014,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowResizable(SDL_Window *window, bool * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetWindowFlags @@ -1828,6 +2029,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowAlwaysOnTop(SDL_Window *window, bo * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_HideWindow @@ -1842,9 +2045,12 @@ extern SDL_DECLSPEC bool SDLCALL SDL_ShowWindow(SDL_Window *window); * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_ShowWindow + * \sa SDL_WINDOW_HIDDEN */ extern SDL_DECLSPEC bool SDLCALL SDL_HideWindow(SDL_Window *window); @@ -1862,6 +2068,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_HideWindow(SDL_Window *window); * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. */ extern SDL_DECLSPEC bool SDLCALL SDL_RaiseWindow(SDL_Window *window); @@ -1890,6 +2098,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_RaiseWindow(SDL_Window *window); * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_MinimizeWindow @@ -1901,6 +2111,9 @@ extern SDL_DECLSPEC bool SDLCALL SDL_MaximizeWindow(SDL_Window *window); /** * Request that the window be minimized to an iconic representation. * + * If the window is in a fullscreen state, this request has no direct effect. + * It may alter the state the window is returned to when leaving fullscreen. + * * On some windowing systems this request is asynchronous and the new window * state may not have been applied immediately upon the return of this * function. If an immediate change is required, call SDL_SyncWindow() to @@ -1914,6 +2127,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_MaximizeWindow(SDL_Window *window); * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_MaximizeWindow @@ -1926,6 +2141,9 @@ extern SDL_DECLSPEC bool SDLCALL SDL_MinimizeWindow(SDL_Window *window); * Request that the size and position of a minimized or maximized window be * restored. * + * If the window is in a fullscreen state, this request has no direct effect. + * It may alter the state the window is returned to when leaving fullscreen. + * * On some windowing systems this request is asynchronous and the new window * state may not have have been applied immediately upon the return of this * function. If an immediate change is required, call SDL_SyncWindow() to @@ -1939,6 +2157,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_MinimizeWindow(SDL_Window *window); * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_MaximizeWindow @@ -1968,11 +2188,14 @@ extern SDL_DECLSPEC bool SDLCALL SDL_RestoreWindow(SDL_Window *window); * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetWindowFullscreenMode * \sa SDL_SetWindowFullscreenMode * \sa SDL_SyncWindow + * \sa SDL_WINDOW_FULLSCREEN */ extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowFullscreen(SDL_Window *window, bool fullscreen); @@ -1993,6 +2216,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowFullscreen(SDL_Window *window, boo * \returns true on success or false if the operation timed out before the * window was in the requested state. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_SetWindowSize @@ -2012,6 +2237,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SyncWindow(SDL_Window *window); * \returns true if there is a surface associated with the window, or false * otherwise. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetWindowSurface @@ -2036,6 +2263,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_WindowHasSurface(SDL_Window *window); * \returns the surface associated with the window, or NULL on failure; call * SDL_GetError() for more information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_DestroyWindowSurface @@ -2063,6 +2292,8 @@ extern SDL_DECLSPEC SDL_Surface * SDLCALL SDL_GetWindowSurface(SDL_Window *windo * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetWindowSurfaceVSync @@ -2081,6 +2312,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowSurfaceVSync(SDL_Window *window, i * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_SetWindowSurfaceVSync @@ -2099,6 +2332,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetWindowSurfaceVSync(SDL_Window *window, i * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetWindowSurface @@ -2126,6 +2361,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_UpdateWindowSurface(SDL_Window *window); * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetWindowSurface @@ -2140,6 +2377,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_UpdateWindowSurfaceRects(SDL_Window *window * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetWindowSurface @@ -2171,6 +2410,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_DestroyWindowSurface(SDL_Window *window); * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetWindowKeyboardGrab @@ -2188,9 +2429,13 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowKeyboardGrab(SDL_Window *window, b * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * - * \sa SDL_GetWindowMouseGrab + * \sa SDL_GetWindowMouseRect + * \sa SDL_SetWindowMouseRect + * \sa SDL_SetWindowMouseGrab * \sa SDL_SetWindowKeyboardGrab */ extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowMouseGrab(SDL_Window *window, bool grabbed); @@ -2201,6 +2446,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowMouseGrab(SDL_Window *window, bool * \param window the window to query. * \returns true if keyboard is grabbed, and false otherwise. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_SetWindowKeyboardGrab @@ -2213,8 +2460,13 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetWindowKeyboardGrab(SDL_Window *window); * \param window the window to query. * \returns true if mouse is grabbed, and false otherwise. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * + * \sa SDL_GetWindowMouseRect + * \sa SDL_SetWindowMouseRect + * \sa SDL_SetWindowMouseGrab * \sa SDL_SetWindowKeyboardGrab */ extern SDL_DECLSPEC bool SDLCALL SDL_GetWindowMouseGrab(SDL_Window *window); @@ -2224,6 +2476,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetWindowMouseGrab(SDL_Window *window); * * \returns the window if input is grabbed or NULL otherwise. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_SetWindowMouseGrab @@ -2243,9 +2497,12 @@ extern SDL_DECLSPEC SDL_Window * SDLCALL SDL_GetGrabbedWindow(void); * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetWindowMouseRect + * \sa SDL_GetWindowMouseGrab * \sa SDL_SetWindowMouseGrab */ extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowMouseRect(SDL_Window *window, const SDL_Rect *rect); @@ -2257,9 +2514,13 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowMouseRect(SDL_Window *window, cons * \returns a pointer to the mouse confinement rectangle of a window, or NULL * if there isn't one. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_SetWindowMouseRect + * \sa SDL_GetWindowMouseGrab + * \sa SDL_SetWindowMouseGrab */ extern SDL_DECLSPEC const SDL_Rect * SDLCALL SDL_GetWindowMouseRect(SDL_Window *window); @@ -2276,6 +2537,8 @@ extern SDL_DECLSPEC const SDL_Rect * SDLCALL SDL_GetWindowMouseRect(SDL_Window * * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GetWindowOpacity @@ -2292,6 +2555,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowOpacity(SDL_Window *window, float * \returns the opacity, (0.0f - transparent, 1.0f - opaque), or -1.0f on * failure; call SDL_GetError() for more information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_SetWindowOpacity @@ -2324,6 +2589,8 @@ extern SDL_DECLSPEC float SDLCALL SDL_GetWindowOpacity(SDL_Window *window); * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_SetWindowModal @@ -2341,9 +2608,12 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowParent(SDL_Window *window, SDL_Win * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_SetWindowParent + * \sa SDL_WINDOW_MODAL */ extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowModal(SDL_Window *window, bool modal); @@ -2355,6 +2625,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowModal(SDL_Window *window, bool mod * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. */ extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowFocusable(SDL_Window *window, bool focusable); @@ -2379,6 +2651,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowFocusable(SDL_Window *window, bool * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. */ extern SDL_DECLSPEC bool SDLCALL SDL_ShowWindowSystemMenu(SDL_Window *window, int x, int y); @@ -2386,6 +2660,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_ShowWindowSystemMenu(SDL_Window *window, in /** * Possible return values from the SDL_HitTest callback. * + * \threadsafety This function should only be called on the main thread. + * * \since This enum is available since SDL 3.1.3. * * \sa SDL_HitTest @@ -2456,6 +2732,8 @@ typedef SDL_HitTestResult (SDLCALL *SDL_HitTest)(SDL_Window *win, * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. */ extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowHitTest(SDL_Window *window, SDL_HitTest callback, void *callback_data); @@ -2482,6 +2760,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowHitTest(SDL_Window *window, SDL_Hi * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. */ extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowShape(SDL_Window *window, SDL_Surface *shape); @@ -2494,6 +2774,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowShape(SDL_Window *window, SDL_Surf * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. */ extern SDL_DECLSPEC bool SDLCALL SDL_FlashWindow(SDL_Window *window, SDL_FlashOperation operation); @@ -2506,6 +2788,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_FlashWindow(SDL_Window *window, SDL_FlashOp * * \param window the window to destroy. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_CreatePopupWindow @@ -2524,6 +2808,8 @@ extern SDL_DECLSPEC void SDLCALL SDL_DestroyWindow(SDL_Window *window); * * \returns true if the screensaver is enabled, false if it is disabled. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_DisableScreenSaver @@ -2537,6 +2823,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_ScreenSaverEnabled(void); * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_DisableScreenSaver @@ -2556,6 +2844,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_EnableScreenSaver(void); * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_EnableScreenSaver @@ -2584,6 +2874,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_DisableScreenSaver(void); * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GL_GetProcAddress @@ -2636,6 +2928,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GL_LoadLibrary(const char *path); * \returns a pointer to the named OpenGL function. The returned pointer * should be cast to the appropriate function signature. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GL_ExtensionSupported @@ -2655,6 +2949,8 @@ extern SDL_DECLSPEC SDL_FunctionPointer SDLCALL SDL_GL_GetProcAddress(const char * \returns a pointer to the named EGL function. The returned pointer should * be cast to the appropriate function signature. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_EGL_GetCurrentDisplay @@ -2664,6 +2960,8 @@ extern SDL_DECLSPEC SDL_FunctionPointer SDLCALL SDL_EGL_GetProcAddress(const cha /** * Unload the OpenGL library previously loaded by SDL_GL_LoadLibrary(). * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GL_LoadLibrary @@ -2687,6 +2985,8 @@ extern SDL_DECLSPEC void SDLCALL SDL_GL_UnloadLibrary(void); * \param extension the name of the extension to check. * \returns true if the extension is supported, false otherwise. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. */ extern SDL_DECLSPEC bool SDLCALL SDL_GL_ExtensionSupported(const char *extension); @@ -2694,6 +2994,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GL_ExtensionSupported(const char *extension /** * Reset all previously set OpenGL context attributes to their default values. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GL_GetAttribute @@ -2715,6 +3017,8 @@ extern SDL_DECLSPEC void SDLCALL SDL_GL_ResetAttributes(void); * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GL_GetAttribute @@ -2731,6 +3035,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GL_SetAttribute(SDL_GLAttr attr, int value) * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GL_ResetAttributes @@ -2753,6 +3059,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GL_GetAttribute(SDL_GLAttr attr, int *value * \returns the OpenGL context associated with `window` or NULL on failure; * call SDL_GetError() for more information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GL_DestroyContext @@ -2770,6 +3078,8 @@ extern SDL_DECLSPEC SDL_GLContext SDLCALL SDL_GL_CreateContext(SDL_Window *windo * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GL_CreateContext @@ -2782,6 +3092,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GL_MakeCurrent(SDL_Window *window, SDL_GLCo * \returns the currently active OpenGL window on success or NULL on failure; * call SDL_GetError() for more information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. */ extern SDL_DECLSPEC SDL_Window * SDLCALL SDL_GL_GetCurrentWindow(void); @@ -2792,6 +3104,8 @@ extern SDL_DECLSPEC SDL_Window * SDLCALL SDL_GL_GetCurrentWindow(void); * \returns the currently active OpenGL context or NULL on failure; call * SDL_GetError() for more information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GL_MakeCurrent @@ -2804,6 +3118,8 @@ extern SDL_DECLSPEC SDL_GLContext SDLCALL SDL_GL_GetCurrentContext(void); * \returns the currently active EGL display or NULL on failure; call * SDL_GetError() for more information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. */ extern SDL_DECLSPEC SDL_EGLDisplay SDLCALL SDL_EGL_GetCurrentDisplay(void); @@ -2814,6 +3130,8 @@ extern SDL_DECLSPEC SDL_EGLDisplay SDLCALL SDL_EGL_GetCurrentDisplay(void); * \returns the currently active EGL config or NULL on failure; call * SDL_GetError() for more information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. */ extern SDL_DECLSPEC SDL_EGLConfig SDLCALL SDL_EGL_GetCurrentConfig(void); @@ -2825,6 +3143,8 @@ extern SDL_DECLSPEC SDL_EGLConfig SDLCALL SDL_EGL_GetCurrentConfig(void); * \returns the EGLSurface pointer associated with the window, or NULL on * failure. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. */ extern SDL_DECLSPEC SDL_EGLSurface SDLCALL SDL_EGL_GetWindowSurface(SDL_Window *window); @@ -2845,6 +3165,8 @@ extern SDL_DECLSPEC SDL_EGLSurface SDLCALL SDL_EGL_GetWindowSurface(SDL_Window * * eglCreateContext. May be NULL. * \param userdata a pointer that is passed to the callbacks. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. */ extern SDL_DECLSPEC void SDLCALL SDL_EGL_SetAttributeCallbacks(SDL_EGLAttribArrayCallback platformAttribCallback, @@ -2874,6 +3196,8 @@ extern SDL_DECLSPEC void SDLCALL SDL_EGL_SetAttributeCallbacks(SDL_EGLAttribArra * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GL_GetSwapInterval @@ -2893,6 +3217,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GL_SetSwapInterval(int interval); * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GL_SetSwapInterval @@ -2913,6 +3239,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GL_GetSwapInterval(int *interval); * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. */ extern SDL_DECLSPEC bool SDLCALL SDL_GL_SwapWindow(SDL_Window *window); @@ -2924,6 +3252,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GL_SwapWindow(SDL_Window *window); * \returns true on success or false on failure; call SDL_GetError() for more * information. * + * \threadsafety This function should only be called on the main thread. + * * \since This function is available since SDL 3.1.3. * * \sa SDL_GL_CreateContext diff --git a/libs/SDL3/include/build_config/SDL_build_config.h b/libs/SDL3/include/build_config/SDL_build_config.h index b712b1e3..afd1dbd5 100644 --- a/libs/SDL3/include/build_config/SDL_build_config.h +++ b/libs/SDL3/include/build_config/SDL_build_config.h @@ -45,8 +45,6 @@ #include "SDL_build_config_ios.h" #elif defined(SDL_PLATFORM_ANDROID) #include "SDL_build_config_android.h" -#elif defined(SDL_PLATFORM_NGAGE) -#include "SDL_build_config_ngage.h" #else /* This is a minimal configuration just to get SDL running on new platforms. */ #include "SDL_build_config_minimal.h" diff --git a/libs/SDL3/include/build_config/SDL_build_config.h.cmake b/libs/SDL3/include/build_config/SDL_build_config.h.cmake index e131e9af..9fac7d0f 100644 --- a/libs/SDL3/include/build_config/SDL_build_config.h.cmake +++ b/libs/SDL3/include/build_config/SDL_build_config.h.cmake @@ -31,18 +31,16 @@ /* General platform specific identifiers */ #include -/* C datatypes */ -/* Define SIZEOF_VOIDP for 64/32 architectures */ -#if defined(__LP64__) || defined(_LP64) || defined(_WIN64) -#define SIZEOF_VOIDP 8 -#else -#define SIZEOF_VOIDP 4 -#endif +#cmakedefine HAVE_GCC_ATOMICS 1 +#cmakedefine HAVE_GCC_SYNC_LOCK_TEST_AND_SET 1 -#cmakedefine HAVE_GCC_ATOMICS @HAVE_GCC_ATOMICS@ -#cmakedefine HAVE_GCC_SYNC_LOCK_TEST_AND_SET @HAVE_GCC_SYNC_LOCK_TEST_AND_SET@ +#cmakedefine SDL_DISABLE_ALLOCA 1 -#cmakedefine SDL_DISABLE_ALLOCA +/* Useful headers */ +#cmakedefine HAVE_FLOAT_H 1 +#cmakedefine HAVE_STDARG_H 1 +#cmakedefine HAVE_STDDEF_H 1 +#cmakedefine HAVE_STDINT_H 1 /* Comment this if you want to build without any C library requirements */ #cmakedefine HAVE_LIBC 1 @@ -50,7 +48,6 @@ /* Useful headers */ #cmakedefine HAVE_ALLOCA_H 1 -#cmakedefine HAVE_FLOAT_H 1 #cmakedefine HAVE_ICONV_H 1 #cmakedefine HAVE_INTTYPES_H 1 #cmakedefine HAVE_LIMITS_H 1 @@ -58,10 +55,6 @@ #cmakedefine HAVE_MATH_H 1 #cmakedefine HAVE_MEMORY_H 1 #cmakedefine HAVE_SIGNAL_H 1 -#cmakedefine HAVE_STDARG_H 1 -#cmakedefine HAVE_STDBOOL_H 1 -#cmakedefine HAVE_STDDEF_H 1 -#cmakedefine HAVE_STDINT_H 1 #cmakedefine HAVE_STDIO_H 1 #cmakedefine HAVE_STDLIB_H 1 #cmakedefine HAVE_STRINGS_H 1 @@ -73,10 +66,7 @@ /* C library functions */ #cmakedefine HAVE_DLOPEN 1 #cmakedefine HAVE_MALLOC 1 -#cmakedefine HAVE_CALLOC 1 -#cmakedefine HAVE_REALLOC 1 #cmakedefine HAVE_FDATASYNC 1 -#cmakedefine HAVE_FREE 1 #cmakedefine HAVE_GETENV 1 #cmakedefine HAVE_GETHOSTNAME 1 #cmakedefine HAVE_SETENV 1 @@ -92,8 +82,6 @@ #cmakedefine HAVE_WCSNLEN 1 #cmakedefine HAVE_WCSLCPY 1 #cmakedefine HAVE_WCSLCAT 1 -#cmakedefine HAVE__WCSDUP 1 -#cmakedefine HAVE_WCSDUP 1 #cmakedefine HAVE_WCSSTR 1 #cmakedefine HAVE_WCSCMP 1 #cmakedefine HAVE_WCSNCMP 1 @@ -104,8 +92,6 @@ #cmakedefine HAVE_STRLCAT 1 #cmakedefine HAVE_STRPBRK 1 #cmakedefine HAVE__STRREV 1 -#cmakedefine HAVE__STRUPR 1 -#cmakedefine HAVE__STRLWR 1 #cmakedefine HAVE_INDEX 1 #cmakedefine HAVE_RINDEX 1 #cmakedefine HAVE_STRCHR 1 @@ -128,8 +114,6 @@ #cmakedefine HAVE_ATOF 1 #cmakedefine HAVE_STRCMP 1 #cmakedefine HAVE_STRNCMP 1 -#cmakedefine HAVE_STRCASESTR 1 -#cmakedefine HAVE_SSCANF 1 #cmakedefine HAVE_VSSCANF 1 #cmakedefine HAVE_VSNPRINTF 1 #cmakedefine HAVE_ACOS 1 @@ -144,6 +128,7 @@ #cmakedefine HAVE_CEILF 1 #cmakedefine HAVE_COPYSIGN 1 #cmakedefine HAVE_COPYSIGNF 1 +#cmakedefine HAVE__COPYSIGN 1 #cmakedefine HAVE_COS 1 #cmakedefine HAVE_COSF 1 #cmakedefine HAVE_EXP 1 @@ -182,6 +167,7 @@ #cmakedefine HAVE_TANF 1 #cmakedefine HAVE_TRUNC 1 #cmakedefine HAVE_TRUNCF 1 +#cmakedefine HAVE__FSEEKI64 1 #cmakedefine HAVE_FOPEN64 1 #cmakedefine HAVE_FSEEKO 1 #cmakedefine HAVE_FSEEKO64 1 @@ -209,18 +195,11 @@ #cmakedefine HAVE_POLL 1 #cmakedefine HAVE__EXIT 1 -#else -#cmakedefine HAVE_STDARG_H 1 -#cmakedefine HAVE_STDDEF_H 1 -#cmakedefine HAVE_STDINT_H 1 -#cmakedefine HAVE_FLOAT_H 1 #endif /* HAVE_LIBC */ #cmakedefine HAVE_DBUS_DBUS_H 1 #cmakedefine HAVE_FCITX 1 #cmakedefine HAVE_IBUS_IBUS_H 1 -#cmakedefine HAVE_SYS_INOTIFY_H 1 -#cmakedefine HAVE_INOTIFY_INIT 1 #cmakedefine HAVE_INOTIFY_INIT1 1 #cmakedefine HAVE_INOTIFY 1 #cmakedefine HAVE_LIBUSB 1 @@ -229,183 +208,181 @@ #cmakedefine HAVE_LINUX_INPUT_H 1 #cmakedefine HAVE_LIBUDEV_H 1 #cmakedefine HAVE_LIBDECOR_H 1 +#cmakedefine HAVE_LIBURING_H 1 -#cmakedefine HAVE_D3D11_H @HAVE_D3D11_H@ -#cmakedefine HAVE_DDRAW_H @HAVE_DDRAW_H@ -#cmakedefine HAVE_DSOUND_H @HAVE_DSOUND_H@ -#cmakedefine HAVE_DINPUT_H @HAVE_DINPUT_H@ -#cmakedefine HAVE_XINPUT_H @HAVE_XINPUT_H@ -#cmakedefine HAVE_WINDOWS_GAMING_INPUT_H @HAVE_WINDOWS_GAMING_INPUT_H@ -#cmakedefine HAVE_GAMEINPUT_H @HAVE_GAMEINPUT_H@ -#cmakedefine HAVE_DXGI_H @HAVE_DXGI_H@ -#cmakedefine HAVE_DXGI1_6_H @HAVE_DXGI1_6_H@ +#cmakedefine HAVE_DDRAW_H 1 +#cmakedefine HAVE_DSOUND_H 1 +#cmakedefine HAVE_DINPUT_H 1 +#cmakedefine HAVE_XINPUT_H 1 +#cmakedefine HAVE_WINDOWS_GAMING_INPUT_H 1 +#cmakedefine HAVE_GAMEINPUT_H 1 +#cmakedefine HAVE_DXGI_H 1 +#cmakedefine HAVE_DXGI1_6_H 1 -#cmakedefine HAVE_MMDEVICEAPI_H @HAVE_MMDEVICEAPI_H@ -#cmakedefine HAVE_AUDIOCLIENT_H @HAVE_AUDIOCLIENT_H@ -#cmakedefine HAVE_TPCSHRD_H @HAVE_TPCSHRD_H@ -#cmakedefine HAVE_SENSORSAPI_H @HAVE_SENSORSAPI_H@ -#cmakedefine HAVE_ROAPI_H @HAVE_ROAPI_H@ -#cmakedefine HAVE_SHELLSCALINGAPI_H @HAVE_SHELLSCALINGAPI_H@ +#cmakedefine HAVE_MMDEVICEAPI_H 1 +#cmakedefine HAVE_TPCSHRD_H 1 +#cmakedefine HAVE_ROAPI_H 1 +#cmakedefine HAVE_SHELLSCALINGAPI_H 1 -#cmakedefine USE_POSIX_SPAWN @USE_POSIX_SPAWN@ +#cmakedefine USE_POSIX_SPAWN 1 /* SDL internal assertion support */ -#if @SDL_DEFAULT_ASSERT_LEVEL_CONFIGURED@ -#cmakedefine SDL_DEFAULT_ASSERT_LEVEL @SDL_DEFAULT_ASSERT_LEVEL@ +#cmakedefine SDL_DEFAULT_ASSERT_LEVEL_CONFIGURED 1 +#ifdef SDL_DEFAULT_ASSERT_LEVEL_CONFIGURED +#define SDL_DEFAULT_ASSERT_LEVEL @SDL_DEFAULT_ASSERT_LEVEL@ #endif /* Allow disabling of major subsystems */ -#cmakedefine SDL_AUDIO_DISABLED @SDL_AUDIO_DISABLED@ -#cmakedefine SDL_JOYSTICK_DISABLED @SDL_JOYSTICK_DISABLED@ -#cmakedefine SDL_HAPTIC_DISABLED @SDL_HAPTIC_DISABLED@ -#cmakedefine SDL_HIDAPI_DISABLED @SDL_HIDAPI_DISABLED@ -#cmakedefine SDL_SENSOR_DISABLED @SDL_SENSOR_DISABLED@ -#cmakedefine SDL_RENDER_DISABLED @SDL_RENDER_DISABLED@ -#cmakedefine SDL_THREADS_DISABLED @SDL_THREADS_DISABLED@ -#cmakedefine SDL_VIDEO_DISABLED @SDL_VIDEO_DISABLED@ -#cmakedefine SDL_POWER_DISABLED @SDL_POWER_DISABLED@ -#cmakedefine SDL_CAMERA_DISABLED @SDL_CAMERA_DISABLED@ -#cmakedefine SDL_GPU_DISABLED @SDL_GPU_DISABLED@ +#cmakedefine SDL_AUDIO_DISABLED 1 +#cmakedefine SDL_VIDEO_DISABLED 1 +#cmakedefine SDL_GPU_DISABLED 1 +#cmakedefine SDL_RENDER_DISABLED 1 +#cmakedefine SDL_CAMERA_DISABLED 1 +#cmakedefine SDL_JOYSTICK_DISABLED 1 +#cmakedefine SDL_HAPTIC_DISABLED 1 +#cmakedefine SDL_HIDAPI_DISABLED 1 +#cmakedefine SDL_POWER_DISABLED 1 +#cmakedefine SDL_SENSOR_DISABLED 1 +#cmakedefine SDL_DIALOG_DISABLED 1 +#cmakedefine SDL_THREADS_DISABLED 1 /* Enable various audio drivers */ -#cmakedefine SDL_AUDIO_DRIVER_ALSA @SDL_AUDIO_DRIVER_ALSA@ +#cmakedefine SDL_AUDIO_DRIVER_ALSA 1 #cmakedefine SDL_AUDIO_DRIVER_ALSA_DYNAMIC @SDL_AUDIO_DRIVER_ALSA_DYNAMIC@ -#cmakedefine SDL_AUDIO_DRIVER_OPENSLES @SDL_AUDIO_DRIVER_OPENSLES@ -#cmakedefine SDL_AUDIO_DRIVER_AAUDIO @SDL_AUDIO_DRIVER_AAUDIO@ -#cmakedefine SDL_AUDIO_DRIVER_COREAUDIO @SDL_AUDIO_DRIVER_COREAUDIO@ -#cmakedefine SDL_AUDIO_DRIVER_DISK @SDL_AUDIO_DRIVER_DISK@ -#cmakedefine SDL_AUDIO_DRIVER_DSOUND @SDL_AUDIO_DRIVER_DSOUND@ -#cmakedefine SDL_AUDIO_DRIVER_DUMMY @SDL_AUDIO_DRIVER_DUMMY@ -#cmakedefine SDL_AUDIO_DRIVER_EMSCRIPTEN @SDL_AUDIO_DRIVER_EMSCRIPTEN@ -#cmakedefine SDL_AUDIO_DRIVER_HAIKU @SDL_AUDIO_DRIVER_HAIKU@ -#cmakedefine SDL_AUDIO_DRIVER_JACK @SDL_AUDIO_DRIVER_JACK@ +#cmakedefine SDL_AUDIO_DRIVER_OPENSLES 1 +#cmakedefine SDL_AUDIO_DRIVER_AAUDIO 1 +#cmakedefine SDL_AUDIO_DRIVER_COREAUDIO 1 +#cmakedefine SDL_AUDIO_DRIVER_DISK 1 +#cmakedefine SDL_AUDIO_DRIVER_DSOUND 1 +#cmakedefine SDL_AUDIO_DRIVER_DUMMY 1 +#cmakedefine SDL_AUDIO_DRIVER_EMSCRIPTEN 1 +#cmakedefine SDL_AUDIO_DRIVER_HAIKU 1 +#cmakedefine SDL_AUDIO_DRIVER_JACK 1 #cmakedefine SDL_AUDIO_DRIVER_JACK_DYNAMIC @SDL_AUDIO_DRIVER_JACK_DYNAMIC@ -#cmakedefine SDL_AUDIO_DRIVER_NETBSD @SDL_AUDIO_DRIVER_NETBSD@ -#cmakedefine SDL_AUDIO_DRIVER_OSS @SDL_AUDIO_DRIVER_OSS@ -#cmakedefine SDL_AUDIO_DRIVER_PIPEWIRE @SDL_AUDIO_DRIVER_PIPEWIRE@ +#cmakedefine SDL_AUDIO_DRIVER_NETBSD 1 +#cmakedefine SDL_AUDIO_DRIVER_OSS 1 +#cmakedefine SDL_AUDIO_DRIVER_PIPEWIRE 1 #cmakedefine SDL_AUDIO_DRIVER_PIPEWIRE_DYNAMIC @SDL_AUDIO_DRIVER_PIPEWIRE_DYNAMIC@ -#cmakedefine SDL_AUDIO_DRIVER_PULSEAUDIO @SDL_AUDIO_DRIVER_PULSEAUDIO@ +#cmakedefine SDL_AUDIO_DRIVER_PULSEAUDIO 1 #cmakedefine SDL_AUDIO_DRIVER_PULSEAUDIO_DYNAMIC @SDL_AUDIO_DRIVER_PULSEAUDIO_DYNAMIC@ -#cmakedefine SDL_AUDIO_DRIVER_SNDIO @SDL_AUDIO_DRIVER_SNDIO@ +#cmakedefine SDL_AUDIO_DRIVER_SNDIO 1 #cmakedefine SDL_AUDIO_DRIVER_SNDIO_DYNAMIC @SDL_AUDIO_DRIVER_SNDIO_DYNAMIC@ -#cmakedefine SDL_AUDIO_DRIVER_WASAPI @SDL_AUDIO_DRIVER_WASAPI@ -#cmakedefine SDL_AUDIO_DRIVER_VITA @SDL_AUDIO_DRIVER_VITA@ -#cmakedefine SDL_AUDIO_DRIVER_PSP @SDL_AUDIO_DRIVER_PSP@ -#cmakedefine SDL_AUDIO_DRIVER_PS2 @SDL_AUDIO_DRIVER_PS2@ -#cmakedefine SDL_AUDIO_DRIVER_N3DS @SDL_AUDIO_DRIVER_N3DS@ -#cmakedefine SDL_AUDIO_DRIVER_QNX @SDL_AUDIO_DRIVER_QNX@ +#cmakedefine SDL_AUDIO_DRIVER_WASAPI 1 +#cmakedefine SDL_AUDIO_DRIVER_VITA 1 +#cmakedefine SDL_AUDIO_DRIVER_PSP 1 +#cmakedefine SDL_AUDIO_DRIVER_PS2 1 +#cmakedefine SDL_AUDIO_DRIVER_N3DS 1 +#cmakedefine SDL_AUDIO_DRIVER_QNX 1 /* Enable various input drivers */ -#cmakedefine SDL_INPUT_LINUXEV @SDL_INPUT_LINUXEV@ -#cmakedefine SDL_INPUT_LINUXKD @SDL_INPUT_LINUXKD@ -#cmakedefine SDL_INPUT_FBSDKBIO @SDL_INPUT_FBSDKBIO@ -#cmakedefine SDL_INPUT_WSCONS @SDL_INPUT_WSCONS@ -#cmakedefine SDL_HAVE_MACHINE_JOYSTICK_H @SDL_HAVE_MACHINE_JOYSTICK_H@ -#cmakedefine SDL_JOYSTICK_ANDROID @SDL_JOYSTICK_ANDROID@ -#cmakedefine SDL_JOYSTICK_DINPUT @SDL_JOYSTICK_DINPUT@ -#cmakedefine SDL_JOYSTICK_DUMMY @SDL_JOYSTICK_DUMMY@ -#cmakedefine SDL_JOYSTICK_EMSCRIPTEN @SDL_JOYSTICK_EMSCRIPTEN@ -#cmakedefine SDL_JOYSTICK_GAMEINPUT @SDL_JOYSTICK_GAMEINPUT@ -#cmakedefine SDL_JOYSTICK_HAIKU @SDL_JOYSTICK_HAIKU@ -#cmakedefine SDL_JOYSTICK_HIDAPI @SDL_JOYSTICK_HIDAPI@ -#cmakedefine SDL_JOYSTICK_IOKIT @SDL_JOYSTICK_IOKIT@ -#cmakedefine SDL_JOYSTICK_LINUX @SDL_JOYSTICK_LINUX@ -#cmakedefine SDL_JOYSTICK_MFI @SDL_JOYSTICK_MFI@ -#cmakedefine SDL_JOYSTICK_N3DS @SDL_JOYSTICK_N3DS@ -#cmakedefine SDL_JOYSTICK_PS2 @SDL_JOYSTICK_PS2@ -#cmakedefine SDL_JOYSTICK_PSP @SDL_JOYSTICK_PSP@ -#cmakedefine SDL_JOYSTICK_RAWINPUT @SDL_JOYSTICK_RAWINPUT@ -#cmakedefine SDL_JOYSTICK_USBHID @SDL_JOYSTICK_USBHID@ -#cmakedefine SDL_JOYSTICK_VIRTUAL @SDL_JOYSTICK_VIRTUAL@ -#cmakedefine SDL_JOYSTICK_VITA @SDL_JOYSTICK_VITA@ -#cmakedefine SDL_JOYSTICK_WGI @SDL_JOYSTICK_WGI@ -#cmakedefine SDL_JOYSTICK_XINPUT @SDL_JOYSTICK_XINPUT@ -#cmakedefine SDL_HAPTIC_DUMMY @SDL_HAPTIC_DUMMY@ -#cmakedefine SDL_HAPTIC_LINUX @SDL_HAPTIC_LINUX@ -#cmakedefine SDL_HAPTIC_IOKIT @SDL_HAPTIC_IOKIT@ -#cmakedefine SDL_HAPTIC_DINPUT @SDL_HAPTIC_DINPUT@ -#cmakedefine SDL_HAPTIC_ANDROID @SDL_HAPTIC_ANDROID@ +#cmakedefine SDL_INPUT_LINUXEV 1 +#cmakedefine SDL_INPUT_LINUXKD 1 +#cmakedefine SDL_INPUT_FBSDKBIO 1 +#cmakedefine SDL_INPUT_WSCONS 1 +#cmakedefine SDL_HAVE_MACHINE_JOYSTICK_H 1 +#cmakedefine SDL_JOYSTICK_ANDROID 1 +#cmakedefine SDL_JOYSTICK_DINPUT 1 +#cmakedefine SDL_JOYSTICK_DUMMY 1 +#cmakedefine SDL_JOYSTICK_EMSCRIPTEN 1 +#cmakedefine SDL_JOYSTICK_GAMEINPUT 1 +#cmakedefine SDL_JOYSTICK_HAIKU 1 +#cmakedefine SDL_JOYSTICK_HIDAPI 1 +#cmakedefine SDL_JOYSTICK_IOKIT 1 +#cmakedefine SDL_JOYSTICK_LINUX 1 +#cmakedefine SDL_JOYSTICK_MFI 1 +#cmakedefine SDL_JOYSTICK_N3DS 1 +#cmakedefine SDL_JOYSTICK_PS2 1 +#cmakedefine SDL_JOYSTICK_PSP 1 +#cmakedefine SDL_JOYSTICK_RAWINPUT 1 +#cmakedefine SDL_JOYSTICK_USBHID 1 +#cmakedefine SDL_JOYSTICK_VIRTUAL 1 +#cmakedefine SDL_JOYSTICK_VITA 1 +#cmakedefine SDL_JOYSTICK_WGI 1 +#cmakedefine SDL_JOYSTICK_XINPUT 1 +#cmakedefine SDL_HAPTIC_DUMMY 1 +#cmakedefine SDL_HAPTIC_LINUX 1 +#cmakedefine SDL_HAPTIC_IOKIT 1 +#cmakedefine SDL_HAPTIC_DINPUT 1 +#cmakedefine SDL_HAPTIC_ANDROID 1 #cmakedefine SDL_LIBUSB_DYNAMIC @SDL_LIBUSB_DYNAMIC@ #cmakedefine SDL_UDEV_DYNAMIC @SDL_UDEV_DYNAMIC@ /* Enable various process implementations */ -#cmakedefine SDL_PROCESS_DUMMY @SDL_PROCESS_DUMMY@ -#cmakedefine SDL_PROCESS_POSIX @SDL_PROCESS_POSIX@ -#cmakedefine SDL_PROCESS_WINDOWS @SDL_PROCESS_WINDOWS@ +#cmakedefine SDL_PROCESS_DUMMY 1 +#cmakedefine SDL_PROCESS_POSIX 1 +#cmakedefine SDL_PROCESS_WINDOWS 1 /* Enable various sensor drivers */ -#cmakedefine SDL_SENSOR_ANDROID @SDL_SENSOR_ANDROID@ -#cmakedefine SDL_SENSOR_COREMOTION @SDL_SENSOR_COREMOTION@ -#cmakedefine SDL_SENSOR_WINDOWS @SDL_SENSOR_WINDOWS@ -#cmakedefine SDL_SENSOR_DUMMY @SDL_SENSOR_DUMMY@ -#cmakedefine SDL_SENSOR_VITA @SDL_SENSOR_VITA@ -#cmakedefine SDL_SENSOR_N3DS @SDL_SENSOR_N3DS@ +#cmakedefine SDL_SENSOR_ANDROID 1 +#cmakedefine SDL_SENSOR_COREMOTION 1 +#cmakedefine SDL_SENSOR_WINDOWS 1 +#cmakedefine SDL_SENSOR_DUMMY 1 +#cmakedefine SDL_SENSOR_VITA 1 +#cmakedefine SDL_SENSOR_N3DS 1 /* Enable various shared object loading systems */ -#cmakedefine SDL_LOADSO_DLOPEN @SDL_LOADSO_DLOPEN@ -#cmakedefine SDL_LOADSO_DUMMY @SDL_LOADSO_DUMMY@ -#cmakedefine SDL_LOADSO_LDG @SDL_LOADSO_LDG@ -#cmakedefine SDL_LOADSO_WINDOWS @SDL_LOADSO_WINDOWS@ +#cmakedefine SDL_LOADSO_DLOPEN 1 +#cmakedefine SDL_LOADSO_DUMMY 1 +#cmakedefine SDL_LOADSO_WINDOWS 1 /* Enable various threading systems */ -#cmakedefine SDL_THREAD_GENERIC_COND_SUFFIX @SDL_THREAD_GENERIC_COND_SUFFIX@ -#cmakedefine SDL_THREAD_GENERIC_RWLOCK_SUFFIX @SDL_THREAD_GENERIC_RWLOCK_SUFFIX@ -#cmakedefine SDL_THREAD_PTHREAD @SDL_THREAD_PTHREAD@ -#cmakedefine SDL_THREAD_PTHREAD_RECURSIVE_MUTEX @SDL_THREAD_PTHREAD_RECURSIVE_MUTEX@ -#cmakedefine SDL_THREAD_PTHREAD_RECURSIVE_MUTEX_NP @SDL_THREAD_PTHREAD_RECURSIVE_MUTEX_NP@ -#cmakedefine SDL_THREAD_WINDOWS @SDL_THREAD_WINDOWS@ -#cmakedefine SDL_THREAD_VITA @SDL_THREAD_VITA@ -#cmakedefine SDL_THREAD_PSP @SDL_THREAD_PSP@ -#cmakedefine SDL_THREAD_PS2 @SDL_THREAD_PS2@ -#cmakedefine SDL_THREAD_N3DS @SDL_THREAD_N3DS@ +#cmakedefine SDL_THREAD_GENERIC_COND_SUFFIX 1 +#cmakedefine SDL_THREAD_GENERIC_RWLOCK_SUFFIX 1 +#cmakedefine SDL_THREAD_PTHREAD 1 +#cmakedefine SDL_THREAD_PTHREAD_RECURSIVE_MUTEX 1 +#cmakedefine SDL_THREAD_PTHREAD_RECURSIVE_MUTEX_NP 1 +#cmakedefine SDL_THREAD_WINDOWS 1 +#cmakedefine SDL_THREAD_VITA 1 +#cmakedefine SDL_THREAD_PSP 1 +#cmakedefine SDL_THREAD_PS2 1 +#cmakedefine SDL_THREAD_N3DS 1 /* Enable various RTC systems */ -#cmakedefine SDL_TIME_UNIX @SDL_TIME_UNIX@ -#cmakedefine SDL_TIME_WINDOWS @SDL_TIME_WINDOWS@ -#cmakedefine SDL_TIME_VITA @SDL_TIME_VITA@ -#cmakedefine SDL_TIME_PSP @SDL_TIME_PSP@ -#cmakedefine SDL_TIME_PS2 @SDL_TIME_PS2@ -#cmakedefine SDL_TIME_N3DS @SDL_TIME_N3DS@ +#cmakedefine SDL_TIME_UNIX 1 +#cmakedefine SDL_TIME_WINDOWS 1 +#cmakedefine SDL_TIME_VITA 1 +#cmakedefine SDL_TIME_PSP 1 +#cmakedefine SDL_TIME_PS2 1 +#cmakedefine SDL_TIME_N3DS 1 /* Enable various timer systems */ -#cmakedefine SDL_TIMER_HAIKU @SDL_TIMER_HAIKU@ -#cmakedefine SDL_TIMER_DUMMY @SDL_TIMER_DUMMY@ -#cmakedefine SDL_TIMER_UNIX @SDL_TIMER_UNIX@ -#cmakedefine SDL_TIMER_WINDOWS @SDL_TIMER_WINDOWS@ -#cmakedefine SDL_TIMER_VITA @SDL_TIMER_VITA@ -#cmakedefine SDL_TIMER_PSP @SDL_TIMER_PSP@ -#cmakedefine SDL_TIMER_PS2 @SDL_TIMER_PS2@ -#cmakedefine SDL_TIMER_N3DS @SDL_TIMER_N3DS@ +#cmakedefine SDL_TIMER_HAIKU 1 +#cmakedefine SDL_TIMER_UNIX 1 +#cmakedefine SDL_TIMER_WINDOWS 1 +#cmakedefine SDL_TIMER_VITA 1 +#cmakedefine SDL_TIMER_PSP 1 +#cmakedefine SDL_TIMER_PS2 1 +#cmakedefine SDL_TIMER_N3DS 1 /* Enable various video drivers */ -#cmakedefine SDL_VIDEO_DRIVER_ANDROID @SDL_VIDEO_DRIVER_ANDROID@ -#cmakedefine SDL_VIDEO_DRIVER_COCOA @SDL_VIDEO_DRIVER_COCOA@ -#cmakedefine SDL_VIDEO_DRIVER_DUMMY @SDL_VIDEO_DRIVER_DUMMY@ -#cmakedefine SDL_VIDEO_DRIVER_EMSCRIPTEN @SDL_VIDEO_DRIVER_EMSCRIPTEN@ -#cmakedefine SDL_VIDEO_DRIVER_HAIKU @SDL_VIDEO_DRIVER_HAIKU@ -#cmakedefine SDL_VIDEO_DRIVER_KMSDRM @SDL_VIDEO_DRIVER_KMSDRM@ +#cmakedefine SDL_VIDEO_DRIVER_ANDROID 1 +#cmakedefine SDL_VIDEO_DRIVER_COCOA 1 +#cmakedefine SDL_VIDEO_DRIVER_DUMMY 1 +#cmakedefine SDL_VIDEO_DRIVER_EMSCRIPTEN 1 +#cmakedefine SDL_VIDEO_DRIVER_HAIKU 1 +#cmakedefine SDL_VIDEO_DRIVER_KMSDRM 1 #cmakedefine SDL_VIDEO_DRIVER_KMSDRM_DYNAMIC @SDL_VIDEO_DRIVER_KMSDRM_DYNAMIC@ #cmakedefine SDL_VIDEO_DRIVER_KMSDRM_DYNAMIC_GBM @SDL_VIDEO_DRIVER_KMSDRM_DYNAMIC_GBM@ -#cmakedefine SDL_VIDEO_DRIVER_N3DS @SDL_VIDEO_DRIVER_N3DS@ -#cmakedefine SDL_VIDEO_DRIVER_OFFSCREEN @SDL_VIDEO_DRIVER_OFFSCREEN@ -#cmakedefine SDL_VIDEO_DRIVER_PS2 @SDL_VIDEO_DRIVER_PS2@ -#cmakedefine SDL_VIDEO_DRIVER_PSP @SDL_VIDEO_DRIVER_PSP@ -#cmakedefine SDL_VIDEO_DRIVER_RISCOS @SDL_VIDEO_DRIVER_RISCOS@ -#cmakedefine SDL_VIDEO_DRIVER_ROCKCHIP @SDL_VIDEO_DRIVER_ROCKCHIP@ -#cmakedefine SDL_VIDEO_DRIVER_RPI @SDL_VIDEO_DRIVER_RPI@ -#cmakedefine SDL_VIDEO_DRIVER_UIKIT @SDL_VIDEO_DRIVER_UIKIT@ -#cmakedefine SDL_VIDEO_DRIVER_VITA @SDL_VIDEO_DRIVER_VITA@ -#cmakedefine SDL_VIDEO_DRIVER_VIVANTE @SDL_VIDEO_DRIVER_VIVANTE@ -#cmakedefine SDL_VIDEO_DRIVER_VIVANTE_VDK @SDL_VIDEO_DRIVER_VIVANTE_VDK@ -#cmakedefine SDL_VIDEO_DRIVER_OPENVR @SDL_VIDEO_DRIVER_OPENVR@ -#cmakedefine SDL_VIDEO_DRIVER_WAYLAND @SDL_VIDEO_DRIVER_WAYLAND@ +#cmakedefine SDL_VIDEO_DRIVER_N3DS 1 +#cmakedefine SDL_VIDEO_DRIVER_OFFSCREEN 1 +#cmakedefine SDL_VIDEO_DRIVER_PS2 1 +#cmakedefine SDL_VIDEO_DRIVER_PSP 1 +#cmakedefine SDL_VIDEO_DRIVER_RISCOS 1 +#cmakedefine SDL_VIDEO_DRIVER_ROCKCHIP 1 +#cmakedefine SDL_VIDEO_DRIVER_RPI 1 +#cmakedefine SDL_VIDEO_DRIVER_UIKIT 1 +#cmakedefine SDL_VIDEO_DRIVER_VITA 1 +#cmakedefine SDL_VIDEO_DRIVER_VIVANTE 1 +#cmakedefine SDL_VIDEO_DRIVER_VIVANTE_VDK 1 +#cmakedefine SDL_VIDEO_DRIVER_OPENVR 1 +#cmakedefine SDL_VIDEO_DRIVER_WAYLAND 1 #cmakedefine SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC @SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC@ #cmakedefine SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_CURSOR @SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_CURSOR@ #cmakedefine SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_EGL @SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_EGL@ #cmakedefine SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_LIBDECOR @SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_LIBDECOR@ #cmakedefine SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_XKBCOMMON @SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_XKBCOMMON@ -#cmakedefine SDL_VIDEO_DRIVER_WINDOWS @SDL_VIDEO_DRIVER_WINDOWS@ -#cmakedefine SDL_VIDEO_DRIVER_X11 @SDL_VIDEO_DRIVER_X11@ +#cmakedefine SDL_VIDEO_DRIVER_WINDOWS 1 +#cmakedefine SDL_VIDEO_DRIVER_X11 1 #cmakedefine SDL_VIDEO_DRIVER_X11_DYNAMIC @SDL_VIDEO_DRIVER_X11_DYNAMIC@ #cmakedefine SDL_VIDEO_DRIVER_X11_DYNAMIC_XCURSOR @SDL_VIDEO_DRIVER_X11_DYNAMIC_XCURSOR@ #cmakedefine SDL_VIDEO_DRIVER_X11_DYNAMIC_XEXT @SDL_VIDEO_DRIVER_X11_DYNAMIC_XEXT@ @@ -413,127 +390,120 @@ #cmakedefine SDL_VIDEO_DRIVER_X11_DYNAMIC_XINPUT2 @SDL_VIDEO_DRIVER_X11_DYNAMIC_XINPUT2@ #cmakedefine SDL_VIDEO_DRIVER_X11_DYNAMIC_XRANDR @SDL_VIDEO_DRIVER_X11_DYNAMIC_XRANDR@ #cmakedefine SDL_VIDEO_DRIVER_X11_DYNAMIC_XSS @SDL_VIDEO_DRIVER_X11_DYNAMIC_XSS@ -#cmakedefine SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM @SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM@ -#cmakedefine SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS @SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS@ -#cmakedefine SDL_VIDEO_DRIVER_X11_XCURSOR @SDL_VIDEO_DRIVER_X11_XCURSOR@ -#cmakedefine SDL_VIDEO_DRIVER_X11_XDBE @SDL_VIDEO_DRIVER_X11_XDBE@ -#cmakedefine SDL_VIDEO_DRIVER_X11_XFIXES @SDL_VIDEO_DRIVER_X11_XFIXES@ -#cmakedefine SDL_VIDEO_DRIVER_X11_XINPUT2 @SDL_VIDEO_DRIVER_X11_XINPUT2@ -#cmakedefine SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH @SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH@ -#cmakedefine SDL_VIDEO_DRIVER_X11_XRANDR @SDL_VIDEO_DRIVER_X11_XRANDR@ -#cmakedefine SDL_VIDEO_DRIVER_X11_XSCRNSAVER @SDL_VIDEO_DRIVER_X11_XSCRNSAVER@ -#cmakedefine SDL_VIDEO_DRIVER_X11_XSHAPE @SDL_VIDEO_DRIVER_X11_XSHAPE@ -#cmakedefine SDL_VIDEO_DRIVER_QNX @SDL_VIDEO_DRIVER_QNX@ +#cmakedefine SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM 1 +#cmakedefine SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS 1 +#cmakedefine SDL_VIDEO_DRIVER_X11_XCURSOR 1 +#cmakedefine SDL_VIDEO_DRIVER_X11_XDBE 1 +#cmakedefine SDL_VIDEO_DRIVER_X11_XFIXES 1 +#cmakedefine SDL_VIDEO_DRIVER_X11_XINPUT2 1 +#cmakedefine SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH 1 +#cmakedefine SDL_VIDEO_DRIVER_X11_XRANDR 1 +#cmakedefine SDL_VIDEO_DRIVER_X11_XSCRNSAVER 1 +#cmakedefine SDL_VIDEO_DRIVER_X11_XSHAPE 1 +#cmakedefine SDL_VIDEO_DRIVER_X11_XSYNC 1 +#cmakedefine SDL_VIDEO_DRIVER_QNX 1 -#cmakedefine SDL_VIDEO_RENDER_D3D @SDL_VIDEO_RENDER_D3D@ -#cmakedefine SDL_VIDEO_RENDER_D3D11 @SDL_VIDEO_RENDER_D3D11@ -#cmakedefine SDL_VIDEO_RENDER_D3D12 @SDL_VIDEO_RENDER_D3D12@ -#cmakedefine SDL_VIDEO_RENDER_GPU @SDL_VIDEO_RENDER_GPU@ -#cmakedefine SDL_VIDEO_RENDER_METAL @SDL_VIDEO_RENDER_METAL@ -#cmakedefine SDL_VIDEO_RENDER_VULKAN @SDL_VIDEO_RENDER_VULKAN@ -#cmakedefine SDL_VIDEO_RENDER_OGL @SDL_VIDEO_RENDER_OGL@ -#cmakedefine SDL_VIDEO_RENDER_OGL_ES2 @SDL_VIDEO_RENDER_OGL_ES2@ -#cmakedefine SDL_VIDEO_RENDER_PS2 @SDL_VIDEO_RENDER_PS2@ -#cmakedefine SDL_VIDEO_RENDER_PSP @SDL_VIDEO_RENDER_PSP@ -#cmakedefine SDL_VIDEO_RENDER_VITA_GXM @SDL_VIDEO_RENDER_VITA_GXM@ +#cmakedefine SDL_VIDEO_RENDER_D3D 1 +#cmakedefine SDL_VIDEO_RENDER_D3D11 1 +#cmakedefine SDL_VIDEO_RENDER_D3D12 1 +#cmakedefine SDL_VIDEO_RENDER_GPU 1 +#cmakedefine SDL_VIDEO_RENDER_METAL 1 +#cmakedefine SDL_VIDEO_RENDER_VULKAN 1 +#cmakedefine SDL_VIDEO_RENDER_OGL 1 +#cmakedefine SDL_VIDEO_RENDER_OGL_ES2 1 +#cmakedefine SDL_VIDEO_RENDER_PS2 1 +#cmakedefine SDL_VIDEO_RENDER_PSP 1 +#cmakedefine SDL_VIDEO_RENDER_VITA_GXM 1 /* Enable OpenGL support */ -#cmakedefine SDL_VIDEO_OPENGL @SDL_VIDEO_OPENGL@ -#cmakedefine SDL_VIDEO_OPENGL_ES @SDL_VIDEO_OPENGL_ES@ -#cmakedefine SDL_VIDEO_OPENGL_ES2 @SDL_VIDEO_OPENGL_ES2@ -#cmakedefine SDL_VIDEO_OPENGL_BGL @SDL_VIDEO_OPENGL_BGL@ -#cmakedefine SDL_VIDEO_OPENGL_CGL @SDL_VIDEO_OPENGL_CGL@ -#cmakedefine SDL_VIDEO_OPENGL_GLX @SDL_VIDEO_OPENGL_GLX@ -#cmakedefine SDL_VIDEO_OPENGL_WGL @SDL_VIDEO_OPENGL_WGL@ -#cmakedefine SDL_VIDEO_OPENGL_EGL @SDL_VIDEO_OPENGL_EGL@ -#cmakedefine SDL_VIDEO_OPENGL_OSMESA @SDL_VIDEO_OPENGL_OSMESA@ -#cmakedefine SDL_VIDEO_OPENGL_OSMESA_DYNAMIC @SDL_VIDEO_OPENGL_OSMESA_DYNAMIC@ +#cmakedefine SDL_VIDEO_OPENGL 1 +#cmakedefine SDL_VIDEO_OPENGL_ES 1 +#cmakedefine SDL_VIDEO_OPENGL_ES2 1 +#cmakedefine SDL_VIDEO_OPENGL_CGL 1 +#cmakedefine SDL_VIDEO_OPENGL_GLX 1 +#cmakedefine SDL_VIDEO_OPENGL_WGL 1 +#cmakedefine SDL_VIDEO_OPENGL_EGL 1 /* Enable Vulkan support */ -#cmakedefine SDL_VIDEO_VULKAN @SDL_VIDEO_VULKAN@ +#cmakedefine SDL_VIDEO_VULKAN 1 /* Enable Metal support */ -#cmakedefine SDL_VIDEO_METAL @SDL_VIDEO_METAL@ +#cmakedefine SDL_VIDEO_METAL 1 /* Enable GPU support */ -#cmakedefine SDL_GPU_D3D11 @SDL_GPU_D3D11@ -#cmakedefine SDL_GPU_D3D12 @SDL_GPU_D3D12@ -#cmakedefine SDL_GPU_VULKAN @SDL_GPU_VULKAN@ -#cmakedefine SDL_GPU_METAL @SDL_GPU_METAL@ +#cmakedefine SDL_GPU_D3D11 1 +#cmakedefine SDL_GPU_D3D12 1 +#cmakedefine SDL_GPU_VULKAN 1 +#cmakedefine SDL_GPU_METAL 1 /* Enable system power support */ -#cmakedefine SDL_POWER_ANDROID @SDL_POWER_ANDROID@ -#cmakedefine SDL_POWER_LINUX @SDL_POWER_LINUX@ -#cmakedefine SDL_POWER_WINDOWS @SDL_POWER_WINDOWS@ -#cmakedefine SDL_POWER_MACOSX @SDL_POWER_MACOSX@ -#cmakedefine SDL_POWER_UIKIT @SDL_POWER_UIKIT@ -#cmakedefine SDL_POWER_HAIKU @SDL_POWER_HAIKU@ -#cmakedefine SDL_POWER_EMSCRIPTEN @SDL_POWER_EMSCRIPTEN@ -#cmakedefine SDL_POWER_HARDWIRED @SDL_POWER_HARDWIRED@ -#cmakedefine SDL_POWER_VITA @SDL_POWER_VITA@ -#cmakedefine SDL_POWER_PSP @SDL_POWER_PSP@ -#cmakedefine SDL_POWER_N3DS @SDL_POWER_N3DS@ +#cmakedefine SDL_POWER_ANDROID 1 +#cmakedefine SDL_POWER_LINUX 1 +#cmakedefine SDL_POWER_WINDOWS 1 +#cmakedefine SDL_POWER_MACOSX 1 +#cmakedefine SDL_POWER_UIKIT 1 +#cmakedefine SDL_POWER_HAIKU 1 +#cmakedefine SDL_POWER_EMSCRIPTEN 1 +#cmakedefine SDL_POWER_HARDWIRED 1 +#cmakedefine SDL_POWER_VITA 1 +#cmakedefine SDL_POWER_PSP 1 +#cmakedefine SDL_POWER_N3DS 1 /* Enable system filesystem support */ -#cmakedefine SDL_FILESYSTEM_ANDROID @SDL_FILESYSTEM_ANDROID@ -#cmakedefine SDL_FILESYSTEM_HAIKU @SDL_FILESYSTEM_HAIKU@ -#cmakedefine SDL_FILESYSTEM_COCOA @SDL_FILESYSTEM_COCOA@ -#cmakedefine SDL_FILESYSTEM_DUMMY @SDL_FILESYSTEM_DUMMY@ -#cmakedefine SDL_FILESYSTEM_RISCOS @SDL_FILESYSTEM_RISCOS@ -#cmakedefine SDL_FILESYSTEM_UNIX @SDL_FILESYSTEM_UNIX@ -#cmakedefine SDL_FILESYSTEM_WINDOWS @SDL_FILESYSTEM_WINDOWS@ -#cmakedefine SDL_FILESYSTEM_EMSCRIPTEN @SDL_FILESYSTEM_EMSCRIPTEN@ -#cmakedefine SDL_FILESYSTEM_VITA @SDL_FILESYSTEM_VITA@ -#cmakedefine SDL_FILESYSTEM_PSP @SDL_FILESYSTEM_PSP@ -#cmakedefine SDL_FILESYSTEM_PS2 @SDL_FILESYSTEM_PS2@ -#cmakedefine SDL_FILESYSTEM_N3DS @SDL_FILESYSTEM_N3DS@ +#cmakedefine SDL_FILESYSTEM_ANDROID 1 +#cmakedefine SDL_FILESYSTEM_HAIKU 1 +#cmakedefine SDL_FILESYSTEM_COCOA 1 +#cmakedefine SDL_FILESYSTEM_DUMMY 1 +#cmakedefine SDL_FILESYSTEM_RISCOS 1 +#cmakedefine SDL_FILESYSTEM_UNIX 1 +#cmakedefine SDL_FILESYSTEM_WINDOWS 1 +#cmakedefine SDL_FILESYSTEM_EMSCRIPTEN 1 +#cmakedefine SDL_FILESYSTEM_VITA 1 +#cmakedefine SDL_FILESYSTEM_PSP 1 +#cmakedefine SDL_FILESYSTEM_PS2 1 +#cmakedefine SDL_FILESYSTEM_N3DS 1 /* Enable system storage support */ -#cmakedefine SDL_STORAGE_GENERIC @SDL_STORAGE_GENERIC@ #cmakedefine SDL_STORAGE_STEAM @SDL_STORAGE_STEAM@ /* Enable system FSops support */ -#cmakedefine SDL_FSOPS_POSIX @SDL_FSOPS_POSIX@ -#cmakedefine SDL_FSOPS_WINDOWS @SDL_FSOPS_WINDOWS@ -#cmakedefine SDL_FSOPS_DUMMY @SDL_FSOPS_DUMMY@ +#cmakedefine SDL_FSOPS_POSIX 1 +#cmakedefine SDL_FSOPS_WINDOWS 1 +#cmakedefine SDL_FSOPS_DUMMY 1 /* Enable camera subsystem */ -#cmakedefine SDL_CAMERA_DRIVER_DUMMY @SDL_CAMERA_DRIVER_DUMMY@ -/* !!! FIXME: for later cmakedefine SDL_CAMERA_DRIVER_DISK @SDL_CAMERA_DRIVER_DISK@ */ -#cmakedefine SDL_CAMERA_DRIVER_V4L2 @SDL_CAMERA_DRIVER_V4L2@ -#cmakedefine SDL_CAMERA_DRIVER_COREMEDIA @SDL_CAMERA_DRIVER_COREMEDIA@ -#cmakedefine SDL_CAMERA_DRIVER_ANDROID @SDL_CAMERA_DRIVER_ANDROID@ -#cmakedefine SDL_CAMERA_DRIVER_EMSCRIPTEN @SDL_CAMERA_DRIVER_EMSCRIPTEN@ -#cmakedefine SDL_CAMERA_DRIVER_MEDIAFOUNDATION @SDL_CAMERA_DRIVER_MEDIAFOUNDATION@ -#cmakedefine SDL_CAMERA_DRIVER_PIPEWIRE @SDL_CAMERA_DRIVER_PIPEWIRE@ +#cmakedefine SDL_CAMERA_DRIVER_DUMMY 1 +/* !!! FIXME: for later cmakedefine SDL_CAMERA_DRIVER_DISK 1 */ +#cmakedefine SDL_CAMERA_DRIVER_V4L2 1 +#cmakedefine SDL_CAMERA_DRIVER_COREMEDIA 1 +#cmakedefine SDL_CAMERA_DRIVER_ANDROID 1 +#cmakedefine SDL_CAMERA_DRIVER_EMSCRIPTEN 1 +#cmakedefine SDL_CAMERA_DRIVER_MEDIAFOUNDATION 1 +#cmakedefine SDL_CAMERA_DRIVER_PIPEWIRE 1 #cmakedefine SDL_CAMERA_DRIVER_PIPEWIRE_DYNAMIC @SDL_CAMERA_DRIVER_PIPEWIRE_DYNAMIC@ -#cmakedefine SDL_CAMERA_DRIVER_VITA @SDL_CAMERA_DRIVER_VITA@ +#cmakedefine SDL_CAMERA_DRIVER_VITA 1 /* Enable dialog subsystem */ -#cmakedefine SDL_DIALOG_DUMMY @SDL_DIALOG_DUMMY@ - -/* Enable misc subsystem */ -#cmakedefine SDL_MISC_DUMMY @SDL_MISC_DUMMY@ - -/* Enable locale subsystem */ -#cmakedefine SDL_LOCALE_DUMMY @SDL_LOCALE_DUMMY@ +#cmakedefine SDL_DIALOG_DUMMY 1 /* Enable assembly routines */ -#cmakedefine SDL_ALTIVEC_BLITTERS @SDL_ALTIVEC_BLITTERS@ +#cmakedefine SDL_ALTIVEC_BLITTERS 1 /* Whether SDL_DYNAMIC_API needs dlopen */ -#cmakedefine DYNAPI_NEEDS_DLOPEN @DYNAPI_NEEDS_DLOPEN@ +#cmakedefine DYNAPI_NEEDS_DLOPEN 1 /* Enable ime support */ -#cmakedefine SDL_USE_IME @SDL_USE_IME@ +#cmakedefine SDL_USE_IME 1 +#cmakedefine SDL_DISABLE_WINDOWS_IME 1 +#cmakedefine SDL_GDK_TEXTINPUT 1 /* Platform specific definitions */ -#cmakedefine SDL_IPHONE_KEYBOARD @SDL_IPHONE_KEYBOARD@ -#cmakedefine SDL_IPHONE_LAUNCHSCREEN @SDL_IPHONE_LAUNCHSCREEN@ +#cmakedefine SDL_IPHONE_KEYBOARD 1 +#cmakedefine SDL_IPHONE_LAUNCHSCREEN 1 -#cmakedefine SDL_VIDEO_VITA_PIB @SDL_VIDEO_VITA_PIB@ -#cmakedefine SDL_VIDEO_VITA_PVR @SDL_VIDEO_VITA_PVR@ -#cmakedefine SDL_VIDEO_VITA_PVR_OGL @SDL_VIDEO_VITA_PVR_OGL@ +#cmakedefine SDL_VIDEO_VITA_PIB 1 +#cmakedefine SDL_VIDEO_VITA_PVR 1 +#cmakedefine SDL_VIDEO_VITA_PVR_OGL 1 /* Libdecor version info */ #define SDL_LIBDECOR_VERSION_MAJOR @SDL_LIBDECOR_VERSION_MAJOR@ @@ -552,12 +522,11 @@ typedef unsigned __int32 uint32_t; typedef signed __int64 int64_t; typedef unsigned __int64 uint64_t; #ifndef _UINTPTR_T_DEFINED -#ifdef _WIN64 +#ifdef _WIN64 typedef unsigned __int64 uintptr_t; #else typedef unsigned int uintptr_t; #endif -#define _UINTPTR_T_DEFINED #endif #endif /* Visual Studio 2008 */ #endif /* !_STDINT_H_ && !HAVE_STDINT_H */ diff --git a/libs/SDL3/include/build_config/SDL_build_config_android.h b/libs/SDL3/include/build_config/SDL_build_config_android.h index 34f26270..13bb9469 100644 --- a/libs/SDL3/include/build_config/SDL_build_config_android.h +++ b/libs/SDL3/include/build_config/SDL_build_config_android.h @@ -33,7 +33,7 @@ #include -#define HAVE_GCC_ATOMICS 1 +#define HAVE_GCC_ATOMICS 1 #define HAVE_ALLOCA_H 1 #define HAVE_FLOAT_H 1 @@ -42,7 +42,6 @@ #define HAVE_MATH_H 1 #define HAVE_SIGNAL_H 1 #define HAVE_STDARG_H 1 -#define HAVE_STDBOOL_H 1 #define HAVE_STDDEF_H 1 #define HAVE_STDINT_H 1 #define HAVE_STDIO_H 1 @@ -52,113 +51,109 @@ #define HAVE_WCHAR_H 1 /* C library functions */ -#define HAVE_LIBC 1 +#define HAVE_LIBC 1 #define HAVE_DLOPEN 1 #define HAVE_MALLOC 1 -#define HAVE_CALLOC 1 -#define HAVE_REALLOC 1 #define HAVE_FDATASYNC 1 -#define HAVE_FREE 1 #define HAVE_GETENV 1 #define HAVE_GETHOSTNAME 1 #define HAVE_PUTENV 1 #define HAVE_SETENV 1 -#define HAVE_UNSETENV 1 -#define HAVE_ABS 1 -#define HAVE_BCOPY 1 +#define HAVE_UNSETENV 1 +#define HAVE_ABS 1 +#define HAVE_BCOPY 1 #define HAVE_MEMSET 1 #define HAVE_MEMCPY 1 -#define HAVE_MEMMOVE 1 +#define HAVE_MEMMOVE 1 #define HAVE_MEMCMP 1 #define HAVE_STRLEN 1 -#define HAVE_STRLCPY 1 -#define HAVE_STRLCAT 1 +#define HAVE_STRLCPY 1 +#define HAVE_STRLCAT 1 #define HAVE_STRCHR 1 -#define HAVE_STRRCHR 1 +#define HAVE_STRRCHR 1 #define HAVE_STRSTR 1 #define HAVE_STRTOK_R 1 #define HAVE_STRTOL 1 -#define HAVE_STRTOUL 1 -#define HAVE_STRTOLL 1 -#define HAVE_STRTOULL 1 +#define HAVE_STRTOUL 1 +#define HAVE_STRTOLL 1 +#define HAVE_STRTOULL 1 #define HAVE_STRTOD 1 -#define HAVE_ATOI 1 +#define HAVE_ATOI 1 #define HAVE_ATOF 1 #define HAVE_STRCMP 1 -#define HAVE_STRNCMP 1 -#define HAVE_STRCASESTR 1 +#define HAVE_STRNCMP 1 #define HAVE_VSSCANF 1 -#define HAVE_VSNPRINTF 1 -#define HAVE_ACOS 1 -#define HAVE_ACOSF 1 -#define HAVE_ASIN 1 -#define HAVE_ASINF 1 -#define HAVE_ATAN 1 -#define HAVE_ATANF 1 -#define HAVE_ATAN2 1 +#define HAVE_VSNPRINTF 1 +#define HAVE_ACOS 1 +#define HAVE_ACOSF 1 +#define HAVE_ASIN 1 +#define HAVE_ASINF 1 +#define HAVE_ATAN 1 +#define HAVE_ATANF 1 +#define HAVE_ATAN2 1 #define HAVE_ATAN2F 1 -#define HAVE_CEIL 1 -#define HAVE_CEILF 1 -#define HAVE_COPYSIGN 1 -#define HAVE_COPYSIGNF 1 -#define HAVE_COS 1 -#define HAVE_COSF 1 -#define HAVE_EXP 1 -#define HAVE_EXPF 1 -#define HAVE_FABS 1 -#define HAVE_FABSF 1 -#define HAVE_FLOOR 1 +#define HAVE_CEIL 1 +#define HAVE_CEILF 1 +#define HAVE_COPYSIGN 1 +#define HAVE_COPYSIGNF 1 +#define HAVE_COS 1 +#define HAVE_COSF 1 +#define HAVE_EXP 1 +#define HAVE_EXPF 1 +#define HAVE_FABS 1 +#define HAVE_FABSF 1 +#define HAVE_FLOOR 1 #define HAVE_FLOORF 1 -#define HAVE_FMOD 1 -#define HAVE_FMODF 1 -#define HAVE_ISINF 1 +#define HAVE_FMOD 1 +#define HAVE_FMODF 1 +#define HAVE_ISINF 1 #define HAVE_ISINF_FLOAT_MACRO 1 -#define HAVE_ISNAN 1 +#define HAVE_ISNAN 1 #define HAVE_ISNAN_FLOAT_MACRO 1 -#define HAVE_LOG 1 -#define HAVE_LOGF 1 -#define HAVE_LOG10 1 +#define HAVE_LOG 1 +#define HAVE_LOGF 1 +#define HAVE_LOG10 1 #define HAVE_LOG10F 1 #define HAVE_LROUND 1 #define HAVE_LROUNDF 1 -#define HAVE_MODF 1 -#define HAVE_MODFF 1 -#define HAVE_POW 1 -#define HAVE_POWF 1 -#define HAVE_ROUND 1 +#define HAVE_MODF 1 +#define HAVE_MODFF 1 +#define HAVE_POW 1 +#define HAVE_POWF 1 +#define HAVE_ROUND 1 #define HAVE_ROUNDF 1 #define HAVE_SCALBN 1 -#define HAVE_SCALBNF 1 -#define HAVE_SIN 1 -#define HAVE_SINF 1 -#define HAVE_SQRT 1 -#define HAVE_SQRTF 1 -#define HAVE_TAN 1 -#define HAVE_TANF 1 -#define HAVE_TRUNC 1 -#define HAVE_TRUNCF 1 +#define HAVE_SCALBNF 1 +#define HAVE_SIN 1 +#define HAVE_SINF 1 +#define HAVE_SQRT 1 +#define HAVE_SQRTF 1 +#define HAVE_TAN 1 +#define HAVE_TANF 1 +#define HAVE_TRUNC 1 +#define HAVE_TRUNCF 1 #define HAVE_SIGACTION 1 #define HAVE_SETJMP 1 -#define HAVE_NANOSLEEP 1 +#define HAVE_NANOSLEEP 1 #define HAVE_GMTIME_R 1 #define HAVE_LOCALTIME_R 1 -#define HAVE_SYSCONF 1 -#define HAVE_CLOCK_GETTIME 1 +#define HAVE_SYSCONF 1 +#define HAVE_CLOCK_GETTIME 1 /* Enable various audio drivers */ #ifndef SDL_AUDIO_DISABLED -#define SDL_AUDIO_DRIVER_OPENSLES 1 -#define SDL_AUDIO_DRIVER_AAUDIO 1 +#define SDL_AUDIO_DRIVER_OPENSLES 1 +#define SDL_AUDIO_DRIVER_AAUDIO 1 #endif /* SDL_AUDIO_DISABLED */ /* Enable various input drivers */ #ifndef SDL_JOYSTICK_DISABLED -#define SDL_JOYSTICK_ANDROID 1 -#define SDL_JOYSTICK_HIDAPI 1 -#define SDL_JOYSTICK_VIRTUAL 1 +#define SDL_JOYSTICK_ANDROID 1 +#define SDL_JOYSTICK_HIDAPI 1 +#define SDL_JOYSTICK_VIRTUAL 1 #endif /* SDL_JOYSTICK_DISABLED */ #ifndef SDL_HAPTIC_DISABLED -#define SDL_HAPTIC_ANDROID 1 +#define SDL_HAPTIC_ANDROID 1 #endif /* SDL_HAPTIC_DISABLED */ /* Enable the stub process support */ @@ -166,21 +161,21 @@ /* Enable sensor driver */ #ifndef SDL_SENSOR_DISABLED -#define SDL_SENSOR_ANDROID 1 +#define SDL_SENSOR_ANDROID 1 #endif /* SDL_SENSOR_DISABLED */ /* Enable various shared object loading systems */ -#define SDL_LOADSO_DLOPEN 1 +#define SDL_LOADSO_DLOPEN 1 /* Enable various threading systems */ -#define SDL_THREAD_PTHREAD 1 -#define SDL_THREAD_PTHREAD_RECURSIVE_MUTEX 1 +#define SDL_THREAD_PTHREAD 1 +#define SDL_THREAD_PTHREAD_RECURSIVE_MUTEX 1 /* Enable RTC system */ -#define SDL_TIME_UNIX 1 +#define SDL_TIME_UNIX 1 /* Enable various timer systems */ -#define SDL_TIMER_UNIX 1 +#define SDL_TIMER_UNIX 1 /* Enable various video drivers */ #define SDL_VIDEO_DRIVER_ANDROID 1 @@ -189,7 +184,7 @@ #define SDL_VIDEO_OPENGL_ES 1 #define SDL_VIDEO_OPENGL_ES2 1 #define SDL_VIDEO_OPENGL_EGL 1 -#define SDL_VIDEO_RENDER_OGL_ES2 1 +#define SDL_VIDEO_RENDER_OGL_ES2 1 /* Enable Vulkan support */ #if defined(__ARM_ARCH) && __ARM_ARCH < 7 @@ -205,7 +200,7 @@ #define SDL_POWER_ANDROID 1 /* Enable the filesystem driver */ -#define SDL_FILESYSTEM_ANDROID 1 +#define SDL_FILESYSTEM_ANDROID 1 #define SDL_FSOPS_POSIX 1 /* Enable the camera driver */ diff --git a/libs/SDL3/include/build_config/SDL_build_config_ios.h b/libs/SDL3/include/build_config/SDL_build_config_ios.h index 1c580018..a6b06c53 100644 --- a/libs/SDL3/include/build_config/SDL_build_config_ios.h +++ b/libs/SDL3/include/build_config/SDL_build_config_ios.h @@ -25,7 +25,7 @@ #include -#define HAVE_GCC_ATOMICS 1 +#define HAVE_GCC_ATOMICS 1 #define HAVE_ALLOCA_H 1 #define HAVE_FLOAT_H 1 @@ -34,7 +34,6 @@ #define HAVE_MATH_H 1 #define HAVE_SIGNAL_H 1 #define HAVE_STDARG_H 1 -#define HAVE_STDBOOL_H 1 #define HAVE_STDDEF_H 1 #define HAVE_STDINT_H 1 #define HAVE_STDIO_H 1 @@ -44,105 +43,101 @@ #define HAVE_WCHAR_H 1 /* C library functions */ -#define HAVE_LIBC 1 +#define HAVE_LIBC 1 #define HAVE_DLOPEN 1 #define HAVE_MALLOC 1 -#define HAVE_CALLOC 1 -#define HAVE_REALLOC 1 -#define HAVE_FREE 1 #define HAVE_GETENV 1 #define HAVE_GETHOSTNAME 1 #define HAVE_PUTENV 1 #define HAVE_SETENV 1 -#define HAVE_UNSETENV 1 -#define HAVE_ABS 1 -#define HAVE_BCOPY 1 +#define HAVE_UNSETENV 1 +#define HAVE_ABS 1 +#define HAVE_BCOPY 1 #define HAVE_MEMSET 1 #define HAVE_MEMCPY 1 -#define HAVE_MEMMOVE 1 +#define HAVE_MEMMOVE 1 #define HAVE_MEMCMP 1 #define HAVE_STRLEN 1 -#define HAVE_STRLCPY 1 -#define HAVE_STRLCAT 1 +#define HAVE_STRLCPY 1 +#define HAVE_STRLCAT 1 #define HAVE_STRCHR 1 -#define HAVE_STRRCHR 1 +#define HAVE_STRRCHR 1 #define HAVE_STRSTR 1 #define HAVE_STRTOK_R 1 #define HAVE_STRTOL 1 -#define HAVE_STRTOUL 1 -#define HAVE_STRTOLL 1 -#define HAVE_STRTOULL 1 +#define HAVE_STRTOUL 1 +#define HAVE_STRTOLL 1 +#define HAVE_STRTOULL 1 #define HAVE_STRTOD 1 -#define HAVE_ATOI 1 -#define HAVE_ATOF 1 +#define HAVE_ATOI 1 +#define HAVE_ATOF 1 #define HAVE_STRCMP 1 -#define HAVE_STRNCMP 1 -#define HAVE_STRPBRK 1 -#define HAVE_STRCASESTR 1 +#define HAVE_STRNCMP 1 +#define HAVE_STRPBRK 1 #define HAVE_VSSCANF 1 -#define HAVE_VSNPRINTF 1 -#define HAVE_ACOS 1 -#define HAVE_ACOSF 1 -#define HAVE_ASIN 1 -#define HAVE_ASINF 1 -#define HAVE_ATAN 1 -#define HAVE_ATANF 1 -#define HAVE_ATAN2 1 +#define HAVE_VSNPRINTF 1 +#define HAVE_ACOS 1 +#define HAVE_ACOSF 1 +#define HAVE_ASIN 1 +#define HAVE_ASINF 1 +#define HAVE_ATAN 1 +#define HAVE_ATANF 1 +#define HAVE_ATAN2 1 #define HAVE_ATAN2F 1 -#define HAVE_CEIL 1 -#define HAVE_CEILF 1 -#define HAVE_COPYSIGN 1 -#define HAVE_COPYSIGNF 1 -#define HAVE_COS 1 -#define HAVE_COSF 1 -#define HAVE_EXP 1 -#define HAVE_EXPF 1 -#define HAVE_FABS 1 -#define HAVE_FABSF 1 -#define HAVE_FLOOR 1 +#define HAVE_CEIL 1 +#define HAVE_CEILF 1 +#define HAVE_COPYSIGN 1 +#define HAVE_COPYSIGNF 1 +#define HAVE_COS 1 +#define HAVE_COSF 1 +#define HAVE_EXP 1 +#define HAVE_EXPF 1 +#define HAVE_FABS 1 +#define HAVE_FABSF 1 +#define HAVE_FLOOR 1 #define HAVE_FLOORF 1 -#define HAVE_FMOD 1 -#define HAVE_FMODF 1 -#define HAVE_ISINF 1 +#define HAVE_FMOD 1 +#define HAVE_FMODF 1 +#define HAVE_ISINF 1 #define HAVE_ISINF_FLOAT_MACRO 1 -#define HAVE_ISNAN 1 +#define HAVE_ISNAN 1 #define HAVE_ISNAN_FLOAT_MACRO 1 -#define HAVE_LOG 1 -#define HAVE_LOGF 1 -#define HAVE_LOG10 1 +#define HAVE_LOG 1 +#define HAVE_LOGF 1 +#define HAVE_LOG10 1 #define HAVE_LOG10F 1 #define HAVE_LROUND 1 #define HAVE_LROUNDF 1 -#define HAVE_MODF 1 -#define HAVE_MODFF 1 -#define HAVE_POW 1 -#define HAVE_POWF 1 -#define HAVE_ROUND 1 +#define HAVE_MODF 1 +#define HAVE_MODFF 1 +#define HAVE_POW 1 +#define HAVE_POWF 1 +#define HAVE_ROUND 1 #define HAVE_ROUNDF 1 #define HAVE_SCALBN 1 -#define HAVE_SCALBNF 1 -#define HAVE_SIN 1 -#define HAVE_SINF 1 -#define HAVE_SQRT 1 -#define HAVE_SQRTF 1 -#define HAVE_TAN 1 -#define HAVE_TANF 1 -#define HAVE_TRUNC 1 +#define HAVE_SCALBNF 1 +#define HAVE_SIN 1 +#define HAVE_SINF 1 +#define HAVE_SQRT 1 +#define HAVE_SQRTF 1 +#define HAVE_TAN 1 +#define HAVE_TANF 1 +#define HAVE_TRUNC 1 #define HAVE_TRUNCF 1 -#define HAVE_SIGACTION 1 +#define HAVE_SIGACTION 1 #define HAVE_SETJMP 1 -#define HAVE_NANOSLEEP 1 +#define HAVE_NANOSLEEP 1 #define HAVE_GMTIME_R 1 #define HAVE_LOCALTIME_R 1 #define HAVE_NL_LANGINFO 1 -#define HAVE_SYSCONF 1 +#define HAVE_SYSCONF 1 #define HAVE_SYSCTLBYNAME 1 #define HAVE_O_CLOEXEC 1 /* enable iPhone version of Core Audio driver */ #define SDL_AUDIO_DRIVER_COREAUDIO 1 /* Enable the dummy audio driver (src/audio/dummy/\*.c) */ -#define SDL_AUDIO_DRIVER_DUMMY 1 +#define SDL_AUDIO_DRIVER_DUMMY 1 /* Enable the stub haptic driver (src/haptic/dummy/\*.c) */ #define SDL_HAPTIC_DUMMY 1 @@ -151,40 +146,40 @@ /* Only enable HIDAPI support if you want to support Steam Controllers on iOS and tvOS */ /*#define SDL_JOYSTICK_HIDAPI 1*/ #define SDL_JOYSTICK_MFI 1 -#define SDL_JOYSTICK_VIRTUAL 1 +#define SDL_JOYSTICK_VIRTUAL 1 /* Enable various process implementations */ #define SDL_PROCESS_DUMMY 1 #ifdef SDL_PLATFORM_TVOS -#define SDL_SENSOR_DUMMY 1 +#define SDL_SENSOR_DUMMY 1 #else /* Enable the CoreMotion sensor driver */ -#define SDL_SENSOR_COREMOTION 1 +#define SDL_SENSOR_COREMOTION 1 #endif /* Enable Unix style SO loading */ #define SDL_LOADSO_DLOPEN 1 /* Enable various threading systems */ -#define SDL_THREAD_PTHREAD 1 -#define SDL_THREAD_PTHREAD_RECURSIVE_MUTEX 1 +#define SDL_THREAD_PTHREAD 1 +#define SDL_THREAD_PTHREAD_RECURSIVE_MUTEX 1 /* Enable various RTC system */ -#define SDL_TIME_UNIX 1 +#define SDL_TIME_UNIX 1 /* Enable various timer systems */ -#define SDL_TIMER_UNIX 1 +#define SDL_TIMER_UNIX 1 /* Supported video drivers */ -#define SDL_VIDEO_DRIVER_UIKIT 1 -#define SDL_VIDEO_DRIVER_DUMMY 1 +#define SDL_VIDEO_DRIVER_UIKIT 1 +#define SDL_VIDEO_DRIVER_DUMMY 1 /* Enable OpenGL ES */ #if !TARGET_OS_MACCATALYST && !TARGET_OS_VISION #define SDL_VIDEO_OPENGL_ES2 1 #define SDL_VIDEO_OPENGL_ES 1 -#define SDL_VIDEO_RENDER_OGL_ES2 1 +#define SDL_VIDEO_RENDER_OGL_ES2 1 #endif /* Metal supported on 64-bit devices running iOS 8.0 and tvOS 9.0 and newer @@ -215,7 +210,7 @@ #define SDL_IPHONE_LAUNCHSCREEN 1 /* enable filesystem support */ -#define SDL_FILESYSTEM_COCOA 1 +#define SDL_FILESYSTEM_COCOA 1 #define SDL_FSOPS_POSIX 1 /* enable camera support */ diff --git a/libs/SDL3/include/build_config/SDL_build_config_macos.h b/libs/SDL3/include/build_config/SDL_build_config_macos.h index 08bbd81d..d14d708a 100644 --- a/libs/SDL3/include/build_config/SDL_build_config_macos.h +++ b/libs/SDL3/include/build_config/SDL_build_config_macos.h @@ -34,12 +34,10 @@ #define HAVE_ALLOCA_H 1 #define HAVE_FLOAT_H 1 #define HAVE_INTTYPES_H 1 -#define HAVE_LIBUNWIND_H 1 #define HAVE_LIMITS_H 1 #define HAVE_MATH_H 1 #define HAVE_SIGNAL_H 1 #define HAVE_STDARG_H 1 -#define HAVE_STDBOOL_H 1 #define HAVE_STDDEF_H 1 #define HAVE_STDINT_H 1 #define HAVE_STDIO_H 1 @@ -49,98 +47,94 @@ #define HAVE_WCHAR_H 1 /* C library functions */ -#define HAVE_LIBC 1 +#define HAVE_LIBC 1 #define HAVE_DLOPEN 1 #define HAVE_MALLOC 1 -#define HAVE_CALLOC 1 -#define HAVE_REALLOC 1 -#define HAVE_FREE 1 #define HAVE_GETENV 1 #define HAVE_GETHOSTNAME 1 #define HAVE_SETENV 1 #define HAVE_PUTENV 1 -#define HAVE_UNSETENV 1 -#define HAVE_ABS 1 -#define HAVE_BCOPY 1 +#define HAVE_UNSETENV 1 +#define HAVE_ABS 1 +#define HAVE_BCOPY 1 #define HAVE_MEMSET 1 #define HAVE_MEMCPY 1 -#define HAVE_MEMMOVE 1 +#define HAVE_MEMMOVE 1 #define HAVE_MEMCMP 1 #define HAVE_STRLEN 1 -#define HAVE_STRLCPY 1 -#define HAVE_STRLCAT 1 -#define HAVE_STRPBRK 1 +#define HAVE_STRLCPY 1 +#define HAVE_STRLCAT 1 +#define HAVE_STRPBRK 1 #define HAVE_STRCHR 1 -#define HAVE_STRRCHR 1 +#define HAVE_STRRCHR 1 #define HAVE_STRSTR 1 #define HAVE_STRTOK_R 1 #define HAVE_STRTOL 1 -#define HAVE_STRTOUL 1 -#define HAVE_STRTOLL 1 -#define HAVE_STRTOULL 1 +#define HAVE_STRTOUL 1 +#define HAVE_STRTOLL 1 +#define HAVE_STRTOULL 1 #define HAVE_STRTOD 1 -#define HAVE_ATOI 1 -#define HAVE_ATOF 1 +#define HAVE_ATOI 1 +#define HAVE_ATOF 1 #define HAVE_STRCMP 1 -#define HAVE_STRNCMP 1 -#define HAVE_STRCASESTR 1 +#define HAVE_STRNCMP 1 #define HAVE_VSSCANF 1 -#define HAVE_VSNPRINTF 1 -#define HAVE_ACOS 1 -#define HAVE_ACOSF 1 -#define HAVE_ASIN 1 -#define HAVE_ASINF 1 -#define HAVE_ATAN 1 -#define HAVE_ATANF 1 -#define HAVE_ATAN2 1 +#define HAVE_VSNPRINTF 1 +#define HAVE_ACOS 1 +#define HAVE_ACOSF 1 +#define HAVE_ASIN 1 +#define HAVE_ASINF 1 +#define HAVE_ATAN 1 +#define HAVE_ATANF 1 +#define HAVE_ATAN2 1 #define HAVE_ATAN2F 1 -#define HAVE_CEIL 1 -#define HAVE_CEILF 1 -#define HAVE_COPYSIGN 1 -#define HAVE_COPYSIGNF 1 -#define HAVE_COS 1 -#define HAVE_COSF 1 -#define HAVE_EXP 1 -#define HAVE_EXPF 1 -#define HAVE_FABS 1 -#define HAVE_FABSF 1 -#define HAVE_FLOOR 1 +#define HAVE_CEIL 1 +#define HAVE_CEILF 1 +#define HAVE_COPYSIGN 1 +#define HAVE_COPYSIGNF 1 +#define HAVE_COS 1 +#define HAVE_COSF 1 +#define HAVE_EXP 1 +#define HAVE_EXPF 1 +#define HAVE_FABS 1 +#define HAVE_FABSF 1 +#define HAVE_FLOOR 1 #define HAVE_FLOORF 1 -#define HAVE_FMOD 1 -#define HAVE_FMODF 1 -#define HAVE_ISINF 1 +#define HAVE_FMOD 1 +#define HAVE_FMODF 1 +#define HAVE_ISINF 1 #define HAVE_ISINF_FLOAT_MACRO 1 -#define HAVE_ISNAN 1 +#define HAVE_ISNAN 1 #define HAVE_ISNAN_FLOAT_MACRO 1 -#define HAVE_LOG 1 -#define HAVE_LOGF 1 -#define HAVE_LOG10 1 +#define HAVE_LOG 1 +#define HAVE_LOGF 1 +#define HAVE_LOG10 1 #define HAVE_LOG10F 1 #define HAVE_LROUND 1 #define HAVE_LROUNDF 1 -#define HAVE_MODF 1 -#define HAVE_MODFF 1 -#define HAVE_POW 1 -#define HAVE_POWF 1 -#define HAVE_ROUND 1 +#define HAVE_MODF 1 +#define HAVE_MODFF 1 +#define HAVE_POW 1 +#define HAVE_POWF 1 +#define HAVE_ROUND 1 #define HAVE_ROUNDF 1 #define HAVE_SCALBN 1 -#define HAVE_SCALBNF 1 -#define HAVE_SIN 1 -#define HAVE_SINF 1 -#define HAVE_SQRT 1 -#define HAVE_SQRTF 1 -#define HAVE_TAN 1 -#define HAVE_TANF 1 -#define HAVE_TRUNC 1 -#define HAVE_TRUNCF 1 -#define HAVE_SIGACTION 1 +#define HAVE_SCALBNF 1 +#define HAVE_SIN 1 +#define HAVE_SINF 1 +#define HAVE_SQRT 1 +#define HAVE_SQRTF 1 +#define HAVE_TAN 1 +#define HAVE_TANF 1 +#define HAVE_TRUNC 1 +#define HAVE_TRUNCF 1 +#define HAVE_SIGACTION 1 #define HAVE_SETJMP 1 -#define HAVE_NANOSLEEP 1 +#define HAVE_NANOSLEEP 1 #define HAVE_GMTIME_R 1 #define HAVE_LOCALTIME_R 1 #define HAVE_NL_LANGINFO 1 -#define HAVE_SYSCONF 1 +#define HAVE_SYSCONF 1 #define HAVE_SYSCTLBYNAME 1 #if defined(__has_include) && (defined(__i386__) || defined(__x86_64)) @@ -156,15 +150,15 @@ #define HAVE_GCC_ATOMICS 1 /* Enable various audio drivers */ -#define SDL_AUDIO_DRIVER_COREAUDIO 1 -#define SDL_AUDIO_DRIVER_DISK 1 -#define SDL_AUDIO_DRIVER_DUMMY 1 +#define SDL_AUDIO_DRIVER_COREAUDIO 1 +#define SDL_AUDIO_DRIVER_DISK 1 +#define SDL_AUDIO_DRIVER_DUMMY 1 /* Enable various input drivers */ #define SDL_JOYSTICK_HIDAPI 1 -#define SDL_JOYSTICK_IOKIT 1 -#define SDL_JOYSTICK_VIRTUAL 1 -#define SDL_HAPTIC_IOKIT 1 +#define SDL_JOYSTICK_IOKIT 1 +#define SDL_JOYSTICK_VIRTUAL 1 +#define SDL_HAPTIC_IOKIT 1 /* The MFI controller support requires ARC Objective C runtime */ #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1080 && !defined(__i386__) @@ -175,24 +169,24 @@ #define SDL_PROCESS_POSIX 1 /* Enable the dummy sensor driver */ -#define SDL_SENSOR_DUMMY 1 +#define SDL_SENSOR_DUMMY 1 /* Enable various shared object loading systems */ -#define SDL_LOADSO_DLOPEN 1 +#define SDL_LOADSO_DLOPEN 1 /* Enable various threading systems */ -#define SDL_THREAD_PTHREAD 1 -#define SDL_THREAD_PTHREAD_RECURSIVE_MUTEX 1 +#define SDL_THREAD_PTHREAD 1 +#define SDL_THREAD_PTHREAD_RECURSIVE_MUTEX 1 /* Enable various RTC system */ -#define SDL_TIME_UNIX 1 +#define SDL_TIME_UNIX 1 /* Enable various timer systems */ -#define SDL_TIMER_UNIX 1 +#define SDL_TIMER_UNIX 1 /* Enable various video drivers */ -#define SDL_VIDEO_DRIVER_COCOA 1 -#define SDL_VIDEO_DRIVER_DUMMY 1 +#define SDL_VIDEO_DRIVER_COCOA 1 +#define SDL_VIDEO_DRIVER_DUMMY 1 #define SDL_VIDEO_DRIVER_OFFSCREEN 1 #undef SDL_VIDEO_DRIVER_X11 #define SDL_VIDEO_DRIVER_X11_DYNAMIC "/opt/X11/lib/libX11.6.dylib" @@ -216,7 +210,7 @@ #define SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS 1 #endif -#define SDL_VIDEO_RENDER_OGL 1 +#define SDL_VIDEO_RENDER_OGL 1 #define SDL_VIDEO_RENDER_OGL_ES2 1 /* Metal only supported on 64-bit architectures with 10.11+ */ @@ -248,7 +242,7 @@ #define SDL_POWER_MACOSX 1 /* enable filesystem support */ -#define SDL_FILESYSTEM_COCOA 1 +#define SDL_FILESYSTEM_COCOA 1 #define SDL_FSOPS_POSIX 1 /* enable camera support */ @@ -257,7 +251,7 @@ /* Enable assembly routines */ #ifdef __ppc__ -#define SDL_ALTIVEC_BLITTERS 1 +#define SDL_ALTIVEC_BLITTERS 1 #endif #endif /* SDL_build_config_macos_h_ */ diff --git a/libs/SDL3/include/build_config/SDL_build_config_minimal.h b/libs/SDL3/include/build_config/SDL_build_config_minimal.h index 962dee35..6ae04eb3 100644 --- a/libs/SDL3/include/build_config/SDL_build_config_minimal.h +++ b/libs/SDL3/include/build_config/SDL_build_config_minimal.h @@ -31,8 +31,8 @@ * This is the minimal configuration that can be used to build SDL. */ -#define HAVE_STDARG_H 1 -#define HAVE_STDDEF_H 1 +#define HAVE_STDARG_H 1 +#define HAVE_STDDEF_H 1 #if !defined(HAVE_STDINT_H) && !defined(_STDINT_H_) /* Most everything except Visual Studio 2008 and earlier has stdint.h now */ @@ -51,7 +51,6 @@ typedef unsigned __int64 uintptr_t; #else typedef unsigned int uintptr_t; #endif -#define _UINTPTR_T_DEFINED #endif #else #define HAVE_STDINT_H 1 @@ -63,10 +62,10 @@ typedef unsigned int uintptr_t; #endif /* Enable the dummy audio driver (src/audio/dummy/\*.c) */ -#define SDL_AUDIO_DRIVER_DUMMY 1 +#define SDL_AUDIO_DRIVER_DUMMY 1 /* Enable the stub joystick driver (src/joystick/dummy/\*.c) */ -#define SDL_JOYSTICK_DISABLED 1 +#define SDL_JOYSTICK_DISABLED 1 /* Enable the stub haptic driver (src/haptic/dummy/\*.c) */ #define SDL_HAPTIC_DISABLED 1 @@ -84,17 +83,17 @@ typedef unsigned int uintptr_t; #define SDL_LOADSO_DUMMY 1 /* Enable the stub thread support (src/thread/generic/\*.c) */ -#define SDL_THREADS_DISABLED 1 +#define SDL_THREADS_DISABLED 1 /* Enable the dummy video driver (src/video/dummy/\*.c) */ -#define SDL_VIDEO_DRIVER_DUMMY 1 +#define SDL_VIDEO_DRIVER_DUMMY 1 /* Enable the dummy filesystem driver (src/filesystem/dummy/\*.c) */ -#define SDL_FILESYSTEM_DUMMY 1 +#define SDL_FILESYSTEM_DUMMY 1 #define SDL_FSOPS_DUMMY 1 /* Enable the camera driver (src/camera/dummy/\*.c) */ -#define SDL_CAMERA_DRIVER_DUMMY 1 +#define SDL_CAMERA_DRIVER_DUMMY 1 /* Enable dialog subsystem */ #define SDL_DIALOG_DUMMY 1 diff --git a/libs/SDL3/include/build_config/SDL_build_config_ngage.h b/libs/SDL3/include/build_config/SDL_build_config_ngage.h deleted file mode 100644 index 9ccf75a8..00000000 --- a/libs/SDL3/include/build_config/SDL_build_config_ngage.h +++ /dev/null @@ -1,99 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2024 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ - -#ifndef SDL_build_config_ngage_h_ -#define SDL_build_config_ngage_h_ -#define SDL_build_config_h_ - -#include - -typedef signed char int8_t; -typedef unsigned char uint8_t; -typedef signed short int16_t; -typedef unsigned short uint16_t; -typedef signed int int32_t; -typedef unsigned int uint32_t; -typedef signed long long int64_t; -typedef unsigned long long uint64_t; -typedef unsigned long uintptr_t; - -#define HAVE_STDARG_H 1 -#define HAVE_STDDEF_H 1 -#define HAVE_STDIO_H 1 -#define HAVE_STDLIB_H 1 -#define HAVE_MATH_H 1 -#define HAVE_CEIL 1 -#define HAVE_COPYSIGN 1 -#define HAVE_COS 1 -#define HAVE_EXP 1 -#define HAVE_FABS 1 -#define HAVE_FLOOR 1 -#define HAVE_LOG 1 -#define HAVE_LOG10 1 -#define HAVE_SCALBN 1 -#define HAVE_SIN 1 -#define HAVE_SQRT 1 -#define HAVE_TAN 1 -#define HAVE_MALLOC 1 -#define SDL_MAIN_NEEDED 1 -#define LACKS_SYS_MMAN_H 1 - -/* Enable the N-Gage thread support (src/thread/ngage/\*.c) */ -#define SDL_THREAD_NGAGE 1 - -/* Enable the N-Gage timer support (src/timer/ngage/\*.c) */ -#define SDL_TIMER_NGAGE 1 - -/* Enable the N-Gage video driver (src/video/ngage/\*.c) */ -#define SDL_VIDEO_DRIVER_NGAGE 1 - -/* Enable the dummy audio driver (src/audio/dummy/\*.c) */ -#define SDL_AUDIO_DRIVER_DUMMY 1 - -/* Enable the stub joystick driver (src/joystick/dummy/\*.c) */ -#define SDL_JOYSTICK_DISABLED 1 - -/* Enable the stub haptic driver (src/haptic/dummy/\*.c) */ -#define SDL_HAPTIC_DISABLED 1 - -/* Enable the stub HIDAPI */ -#define SDL_HIDAPI_DISABLED 1 - -/* Enable the stub process support */ -#define SDL_PROCESS_DUMMY 1 - -/* Enable the stub sensor driver (src/sensor/dummy/\*.c) */ -#define SDL_SENSOR_DISABLED 1 - -/* Enable the dummy shared object loader (src/loadso/dummy/\*.c) */ -#define SDL_LOADSO_DUMMY 1 - -/* Enable the dummy filesystem driver (src/filesystem/dummy/\*.c) */ -#define SDL_FILESYSTEM_DUMMY 1 -#define SDL_FSOPS_DUMMY 1 - -/* Enable the camera driver (src/camera/dummy/\*.c) */ -#define SDL_CAMERA_DRIVER_DUMMY 1 - -/* Enable dialog subsystem */ -#define SDL_DIALOG_DUMMY 1 - -#endif /* SDL_build_config_ngage_h_ */ diff --git a/libs/SDL3/include/build_config/SDL_build_config_windows.h b/libs/SDL3/include/build_config/SDL_build_config_windows.h index 24e21f88..fd7cc0cb 100644 --- a/libs/SDL3/include/build_config/SDL_build_config_windows.h +++ b/libs/SDL3/include/build_config/SDL_build_config_windows.h @@ -69,7 +69,6 @@ typedef unsigned __int64 uintptr_t; #else typedef unsigned int uintptr_t; #endif -#define _UINTPTR_T_DEFINED #endif #else #define HAVE_STDINT_H 1 @@ -114,7 +113,7 @@ typedef unsigned int uintptr_t; /* This can be disabled to avoid C runtime dependencies and manifest requirements */ #ifndef HAVE_LIBC -#define HAVE_LIBC 1 +#define HAVE_LIBC 1 #endif #if HAVE_LIBC @@ -124,7 +123,6 @@ typedef unsigned int uintptr_t; #define HAVE_MATH_H 1 #define HAVE_SIGNAL_H 1 #define HAVE_STDARG_H 1 -#define HAVE_STDBOOL_H 1 #define HAVE_STDDEF_H 1 #define HAVE_STDIO_H 1 #define HAVE_STDLIB_H 1 @@ -133,9 +131,6 @@ typedef unsigned int uintptr_t; /* C library functions */ #define HAVE_MALLOC 1 -#define HAVE_CALLOC 1 -#define HAVE_REALLOC 1 -#define HAVE_FREE 1 #define HAVE_ABS 1 #define HAVE_MEMSET 1 #define HAVE_MEMCPY 1 @@ -143,9 +138,6 @@ typedef unsigned int uintptr_t; #define HAVE_MEMCMP 1 #define HAVE_STRLEN 1 #define HAVE__STRREV 1 -/* These functions have security warnings, so we won't use them */ -/* #undef HAVE__STRUPR */ -/* #undef HAVE__STRLWR */ #define HAVE_STRCHR 1 #define HAVE_STRRCHR 1 #define HAVE_STRSTR 1 @@ -161,47 +153,45 @@ typedef unsigned int uintptr_t; #define HAVE_STRCMP 1 #define HAVE_STRNCMP 1 #define HAVE_STRPBRK 1 -#define HAVE__WCSDUP 1 -#define HAVE_SSCANF 1 #define HAVE_VSSCANF 1 #define HAVE_VSNPRINTF 1 -#define HAVE_ACOS 1 -#define HAVE_ASIN 1 -#define HAVE_ATAN 1 -#define HAVE_ATAN2 1 -#define HAVE_CEIL 1 -#define HAVE_COS 1 -#define HAVE_EXP 1 -#define HAVE_FABS 1 -#define HAVE_FLOOR 1 -#define HAVE_FMOD 1 -#define HAVE_ISINF 1 +#define HAVE_ACOS 1 +#define HAVE_ASIN 1 +#define HAVE_ATAN 1 +#define HAVE_ATAN2 1 +#define HAVE_CEIL 1 +#define HAVE_COS 1 +#define HAVE_EXP 1 +#define HAVE_FABS 1 +#define HAVE_FLOOR 1 +#define HAVE_FMOD 1 +#define HAVE_ISINF 1 #define HAVE_ISINF_FLOAT_MACRO 1 -#define HAVE_ISNAN 1 +#define HAVE_ISNAN 1 #define HAVE_ISNAN_FLOAT_MACRO 1 -#define HAVE_LOG 1 -#define HAVE_LOG10 1 -#define HAVE_POW 1 -#define HAVE_SIN 1 -#define HAVE_SQRT 1 -#define HAVE_TAN 1 -#define HAVE_ACOSF 1 -#define HAVE_ASINF 1 -#define HAVE_ATANF 1 +#define HAVE_LOG 1 +#define HAVE_LOG10 1 +#define HAVE_POW 1 +#define HAVE_SIN 1 +#define HAVE_SQRT 1 +#define HAVE_TAN 1 +#define HAVE_ACOSF 1 +#define HAVE_ASINF 1 +#define HAVE_ATANF 1 #define HAVE_ATAN2F 1 -#define HAVE_CEILF 1 +#define HAVE_CEILF 1 #define HAVE__COPYSIGN 1 -#define HAVE_COSF 1 -#define HAVE_EXPF 1 -#define HAVE_FABSF 1 +#define HAVE_COSF 1 +#define HAVE_EXPF 1 +#define HAVE_FABSF 1 #define HAVE_FLOORF 1 -#define HAVE_FMODF 1 -#define HAVE_LOGF 1 +#define HAVE_FMODF 1 +#define HAVE_LOGF 1 #define HAVE_LOG10F 1 -#define HAVE_POWF 1 -#define HAVE_SINF 1 -#define HAVE_SQRTF 1 -#define HAVE_TANF 1 +#define HAVE_POWF 1 +#define HAVE_SINF 1 +#define HAVE_SQRTF 1 +#define HAVE_TANF 1 #ifdef _MSC_VER /* These functions were added with the VC++ 2013 C runtime library */ #if _MSC_VER >= 1800 @@ -214,7 +204,7 @@ typedef unsigned int uintptr_t; #define HAVE_ROUNDF 1 #define HAVE_SCALBN 1 #define HAVE_SCALBNF 1 -#define HAVE_TRUNC 1 +#define HAVE_TRUNC 1 #define HAVE_TRUNCF 1 #endif /* This function is available with at least the VC++ 2008 C runtime library */ @@ -223,8 +213,8 @@ typedef unsigned int uintptr_t; #endif #endif /* _MSC_VER */ #else -#define HAVE_STDARG_H 1 -#define HAVE_STDDEF_H 1 +#define HAVE_STDARG_H 1 +#define HAVE_STDDEF_H 1 #endif /* Enable various audio drivers */ @@ -232,9 +222,8 @@ typedef unsigned int uintptr_t; #define SDL_AUDIO_DRIVER_WASAPI 1 #endif #define SDL_AUDIO_DRIVER_DSOUND 1 -#define SDL_AUDIO_DRIVER_WINMM 1 -#define SDL_AUDIO_DRIVER_DISK 1 -#define SDL_AUDIO_DRIVER_DUMMY 1 +#define SDL_AUDIO_DRIVER_DISK 1 +#define SDL_AUDIO_DRIVER_DUMMY 1 /* Enable various input drivers */ #define SDL_JOYSTICK_DINPUT 1 @@ -242,37 +231,37 @@ typedef unsigned int uintptr_t; #define SDL_JOYSTICK_GAMEINPUT 1 #endif #define SDL_JOYSTICK_HIDAPI 1 -#define SDL_JOYSTICK_RAWINPUT 1 -#define SDL_JOYSTICK_VIRTUAL 1 +#define SDL_JOYSTICK_RAWINPUT 1 +#define SDL_JOYSTICK_VIRTUAL 1 #ifdef HAVE_WINDOWS_GAMING_INPUT_H -#define SDL_JOYSTICK_WGI 1 +#define SDL_JOYSTICK_WGI 1 #endif #define SDL_JOYSTICK_XINPUT 1 -#define SDL_HAPTIC_DINPUT 1 +#define SDL_HAPTIC_DINPUT 1 /* Enable various process implementations */ #define SDL_PROCESS_WINDOWS 1 /* Enable the sensor driver */ #ifdef HAVE_SENSORSAPI_H -#define SDL_SENSOR_WINDOWS 1 +#define SDL_SENSOR_WINDOWS 1 #else -#define SDL_SENSOR_DUMMY 1 +#define SDL_SENSOR_DUMMY 1 #endif /* Enable various shared object loading systems */ -#define SDL_LOADSO_WINDOWS 1 +#define SDL_LOADSO_WINDOWS 1 /* Enable various threading systems */ #define SDL_THREAD_GENERIC_COND_SUFFIX 1 #define SDL_THREAD_GENERIC_RWLOCK_SUFFIX 1 -#define SDL_THREAD_WINDOWS 1 +#define SDL_THREAD_WINDOWS 1 /* Enable RTC system */ -#define SDL_TIME_WINDOWS 1 +#define SDL_TIME_WINDOWS 1 /* Enable various timer systems */ -#define SDL_TIMER_WINDOWS 1 +#define SDL_TIMER_WINDOWS 1 /* Enable various video drivers */ #define SDL_VIDEO_DRIVER_DUMMY 1 @@ -308,7 +297,7 @@ typedef unsigned int uintptr_t; #define SDL_POWER_WINDOWS 1 /* Enable filesystem support */ -#define SDL_FILESYSTEM_WINDOWS 1 +#define SDL_FILESYSTEM_WINDOWS 1 #define SDL_FSOPS_WINDOWS 1 /* Enable the camera driver */ diff --git a/libs/SDL3/include/build_config/SDL_build_config_wingdk.h b/libs/SDL3/include/build_config/SDL_build_config_wingdk.h index 5b7ebf1d..d26e2cb0 100644 --- a/libs/SDL3/include/build_config/SDL_build_config_wingdk.h +++ b/libs/SDL3/include/build_config/SDL_build_config_wingdk.h @@ -63,7 +63,6 @@ #define HAVE_MATH_H 1 #define HAVE_SIGNAL_H 1 #define HAVE_STDARG_H 1 -#define HAVE_STDBOOL_H 1 #define HAVE_STDDEF_H 1 #define HAVE_STDINT_H 1 #define HAVE_STDIO_H 1 @@ -72,11 +71,8 @@ #define HAVE_WCHAR_H 1 /* C library functions */ -#define HAVE_LIBC 1 +#define HAVE_LIBC 1 #define HAVE_MALLOC 1 -#define HAVE_CALLOC 1 -#define HAVE_REALLOC 1 -#define HAVE_FREE 1 #define HAVE_ABS 1 #define HAVE_MEMSET 1 #define HAVE_MEMCPY 1 @@ -84,9 +80,6 @@ #define HAVE_MEMCMP 1 #define HAVE_STRLEN 1 #define HAVE__STRREV 1 -/* These functions have security warnings, so we won't use them */ -/* #undef HAVE__STRUPR */ -/* #undef HAVE__STRLWR */ #define HAVE_STRCHR 1 #define HAVE_STRRCHR 1 #define HAVE_STRSTR 1 @@ -101,44 +94,43 @@ #define HAVE_ATOF 1 #define HAVE_STRCMP 1 #define HAVE_STRNCMP 1 -#define HAVE__WCSDUP 1 -#define HAVE_ACOS 1 -#define HAVE_ASIN 1 -#define HAVE_ATAN 1 -#define HAVE_ATAN2 1 -#define HAVE_CEIL 1 -#define HAVE_COS 1 -#define HAVE_EXP 1 -#define HAVE_FABS 1 -#define HAVE_FLOOR 1 -#define HAVE_FMOD 1 -#define HAVE_ISINF 1 +#define HAVE_ACOS 1 +#define HAVE_ASIN 1 +#define HAVE_ATAN 1 +#define HAVE_ATAN2 1 +#define HAVE_CEIL 1 +#define HAVE_COS 1 +#define HAVE_EXP 1 +#define HAVE_FABS 1 +#define HAVE_FLOOR 1 +#define HAVE_FMOD 1 +#define HAVE_ISINF 1 #define HAVE_ISINF_FLOAT_MACRO 1 -#define HAVE_ISNAN 1 +#define HAVE_ISNAN 1 #define HAVE_ISNAN_FLOAT_MACRO 1 -#define HAVE_LOG 1 -#define HAVE_LOG10 1 -#define HAVE_POW 1 -#define HAVE_SIN 1 -#define HAVE_SQRT 1 -#define HAVE_TAN 1 -#define HAVE_ACOSF 1 -#define HAVE_ASINF 1 -#define HAVE_ATANF 1 +#define HAVE_LOG 1 +#define HAVE_LOG10 1 +#define HAVE_POW 1 +#define HAVE_SIN 1 +#define HAVE_SQRT 1 +#define HAVE_TAN 1 +#define HAVE_ACOSF 1 +#define HAVE_ASINF 1 +#define HAVE_ATANF 1 #define HAVE_ATAN2F 1 -#define HAVE_CEILF 1 +#define HAVE_CEILF 1 #define HAVE__COPYSIGN 1 -#define HAVE_COSF 1 -#define HAVE_EXPF 1 -#define HAVE_FABSF 1 +#define HAVE_COSF 1 +#define HAVE_EXPF 1 +#define HAVE_FABSF 1 #define HAVE_FLOORF 1 -#define HAVE_FMODF 1 -#define HAVE_LOGF 1 +#define HAVE_FMODF 1 +#define HAVE_LOGF 1 #define HAVE_LOG10F 1 -#define HAVE_POWF 1 -#define HAVE_SINF 1 -#define HAVE_SQRTF 1 -#define HAVE_TANF 1 +#define HAVE_POWF 1 +#define HAVE_SINF 1 +#define HAVE_SQRTF 1 +#define HAVE_TANF 1 #ifdef _MSC_VER /* These functions were added with the VC++ 2013 C runtime library */ #define HAVE_STRTOLL 1 @@ -150,7 +142,7 @@ #define HAVE_ROUNDF 1 #define HAVE_SCALBN 1 #define HAVE_SCALBNF 1 -#define HAVE_TRUNC 1 +#define HAVE_TRUNC 1 #define HAVE_TRUNCF 1 #define HAVE__FSEEKI64 1 #endif /* _MSC_VER */ @@ -160,43 +152,42 @@ #define SDL_AUDIO_DRIVER_WASAPI 1 #endif #define SDL_AUDIO_DRIVER_DSOUND 1 -#define SDL_AUDIO_DRIVER_WINMM 1 -#define SDL_AUDIO_DRIVER_DISK 1 -#define SDL_AUDIO_DRIVER_DUMMY 1 +#define SDL_AUDIO_DRIVER_DISK 1 +#define SDL_AUDIO_DRIVER_DUMMY 1 /* Enable various input drivers */ #define SDL_JOYSTICK_DINPUT 1 #define SDL_JOYSTICK_GAMEINPUT 1 #define SDL_JOYSTICK_HIDAPI 1 -#define SDL_JOYSTICK_RAWINPUT 1 -#define SDL_JOYSTICK_VIRTUAL 1 -#define SDL_JOYSTICK_WGI 1 +#define SDL_JOYSTICK_RAWINPUT 1 +#define SDL_JOYSTICK_VIRTUAL 1 +#define SDL_JOYSTICK_WGI 1 #define SDL_JOYSTICK_XINPUT 1 -#define SDL_HAPTIC_DINPUT 1 +#define SDL_HAPTIC_DINPUT 1 /* Enable various process implementations */ #define SDL_PROCESS_WINDOWS 1 /* Enable the sensor driver */ #ifdef HAVE_SENSORSAPI_H -#define SDL_SENSOR_WINDOWS 1 +#define SDL_SENSOR_WINDOWS 1 #else -#define SDL_SENSOR_DUMMY 1 +#define SDL_SENSOR_DUMMY 1 #endif /* Enable various shared object loading systems */ -#define SDL_LOADSO_WINDOWS 1 +#define SDL_LOADSO_WINDOWS 1 /* Enable various threading systems */ #define SDL_THREAD_GENERIC_COND_SUFFIX 1 #define SDL_THREAD_GENERIC_RWLOCK_SUFFIX 1 -#define SDL_THREAD_WINDOWS 1 +#define SDL_THREAD_WINDOWS 1 /* Enable various time systems */ -#define SDL_TIME_WINDOWS 1 +#define SDL_TIME_WINDOWS 1 /* Enable various timer systems */ -#define SDL_TIMER_WINDOWS 1 +#define SDL_TIMER_WINDOWS 1 /* Enable various video drivers */ #define SDL_VIDEO_DRIVER_DUMMY 1 @@ -231,11 +222,11 @@ #define SDL_POWER_WINDOWS 1 /* Enable filesystem support */ -#define SDL_FILESYSTEM_WINDOWS 1 +#define SDL_FILESYSTEM_WINDOWS 1 #define SDL_FSOPS_WINDOWS 1 /* Enable the camera driver (src/camera/dummy/\*.c) */ /* !!! FIXME */ -#define SDL_CAMERA_DRIVER_DUMMY 1 +#define SDL_CAMERA_DRIVER_DUMMY 1 /* Use the (inferior) GDK text input method for GDK platforms */ /*#define SDL_GDK_TEXTINPUT 1*/ diff --git a/libs/SDL3/include/build_config/SDL_build_config_xbox.h b/libs/SDL3/include/build_config/SDL_build_config_xbox.h index 82b4f3c1..a79db916 100644 --- a/libs/SDL3/include/build_config/SDL_build_config_xbox.h +++ b/libs/SDL3/include/build_config/SDL_build_config_xbox.h @@ -39,7 +39,6 @@ /* #define HAVE_DXGI_H 1 */ #define HAVE_XINPUT_H 1 /*#define HAVE_WINDOWS_GAMING_INPUT_H 1*/ -/*#define HAVE_D3D11_H 1*/ /*#define HAVE_ROAPI_H 1*/ /*#define HAVE_SHELLSCALINGAPI_H 1*/ #define HAVE_MMDEVICEAPI_H 1 @@ -62,7 +61,6 @@ #define HAVE_MATH_H 1 #define HAVE_SIGNAL_H 1 #define HAVE_STDARG_H 1 -#define HAVE_STDBOOL_H 1 #define HAVE_STDDEF_H 1 #define HAVE_STDINT_H 1 #define HAVE_STDIO_H 1 @@ -71,11 +69,8 @@ #define HAVE_WCHAR_H 1 /* C library functions */ -#define HAVE_LIBC 1 +#define HAVE_LIBC 1 #define HAVE_MALLOC 1 -#define HAVE_CALLOC 1 -#define HAVE_REALLOC 1 -#define HAVE_FREE 1 #define HAVE_ABS 1 #define HAVE_MEMSET 1 #define HAVE_MEMCPY 1 @@ -84,9 +79,6 @@ #define HAVE_STRLEN 1 #define HAVE_STRPBRK 1 #define HAVE__STRREV 1 -/* These functions have security warnings, so we won't use them */ -/* #undef HAVE__STRUPR */ -/* #undef HAVE__STRLWR */ #define HAVE_STRCHR 1 #define HAVE_STRRCHR 1 #define HAVE_STRSTR 1 @@ -101,44 +93,43 @@ #define HAVE_ATOF 1 #define HAVE_STRCMP 1 #define HAVE_STRNCMP 1 -#define HAVE__WCSDUP 1 -#define HAVE_ACOS 1 -#define HAVE_ASIN 1 -#define HAVE_ATAN 1 -#define HAVE_ATAN2 1 -#define HAVE_CEIL 1 -#define HAVE_COS 1 -#define HAVE_EXP 1 -#define HAVE_FABS 1 -#define HAVE_FLOOR 1 -#define HAVE_FMOD 1 -#define HAVE_ISINF 1 +#define HAVE_ACOS 1 +#define HAVE_ASIN 1 +#define HAVE_ATAN 1 +#define HAVE_ATAN2 1 +#define HAVE_CEIL 1 +#define HAVE_COS 1 +#define HAVE_EXP 1 +#define HAVE_FABS 1 +#define HAVE_FLOOR 1 +#define HAVE_FMOD 1 +#define HAVE_ISINF 1 #define HAVE_ISINF_FLOAT_MACRO 1 -#define HAVE_ISNAN 1 +#define HAVE_ISNAN 1 #define HAVE_ISNAN_FLOAT_MACRO 1 -#define HAVE_LOG 1 -#define HAVE_LOG10 1 -#define HAVE_POW 1 -#define HAVE_SIN 1 -#define HAVE_SQRT 1 -#define HAVE_TAN 1 -#define HAVE_ACOSF 1 -#define HAVE_ASINF 1 -#define HAVE_ATANF 1 +#define HAVE_LOG 1 +#define HAVE_LOG10 1 +#define HAVE_POW 1 +#define HAVE_SIN 1 +#define HAVE_SQRT 1 +#define HAVE_TAN 1 +#define HAVE_ACOSF 1 +#define HAVE_ASINF 1 +#define HAVE_ATANF 1 #define HAVE_ATAN2F 1 -#define HAVE_CEILF 1 +#define HAVE_CEILF 1 #define HAVE__COPYSIGN 1 -#define HAVE_COSF 1 -#define HAVE_EXPF 1 -#define HAVE_FABSF 1 +#define HAVE_COSF 1 +#define HAVE_EXPF 1 +#define HAVE_FABSF 1 #define HAVE_FLOORF 1 -#define HAVE_FMODF 1 -#define HAVE_LOGF 1 +#define HAVE_FMODF 1 +#define HAVE_LOGF 1 #define HAVE_LOG10F 1 -#define HAVE_POWF 1 -#define HAVE_SINF 1 -#define HAVE_SQRTF 1 -#define HAVE_TANF 1 +#define HAVE_POWF 1 +#define HAVE_SINF 1 +#define HAVE_SQRTF 1 +#define HAVE_TANF 1 #ifdef _MSC_VER /* These functions were added with the VC++ 2013 C runtime library */ #define HAVE_STRTOLL 1 @@ -150,7 +141,7 @@ #define HAVE_ROUNDF 1 #define HAVE_SCALBN 1 #define HAVE_SCALBNF 1 -#define HAVE_TRUNC 1 +#define HAVE_TRUNC 1 #define HAVE_TRUNCF 1 #define HAVE__FSEEKI64 1 #endif /* _MSC_VER */ @@ -160,12 +151,11 @@ #define SDL_AUDIO_DRIVER_WASAPI 1 #endif /*#define SDL_AUDIO_DRIVER_DSOUND 1*/ -/*#define SDL_AUDIO_DRIVER_WINMM 1*/ -#define SDL_AUDIO_DRIVER_DISK 1 -#define SDL_AUDIO_DRIVER_DUMMY 1 +#define SDL_AUDIO_DRIVER_DISK 1 +#define SDL_AUDIO_DRIVER_DUMMY 1 /* Enable various input drivers */ -#define SDL_JOYSTICK_VIRTUAL 1 +#define SDL_JOYSTICK_VIRTUAL 1 /* This is XInputOnGameInput for GDK platforms: */ /*#define SDL_JOYSTICK_XINPUT 1*/ /* Native GameInput: */ @@ -177,24 +167,24 @@ /* Enable the sensor driver */ #ifdef HAVE_SENSORSAPI_H -#define SDL_SENSOR_WINDOWS 1 +#define SDL_SENSOR_WINDOWS 1 #else -#define SDL_SENSOR_DUMMY 1 +#define SDL_SENSOR_DUMMY 1 #endif /* Enable various shared object loading systems */ -#define SDL_LOADSO_WINDOWS 1 +#define SDL_LOADSO_WINDOWS 1 /* Enable various threading systems */ #define SDL_THREAD_GENERIC_COND_SUFFIX 1 #define SDL_THREAD_GENERIC_RWLOCK_SUFFIX 1 -#define SDL_THREAD_WINDOWS 1 +#define SDL_THREAD_WINDOWS 1 /* Enable various time systems */ -#define SDL_TIME_WINDOWS 1 +#define SDL_TIME_WINDOWS 1 /* Enable various timer systems */ -#define SDL_TIMER_WINDOWS 1 +#define SDL_TIMER_WINDOWS 1 /* Enable various video drivers */ #define SDL_VIDEO_DRIVER_DUMMY 1 @@ -216,7 +206,6 @@ /* Enable filesystem support */ /* #define SDL_FILESYSTEM_WINDOWS 1*/ -#define SDL_FILESYSTEM_XBOX 1 #define SDL_FSOPS_WINDOWS 1 @@ -226,7 +215,7 @@ #define SDL_GDK_TEXTINPUT 1 /* Enable the camera driver (src/camera/dummy/\*.c) */ -#define SDL_CAMERA_DRIVER_DUMMY 1 +#define SDL_CAMERA_DRIVER_DUMMY 1 /* Enable dialog subsystem */ #define SDL_DIALOG_DUMMY 1 diff --git a/libs/SDL3/src/SDL.c b/libs/SDL3/src/SDL.c index 0fdf9713..c99ee401 100644 --- a/libs/SDL3/src/SDL.c +++ b/libs/SDL3/src/SDL.c @@ -55,6 +55,7 @@ #include "video/SDL_surface_c.h" #include "video/SDL_video_c.h" #include "filesystem/SDL_filesystem_c.h" +#include "file/SDL_asyncio_c.h" #ifdef SDL_PLATFORM_ANDROID #include "core/android/SDL_android.h" #endif @@ -182,6 +183,7 @@ static bool SDL_MainIsReady = false; #else static bool SDL_MainIsReady = true; #endif +static SDL_ThreadID SDL_MainThreadID = 0; static bool SDL_bInMainQuit = false; static Uint8 SDL_SubsystemRefCount[32]; @@ -249,6 +251,22 @@ static bool SDL_InitOrIncrementSubsystem(Uint32 subsystem) void SDL_SetMainReady(void) { SDL_MainIsReady = true; + + if (SDL_MainThreadID == 0) { + SDL_MainThreadID = SDL_GetCurrentThreadID(); + } +} + +bool SDL_IsMainThread(void) +{ + if (SDL_MainThreadID == 0) { + // Not initialized yet? + return true; + } + if (SDL_MainThreadID == SDL_GetCurrentThreadID()) { + return true; + } + return false; } // Initialize all the subsystems that require initialization before threads start @@ -329,6 +347,11 @@ bool SDL_InitSubSystem(SDL_InitFlags flags) goto quit_and_error; } + // We initialize video on the main thread + // On Apple platforms this is a requirement. + // On other platforms, this is the definition. + SDL_MainThreadID = SDL_GetCurrentThreadID(); + SDL_IncrementSubsystemRefCount(SDL_INIT_VIDEO); if (!SDL_VideoInit(NULL)) { SDL_DecrementSubsystemRefCount(SDL_INIT_VIDEO); @@ -625,6 +648,7 @@ void SDL_Quit(void) #endif SDL_QuitTimers(); + SDL_QuitAsyncIO(); SDL_SetObjectsInvalid(); SDL_AssertionsQuit(); @@ -718,8 +742,6 @@ const char *SDL_GetPlatform(void) return "PlayStation Portable"; #elif defined(SDL_PLATFORM_VITA) return "PlayStation Vita"; -#elif defined(SDL_PLATFORM_NGAGE) - return "Nokia N-Gage"; #elif defined(SDL_PLATFORM_3DS) return "Nintendo 3DS"; #elif defined(__managarm__) diff --git a/libs/SDL3/src/SDL_hashtable.c b/libs/SDL3/src/SDL_hashtable.c index c77f55fe..192121a3 100644 --- a/libs/SDL3/src/SDL_hashtable.c +++ b/libs/SDL3/src/SDL_hashtable.c @@ -44,6 +44,7 @@ SDL_COMPILE_TIME_ASSERT(sizeof_SDL_HashItem, sizeof(SDL_HashItem) <= MAX_HASHITE struct SDL_HashTable { + SDL_RWLock *lock; SDL_HashItem *table; SDL_HashTable_HashFn hash; SDL_HashTable_KeyMatchFn keymatch; @@ -60,6 +61,7 @@ SDL_HashTable *SDL_CreateHashTable(void *data, SDL_HashTable_HashFn hashfn, SDL_HashTable_KeyMatchFn keymatchfn, SDL_HashTable_NukeFn nukefn, + bool threadsafe, bool stackable) { SDL_HashTable *table; @@ -80,9 +82,14 @@ SDL_HashTable *SDL_CreateHashTable(void *data, return NULL; } + if (threadsafe) { + // Don't fail if we can't create a lock (single threaded environment?) + table->lock = SDL_CreateRWLock(); + } + table->table = (SDL_HashItem *)SDL_calloc(num_buckets, sizeof(SDL_HashItem)); if (!table->table) { - SDL_free(table); + SDL_DestroyHashTable(table); return NULL; } @@ -289,17 +296,22 @@ bool SDL_InsertIntoHashTable(SDL_HashTable *table, const void *key, const void * { SDL_HashItem *item; Uint32 hash; + bool result = false; if (!table) { return false; } + if (table->lock) { + SDL_LockRWLockForWriting(table->lock); + } + hash = calc_hash(table, key); item = find_first_item(table, key, hash); if (item && !table->stackable) { - // TODO: Maybe allow overwrites? We could do it more efficiently here than unset followed by insert. - return false; + // Allow overwrites, this might have been inserted on another thread + delete_item(table, item); } SDL_HashItem new_item; @@ -313,18 +325,25 @@ bool SDL_InsertIntoHashTable(SDL_HashTable *table, const void *key, const void * if (!maybe_resize(table)) { table->num_occupied_slots--; - return false; + goto done; } // This never returns NULL insert_item(&new_item, table->table, table->hash_mask, &table->max_probe_len); - return true; + result = true; + +done: + if (table->lock) { + SDL_UnlockRWLock(table->lock); + } + return result; } bool SDL_FindInHashTable(const SDL_HashTable *table, const void *key, const void **value) { Uint32 hash; SDL_HashItem *i; + bool result = false; if (!table) { if (value) { @@ -333,26 +352,39 @@ bool SDL_FindInHashTable(const SDL_HashTable *table, const void *key, const void return false; } + if (table->lock) { + SDL_LockRWLockForReading(table->lock); + } + hash = calc_hash(table, key); i = find_first_item(table, key, hash); if (i) { if (value) { *value = i->value; } - return true; + result = true; } - return false; + + if (table->lock) { + SDL_UnlockRWLock(table->lock); + } + return result; } bool SDL_RemoveFromHashTable(SDL_HashTable *table, const void *key) { Uint32 hash; SDL_HashItem *item; + bool result = false; if (!table) { return false; } + if (table->lock) { + SDL_LockRWLockForWriting(table->lock); + } + // FIXME: what to do for stacking hashtables? // The original code removes just one item. // This hashtable happens to preserve the insertion order of multi-value keys, @@ -361,13 +393,18 @@ bool SDL_RemoveFromHashTable(SDL_HashTable *table, const void *key) hash = calc_hash(table, key); item = find_first_item(table, key, hash); - if (!item) { - return false; + goto done; } delete_item(table, item); - return true; + result = true; + +done: + if (table->lock) { + SDL_UnlockRWLock(table->lock); + } + return result; } bool SDL_IterateHashTableKey(const SDL_HashTable *table, const void *key, const void **_value, void **iter) @@ -472,22 +509,25 @@ static void nuke_all(SDL_HashTable *table) void SDL_EmptyHashTable(SDL_HashTable *table) { if (table) { - if (table->nuke) { - nuke_all(table); - } + SDL_LockRWLockForWriting(table->lock); + { + if (table->nuke) { + nuke_all(table); + } - SDL_memset(table->table, 0, sizeof(*table->table) * (table->hash_mask + 1)); - table->num_occupied_slots = 0; + SDL_memset(table->table, 0, sizeof(*table->table) * (table->hash_mask + 1)); + table->num_occupied_slots = 0; + } + SDL_UnlockRWLock(table->lock); } } void SDL_DestroyHashTable(SDL_HashTable *table) { if (table) { - if (table->nuke) { - nuke_all(table); - } + SDL_EmptyHashTable(table); + SDL_DestroyRWLock(table->lock); SDL_free(table->table); SDL_free(table); } diff --git a/libs/SDL3/src/SDL_hashtable.h b/libs/SDL3/src/SDL_hashtable.h index 62ff9c79..5d0f8105 100644 --- a/libs/SDL3/src/SDL_hashtable.h +++ b/libs/SDL3/src/SDL_hashtable.h @@ -34,19 +34,33 @@ extern SDL_HashTable *SDL_CreateHashTable(void *data, SDL_HashTable_HashFn hashfn, SDL_HashTable_KeyMatchFn keymatchfn, SDL_HashTable_NukeFn nukefn, + bool threadsafe, bool stackable); +// This function is thread-safe if the hashtable was created with threadsafe = true extern void SDL_EmptyHashTable(SDL_HashTable *table); + +// This function is not thread-safe. extern void SDL_DestroyHashTable(SDL_HashTable *table); + +// This function is thread-safe if the hashtable was created with threadsafe = true extern bool SDL_InsertIntoHashTable(SDL_HashTable *table, const void *key, const void *value); + +// This function is thread-safe if the hashtable was created with threadsafe = true extern bool SDL_RemoveFromHashTable(SDL_HashTable *table, const void *key); + +// This function is thread-safe if the hashtable was created with threadsafe = true extern bool SDL_FindInHashTable(const SDL_HashTable *table, const void *key, const void **_value); + +// This function is thread-safe if the hashtable was created with threadsafe = true extern bool SDL_HashTableEmpty(SDL_HashTable *table); // iterate all values for a specific key. This only makes sense if the hash is stackable. If not-stackable, just use SDL_FindInHashTable(). +// This function is not thread-safe, you should use external locking if you use this function extern bool SDL_IterateHashTableKey(const SDL_HashTable *table, const void *key, const void **_value, void **iter); // iterate all key/value pairs in a hash (stackable hashes can have duplicate keys with multiple values). +// This function is not thread-safe, you should use external locking if you use this function extern bool SDL_IterateHashTable(const SDL_HashTable *table, const void **_key, const void **_value, void **iter); extern Uint32 SDL_HashPointer(const void *key, void *unused); diff --git a/libs/SDL3/src/SDL_internal.h b/libs/SDL3/src/SDL_internal.h index 75fa957a..8364c1ac 100644 --- a/libs/SDL3/src/SDL_internal.h +++ b/libs/SDL3/src/SDL_internal.h @@ -127,68 +127,64 @@ #endif /* A few #defines to reduce SDL footprint. - Only effective when library is statically linked. - You have to manually edit this file. */ -#ifndef SDL_LEAN_AND_MEAN -#define SDL_LEAN_AND_MEAN 0 -#endif + Only effective when library is statically linked. */ /* Optimized functions from 'SDL_blit_0.c' - blit with source bits_per_pixel < 8, palette */ -#ifndef SDL_HAVE_BLIT_0 -#define SDL_HAVE_BLIT_0 !SDL_LEAN_AND_MEAN +#if !defined(SDL_HAVE_BLIT_0) && !defined(SDL_LEAN_AND_MEAN) +#define SDL_HAVE_BLIT_0 1 #endif /* Optimized functions from 'SDL_blit_1.c' - blit with source bytes_per_pixel == 1, palette */ -#ifndef SDL_HAVE_BLIT_1 -#define SDL_HAVE_BLIT_1 !SDL_LEAN_AND_MEAN +#if !defined(SDL_HAVE_BLIT_1) && !defined(SDL_LEAN_AND_MEAN) +#define SDL_HAVE_BLIT_1 1 #endif /* Optimized functions from 'SDL_blit_A.c' - blit with 'SDL_BLENDMODE_BLEND' blending mode */ -#ifndef SDL_HAVE_BLIT_A -#define SDL_HAVE_BLIT_A !SDL_LEAN_AND_MEAN +#if !defined(SDL_HAVE_BLIT_A) && !defined(SDL_LEAN_AND_MEAN) +#define SDL_HAVE_BLIT_A 1 #endif /* Optimized functions from 'SDL_blit_N.c' - blit with COLORKEY mode, or nothing */ -#ifndef SDL_HAVE_BLIT_N -#define SDL_HAVE_BLIT_N !SDL_LEAN_AND_MEAN +#if !defined(SDL_HAVE_BLIT_N) && !defined(SDL_LEAN_AND_MEAN) +#define SDL_HAVE_BLIT_N 1 #endif /* Optimized functions from 'SDL_blit_N.c' - RGB565 conversion with Lookup tables */ -#ifndef SDL_HAVE_BLIT_N_RGB565 -#define SDL_HAVE_BLIT_N_RGB565 !SDL_LEAN_AND_MEAN +#if !defined(SDL_HAVE_BLIT_N_RGB565) && !defined(SDL_LEAN_AND_MEAN) +#define SDL_HAVE_BLIT_N_RGB565 1 #endif /* Optimized functions from 'SDL_blit_AUTO.c' - blit with modulate color, modulate alpha, any blending mode - scaling or not */ -#ifndef SDL_HAVE_BLIT_AUTO -#define SDL_HAVE_BLIT_AUTO !SDL_LEAN_AND_MEAN +#if !defined(SDL_HAVE_BLIT_AUTO) && !defined(SDL_LEAN_AND_MEAN) +#define SDL_HAVE_BLIT_AUTO 1 #endif /* Run-Length-Encoding - SDL_SetSurfaceColorKey() called with SDL_RLEACCEL flag */ -#ifndef SDL_HAVE_RLE -#define SDL_HAVE_RLE !SDL_LEAN_AND_MEAN +#if !defined(SDL_HAVE_RLE) && !defined(SDL_LEAN_AND_MEAN) +#define SDL_HAVE_RLE 1 #endif /* Software SDL_Renderer - creation of software renderer - *not* general blitting functions - {blend,draw}{fillrect,line,point} internal functions */ -#ifndef SDL_VIDEO_RENDER_SW -#define SDL_VIDEO_RENDER_SW !SDL_LEAN_AND_MEAN +#if !defined(SDL_VIDEO_RENDER_SW) && !defined(SDL_LEAN_AND_MEAN) +#define SDL_VIDEO_RENDER_SW 1 #endif /* YUV formats - handling of YUV surfaces - blitting and conversion functions */ -#ifndef SDL_HAVE_YUV -#define SDL_HAVE_YUV !SDL_LEAN_AND_MEAN +#if !defined(SDL_HAVE_YUV) && !defined(SDL_LEAN_AND_MEAN) +#define SDL_HAVE_YUV 1 #endif #ifdef SDL_RENDER_DISABLED @@ -207,7 +203,6 @@ #endif // SDL_RENDER_DISABLED #ifdef SDL_GPU_DISABLED -#undef SDL_GPU_D3D11 #undef SDL_GPU_D3D12 #undef SDL_GPU_METAL #undef SDL_GPU_VULKAN diff --git a/libs/SDL3/src/SDL_properties.c b/libs/SDL3/src/SDL_properties.c index 0055bd53..ce3e0200 100644 --- a/libs/SDL3/src/SDL_properties.c +++ b/libs/SDL3/src/SDL_properties.c @@ -50,8 +50,7 @@ typedef struct static SDL_InitState SDL_properties_init; static SDL_HashTable *SDL_properties; -static SDL_Mutex *SDL_properties_lock; -static SDL_PropertiesID SDL_last_properties_id; +static SDL_AtomicU32 SDL_last_properties_id; static SDL_AtomicU32 SDL_global_properties; @@ -82,9 +81,8 @@ static void SDL_FreeProperty(const void *key, const void *value, void *data) SDL_FreePropertyWithCleanup(key, value, data, true); } -static void SDL_FreeProperties(const void *key, const void *value, void *data) +static void SDL_FreeProperties(SDL_Properties *properties) { - SDL_Properties *properties = (SDL_Properties *)value; if (properties) { if (properties->props) { SDL_DestroyHashTable(properties->props); @@ -104,10 +102,7 @@ bool SDL_InitProperties(void) return true; } - // If this fails we'll continue without it. - SDL_properties_lock = SDL_CreateMutex(); - - SDL_properties = SDL_CreateHashTable(NULL, 16, SDL_HashID, SDL_KeyMatchID, SDL_FreeProperties, false); + SDL_properties = SDL_CreateHashTable(NULL, 16, SDL_HashID, SDL_KeyMatchID, NULL, true, false); if (!SDL_properties) { goto error; } @@ -137,13 +132,16 @@ void SDL_QuitProperties(void) } if (SDL_properties) { + void *iter; + const void *key, *value; + + iter = NULL; + while (SDL_IterateHashTable(SDL_properties, &key, &value, &iter)) { + SDL_FreeProperties((SDL_Properties *)value); + } SDL_DestroyHashTable(SDL_properties); SDL_properties = NULL; } - if (SDL_properties_lock) { - SDL_DestroyMutex(SDL_properties_lock); - SDL_properties_lock = NULL; - } SDL_SetInitialized(&SDL_properties_init, false); } @@ -181,7 +179,7 @@ SDL_PropertiesID SDL_CreateProperties(void) if (!properties) { goto error; } - properties->props = SDL_CreateHashTable(NULL, 4, SDL_HashString, SDL_KeyMatchString, SDL_FreeProperty, false); + properties->props = SDL_CreateHashTable(NULL, 4, SDL_HashString, SDL_KeyMatchString, SDL_FreeProperty, false, false); if (!properties->props) { goto error; } @@ -189,16 +187,18 @@ SDL_PropertiesID SDL_CreateProperties(void) // If this fails we'll continue without it. properties->lock = SDL_CreateMutex(); - SDL_LockMutex(SDL_properties_lock); - ++SDL_last_properties_id; - if (SDL_last_properties_id == 0) { - ++SDL_last_properties_id; + for ( ; ; ) { + props = (SDL_GetAtomicU32(&SDL_last_properties_id) + 1); + if (props == 0) { + continue; + } + if (SDL_CompareAndSwapAtomicU32(&SDL_last_properties_id, props - 1, props)) { + break; + } } - props = SDL_last_properties_id; if (SDL_InsertIntoHashTable(SDL_properties, (const void *)(uintptr_t)props, properties)) { inserted = true; } - SDL_UnlockMutex(SDL_properties_lock); if (inserted) { // All done! @@ -206,7 +206,7 @@ SDL_PropertiesID SDL_CreateProperties(void) } error: - SDL_FreeProperties(NULL, properties, NULL); + SDL_FreeProperties(properties); return 0; } @@ -223,11 +223,8 @@ bool SDL_CopyProperties(SDL_PropertiesID src, SDL_PropertiesID dst) return SDL_InvalidParamError("dst"); } - SDL_LockMutex(SDL_properties_lock); SDL_FindInHashTable(SDL_properties, (const void *)(uintptr_t)src, (const void **)&src_properties); SDL_FindInHashTable(SDL_properties, (const void *)(uintptr_t)dst, (const void **)&dst_properties); - SDL_UnlockMutex(SDL_properties_lock); - if (!src_properties) { return SDL_InvalidParamError("src"); } @@ -290,10 +287,7 @@ bool SDL_LockProperties(SDL_PropertiesID props) return SDL_InvalidParamError("props"); } - SDL_LockMutex(SDL_properties_lock); SDL_FindInHashTable(SDL_properties, (const void *)(uintptr_t)props, (const void **)&properties); - SDL_UnlockMutex(SDL_properties_lock); - if (!properties) { return SDL_InvalidParamError("props"); } @@ -310,10 +304,7 @@ void SDL_UnlockProperties(SDL_PropertiesID props) return; } - SDL_LockMutex(SDL_properties_lock); SDL_FindInHashTable(SDL_properties, (const void *)(uintptr_t)props, (const void **)&properties); - SDL_UnlockMutex(SDL_properties_lock); - if (!properties) { return; } @@ -335,10 +326,7 @@ static bool SDL_PrivateSetProperty(SDL_PropertiesID props, const char *name, SDL return SDL_InvalidParamError("name"); } - SDL_LockMutex(SDL_properties_lock); SDL_FindInHashTable(SDL_properties, (const void *)(uintptr_t)props, (const void **)&properties); - SDL_UnlockMutex(SDL_properties_lock); - if (!properties) { SDL_FreePropertyWithCleanup(NULL, property, NULL, true); return SDL_InvalidParamError("props"); @@ -496,10 +484,7 @@ SDL_PropertyType SDL_GetPropertyType(SDL_PropertiesID props, const char *name) return SDL_PROPERTY_TYPE_INVALID; } - SDL_LockMutex(SDL_properties_lock); SDL_FindInHashTable(SDL_properties, (const void *)(uintptr_t)props, (const void **)&properties); - SDL_UnlockMutex(SDL_properties_lock); - if (!properties) { return SDL_PROPERTY_TYPE_INVALID; } @@ -528,10 +513,7 @@ void *SDL_GetPointerProperty(SDL_PropertiesID props, const char *name, void *def return value; } - SDL_LockMutex(SDL_properties_lock); SDL_FindInHashTable(SDL_properties, (const void *)(uintptr_t)props, (const void **)&properties); - SDL_UnlockMutex(SDL_properties_lock); - if (!properties) { return value; } @@ -566,10 +548,7 @@ const char *SDL_GetStringProperty(SDL_PropertiesID props, const char *name, cons return value; } - SDL_LockMutex(SDL_properties_lock); SDL_FindInHashTable(SDL_properties, (const void *)(uintptr_t)props, (const void **)&properties); - SDL_UnlockMutex(SDL_properties_lock); - if (!properties) { return value; } @@ -627,10 +606,7 @@ Sint64 SDL_GetNumberProperty(SDL_PropertiesID props, const char *name, Sint64 de return value; } - SDL_LockMutex(SDL_properties_lock); SDL_FindInHashTable(SDL_properties, (const void *)(uintptr_t)props, (const void **)&properties); - SDL_UnlockMutex(SDL_properties_lock); - if (!properties) { return value; } @@ -674,10 +650,7 @@ float SDL_GetFloatProperty(SDL_PropertiesID props, const char *name, float defau return value; } - SDL_LockMutex(SDL_properties_lock); SDL_FindInHashTable(SDL_properties, (const void *)(uintptr_t)props, (const void **)&properties); - SDL_UnlockMutex(SDL_properties_lock); - if (!properties) { return value; } @@ -721,10 +694,7 @@ bool SDL_GetBooleanProperty(SDL_PropertiesID props, const char *name, bool defau return value; } - SDL_LockMutex(SDL_properties_lock); SDL_FindInHashTable(SDL_properties, (const void *)(uintptr_t)props, (const void **)&properties); - SDL_UnlockMutex(SDL_properties_lock); - if (!properties) { return value; } @@ -772,10 +742,7 @@ bool SDL_EnumerateProperties(SDL_PropertiesID props, SDL_EnumeratePropertiesCall return SDL_InvalidParamError("callback"); } - SDL_LockMutex(SDL_properties_lock); SDL_FindInHashTable(SDL_properties, (const void *)(uintptr_t)props, (const void **)&properties); - SDL_UnlockMutex(SDL_properties_lock); - if (!properties) { return SDL_InvalidParamError("props"); } @@ -829,11 +796,14 @@ bool SDL_DumpProperties(SDL_PropertiesID props) void SDL_DestroyProperties(SDL_PropertiesID props) { + SDL_Properties *properties = NULL; + if (!props) { return; } - SDL_LockMutex(SDL_properties_lock); - SDL_RemoveFromHashTable(SDL_properties, (const void *)(uintptr_t)props); - SDL_UnlockMutex(SDL_properties_lock); + if (SDL_FindInHashTable(SDL_properties, (const void *)(uintptr_t)props, (const void **)&properties)) { + SDL_FreeProperties(properties); + SDL_RemoveFromHashTable(SDL_properties, (const void *)(uintptr_t)props); + } } diff --git a/libs/SDL3/src/SDL_utils.c b/libs/SDL3/src/SDL_utils.c index 8607798a..5487b9d5 100644 --- a/libs/SDL3/src/SDL_utils.c +++ b/libs/SDL3/src/SDL_utils.c @@ -132,6 +132,7 @@ Uint32 SDL_GetNextObjectID(void) return id; } +static SDL_InitState SDL_objects_init; static SDL_HashTable *SDL_objects; static Uint32 SDL_HashObject(const void *key, void *unused) @@ -148,16 +149,18 @@ void SDL_SetObjectValid(void *object, SDL_ObjectType type, bool valid) { SDL_assert(object != NULL); - if (valid) { + if (valid && SDL_ShouldInit(&SDL_objects_init)) { + SDL_objects = SDL_CreateHashTable(NULL, 32, SDL_HashObject, SDL_KeyMatchObject, NULL, true, false); if (!SDL_objects) { - SDL_objects = SDL_CreateHashTable(NULL, 32, SDL_HashObject, SDL_KeyMatchObject, NULL, false); + SDL_SetInitialized(&SDL_objects_init, false); } + SDL_SetInitialized(&SDL_objects_init, true); + } + if (valid) { SDL_InsertIntoHashTable(SDL_objects, object, (void *)(uintptr_t)type); } else { - if (SDL_objects) { - SDL_RemoveFromHashTable(SDL_objects, object); - } + SDL_RemoveFromHashTable(SDL_objects, object); } } @@ -177,7 +180,7 @@ bool SDL_ObjectValid(void *object, SDL_ObjectType type) void SDL_SetObjectsInvalid(void) { - if (SDL_objects) { + if (SDL_ShouldQuit(&SDL_objects_init)) { // Log any leaked objects const void *object, *object_type; void *iter = NULL; @@ -221,6 +224,8 @@ void SDL_SetObjectsInvalid(void) SDL_DestroyHashTable(SDL_objects); SDL_objects = NULL; + + SDL_SetInitialized(&SDL_objects_init, false); } } @@ -357,7 +362,7 @@ const char *SDL_GetPersistentString(const char *string) SDL_HashTable *strings = (SDL_HashTable *)SDL_GetTLS(&SDL_string_storage); if (!strings) { - strings = SDL_CreateHashTable(NULL, 32, SDL_HashString, SDL_KeyMatchString, SDL_NukeFreeValue, false); + strings = SDL_CreateHashTable(NULL, 32, SDL_HashString, SDL_KeyMatchString, SDL_NukeFreeValue, false, false); if (!strings) { return NULL; } diff --git a/libs/SDL3/src/audio/SDL_audio.c b/libs/SDL3/src/audio/SDL_audio.c index 2de86cec..e477a749 100644 --- a/libs/SDL3/src/audio/SDL_audio.c +++ b/libs/SDL3/src/audio/SDL_audio.c @@ -144,7 +144,7 @@ const char *SDL_GetCurrentAudioDriver(void) return current_audio.name; } -static int GetDefaultSampleFramesFromFreq(const int freq) +int SDL_GetDefaultSampleFramesFromFreq(const int freq) { const char *hint = SDL_GetHint(SDL_HINT_AUDIO_DEVICE_SAMPLE_FRAMES); if (hint) { @@ -234,34 +234,39 @@ static bool AudioDeviceCanUseSimpleCopy(SDL_AudioDevice *device) // should hold device->lock before calling. static void UpdateAudioStreamFormatsPhysical(SDL_AudioDevice *device) { - if (device->recording) { // for recording devices, we only want to move to float32 for postmix and gain, which we'll handle elsewhere. - // we _do_ need to make sure the channel map is correct, though... - for (SDL_LogicalAudioDevice *logdev = device->logical_devices; logdev; logdev = logdev->next) { - for (SDL_AudioStream *stream = logdev->bound_streams; stream; stream = stream->next_binding) { - // set the proper end of the stream to the device's channel map. This will obtain stream->lock itself. - SetAudioStreamChannelMap(stream, &stream->src_spec, &stream->src_chmap, device->chmap, device->spec.channels, -1); - } - } - } else { + if (!device) { + return; + } + + const bool recording = device->recording; + SDL_AudioSpec spec; + SDL_copyp(&spec, &device->spec); + + const SDL_AudioFormat devformat = spec.format; + + if (!recording) { const bool simple_copy = AudioDeviceCanUseSimpleCopy(device); - SDL_AudioSpec spec; - device->simple_copy = simple_copy; - SDL_copyp(&spec, &device->spec); - if (!simple_copy) { spec.format = SDL_AUDIO_F32; // mixing and postbuf operates in float32 format. } + } - for (SDL_LogicalAudioDevice *logdev = device->logical_devices; logdev; logdev = logdev->next) { - for (SDL_AudioStream *stream = logdev->bound_streams; stream; stream = stream->next_binding) { - // set the proper end of the stream to the device's format. - // SDL_SetAudioStreamFormat does a ton of validation just to memcpy an audiospec. - SDL_LockMutex(stream->lock); - SDL_copyp(&stream->dst_spec, &spec); - SetAudioStreamChannelMap(stream, &stream->dst_spec, &stream->dst_chmap, device->chmap, spec.channels, -1); // this should be fast for normal cases, though! - SDL_UnlockMutex(stream->lock); - } + for (SDL_LogicalAudioDevice *logdev = device->logical_devices; logdev; logdev = logdev->next) { + if (recording) { + const bool need_float32 = (logdev->postmix || logdev->gain != 1.0f); + spec.format = need_float32 ? SDL_AUDIO_F32 : devformat; + } + + for (SDL_AudioStream *stream = logdev->bound_streams; stream; stream = stream->next_binding) { + // set the proper end of the stream to the device's format. + // SDL_SetAudioStreamFormat does a ton of validation just to memcpy an audiospec. + SDL_AudioSpec *streamspec = recording ? &stream->src_spec : &stream->dst_spec; + int **streamchmap = recording ? &stream->src_chmap : &stream->dst_chmap; + SDL_LockMutex(stream->lock); + SDL_copyp(streamspec, &spec); + SetAudioStreamChannelMap(stream, streamspec, streamchmap, device->chmap, device->spec.channels, -1); // this should be fast for normal cases, though! + SDL_UnlockMutex(stream->lock); } } } @@ -346,6 +351,16 @@ static SDL_AudioDeviceID AssignAudioDeviceInstanceId(bool recording, bool islogi return instance_id; } +bool SDL_IsAudioDevicePhysical(SDL_AudioDeviceID devid) +{ + return (devid & (1 << 1)) != 0; +} + +bool SDL_IsAudioDevicePlayback(SDL_AudioDeviceID devid) +{ + return (devid & (1 << 0)) != 0; +} + static void ObtainPhysicalAudioDeviceObj(SDL_AudioDevice *device) SDL_NO_THREAD_SAFETY_ANALYSIS // !!! FIXMEL SDL_ACQUIRE { if (device) { @@ -616,7 +631,7 @@ static SDL_AudioDevice *CreatePhysicalAudioDevice(const char *name, bool recordi device->recording = recording; SDL_copyp(&device->spec, spec); SDL_copyp(&device->default_spec, spec); - device->sample_frames = GetDefaultSampleFramesFromFreq(device->spec.freq); + device->sample_frames = SDL_GetDefaultSampleFramesFromFreq(device->spec.freq); device->silence_value = SDL_GetSilenceValueForFormat(device->spec.format); device->handle = handle; @@ -853,7 +868,7 @@ static SDL_AudioDevice *GetFirstAddedAudioDevice(const bool recording) // bit #0 of devid is set for playback devices and unset for recording. // bit #1 of devid is set for physical devices and unset for logical. const bool devid_recording = !(devid & (1 << 0)); - const bool isphysical = (devid & (1 << 1)); + const bool isphysical = !!(devid & (1 << 1)); if (isphysical && (devid_recording == recording) && (devid < highest)) { highest = devid; result = (SDL_AudioDevice *) value; @@ -899,7 +914,7 @@ bool SDL_InitAudio(const char *driver_name) return false; } - SDL_HashTable *device_hash = SDL_CreateHashTable(NULL, 8, HashAudioDeviceID, MatchAudioDeviceID, NukeAudioDeviceHashItem, false); + SDL_HashTable *device_hash = SDL_CreateHashTable(NULL, 8, HashAudioDeviceID, MatchAudioDeviceID, NukeAudioDeviceHashItem, false, false); if (!device_hash) { SDL_DestroyRWLock(device_hash_lock); return false; @@ -1055,7 +1070,7 @@ void SDL_QuitAudio(void) while (SDL_IterateHashTable(device_hash, &key, &value, &iter)) { // bit #1 of devid is set for physical devices and unset for logical. const SDL_AudioDeviceID devid = (SDL_AudioDeviceID) (uintptr_t) key; - const bool isphysical = (devid & (1<<1)); + const bool isphysical = !!(devid & (1<<1)); if (isphysical) { DestroyPhysicalAudioDevice((SDL_AudioDevice *) value); } @@ -1357,7 +1372,7 @@ static SDL_AudioDeviceID *GetAudioDevices(int *count, bool recording) // bit #0 of devid is set for playback devices and unset for recording. // bit #1 of devid is set for physical devices and unset for logical. const bool devid_recording = !(devid & (1<<0)); - const bool isphysical = (devid & (1<<1)); + const bool isphysical = !!(devid & (1<<1)); if (isphysical && (devid_recording == recording)) { SDL_assert(devs_seen < num_devices); result[devs_seen++] = devid; @@ -1409,7 +1424,7 @@ SDL_AudioDevice *SDL_FindPhysicalAudioDeviceByCallback(bool (*callback)(SDL_Audi while (SDL_IterateHashTable(current_audio.device_hash, &key, &value, &iter)) { const SDL_AudioDeviceID devid = (SDL_AudioDeviceID) (uintptr_t) key; // bit #1 of devid is set for physical devices and unset for logical. - const bool isphysical = (devid & (1<<1)); + const bool isphysical = !!(devid & (1<<1)); if (isphysical) { SDL_AudioDevice *device = (SDL_AudioDevice *) value; if (callback(device, userdata)) { // found it? @@ -1656,7 +1671,7 @@ static bool OpenPhysicalAudioDevice(SDL_AudioDevice *device, const SDL_AudioSpec device->spec.format = (SDL_AUDIO_BITSIZE(device->default_spec.format) >= SDL_AUDIO_BITSIZE(spec.format)) ? device->default_spec.format : spec.format; device->spec.freq = SDL_max(device->default_spec.freq, spec.freq); device->spec.channels = SDL_max(device->default_spec.channels, spec.channels); - device->sample_frames = GetDefaultSampleFramesFromFreq(device->spec.freq); + device->sample_frames = SDL_GetDefaultSampleFramesFromFreq(device->spec.freq); SDL_UpdatedAudioDeviceFormat(device); // start this off sane. device->currently_opened = true; // mark this true even if impl.OpenDevice fails, so we know to clean up. @@ -1812,17 +1827,6 @@ bool SDL_SetAudioDeviceGain(SDL_AudioDeviceID devid, float gain) bool result = false; if (logdev) { logdev->gain = gain; - if (device->recording) { - const bool need_float32 = (logdev->postmix || logdev->gain != 1.0f); - for (SDL_AudioStream *stream = logdev->bound_streams; stream; stream = stream->next_binding) { - // set the proper end of the stream to the device's format. - // SDL_SetAudioStreamFormat does a ton of validation just to memcpy an audiospec. - SDL_LockMutex(stream->lock); - stream->src_spec.format = need_float32 ? SDL_AUDIO_F32 : device->spec.format; - SDL_UnlockMutex(stream->lock); - } - } - UpdateAudioStreamFormatsPhysical(device); result = true; } @@ -1846,17 +1850,6 @@ bool SDL_SetAudioPostmixCallback(SDL_AudioDeviceID devid, SDL_AudioPostmixCallba if (result) { logdev->postmix = callback; logdev->postmix_userdata = userdata; - - if (device->recording) { - const bool need_float32 = (callback || logdev->gain != 1.0f); - for (SDL_AudioStream *stream = logdev->bound_streams; stream; stream = stream->next_binding) { - // set the proper end of the stream to the device's format. - // SDL_SetAudioStreamFormat does a ton of validation just to memcpy an audiospec. - SDL_LockMutex(stream->lock); - stream->src_spec.format = need_float32 ? SDL_AUDIO_F32 : device->spec.format; - SDL_UnlockMutex(stream->lock); - } - } } UpdateAudioStreamFormatsPhysical(device); @@ -1926,7 +1919,6 @@ bool SDL_BindAudioStreams(SDL_AudioDeviceID devid, SDL_AudioStream **streams, in 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... @@ -1937,15 +1929,6 @@ bool SDL_BindAudioStreams(SDL_AudioDeviceID devid, SDL_AudioStream **streams, in logdev->bound_streams->prev_binding = stream; } logdev->bound_streams = stream; - - if (recording) { - SDL_copyp(&stream->src_spec, &device->spec); - if (logdev->postmix) { - stream->src_spec.format = SDL_AUDIO_F32; - } - SDL_SetAudioStreamInputChannelMap(stream, device->chmap, device->spec.channels); // this should be fast for normal cases, though! - } - SDL_UnlockMutex(stream->lock); } } diff --git a/libs/SDL3/src/audio/SDL_audiocvt.c b/libs/SDL3/src/audio/SDL_audiocvt.c index 644df39d..4f375034 100644 --- a/libs/SDL3/src/audio/SDL_audiocvt.c +++ b/libs/SDL3/src/audio/SDL_audiocvt.c @@ -129,7 +129,7 @@ bool SDL_ChannelMapIsBogus(const int *chmap, int channels) if (chmap) { for (int i = 0; i < channels; i++) { const int mapping = chmap[i]; - if ((mapping < 0) || (mapping >= channels)) { + if ((mapping < -1) || (mapping >= channels)) { return true; } } @@ -150,27 +150,60 @@ bool SDL_ChannelMapIsDefault(const int *chmap, int channels) } // Swizzle audio channels. src and dst can be the same pointer. It does not change the buffer size. -static void SwizzleAudio(const int num_frames, void *dst, const void *src, int channels, const int *map, int bitsize) +static void SwizzleAudio(const int num_frames, void *dst, const void *src, int channels, const int *map, SDL_AudioFormat fmt) { + const int bitsize = (int) SDL_AUDIO_BITSIZE(fmt); + + bool has_null_mappings = false; + for (int i = 0; i < channels; i++) { + if (map[i] == -1) { + has_null_mappings = true; + break; + } + } + #define CHANNEL_SWIZZLE(bits) { \ Uint##bits *tdst = (Uint##bits *) dst; /* treat as UintX; we only care about moving bits and not the type here. */ \ const Uint##bits *tsrc = (const Uint##bits *) src; \ if (src != dst) { /* don't need to copy to a temporary frame first. */ \ - for (int i = 0; i < num_frames; i++, tsrc += channels, tdst += channels) { \ - for (int ch = 0; ch < channels; ch++) { \ - tdst[ch] = tsrc[map[ch]]; \ + if (has_null_mappings) { \ + const Uint##bits silence = (Uint##bits) SDL_GetSilenceValueForFormat(fmt); \ + for (int i = 0; i < num_frames; i++, tsrc += channels, tdst += channels) { \ + for (int ch = 0; ch < channels; ch++) { \ + const int m = map[ch]; \ + tdst[ch] = (m == -1) ? silence : tsrc[m]; \ + } \ + } \ + } else { \ + for (int i = 0; i < num_frames; i++, tsrc += channels, tdst += channels) { \ + for (int ch = 0; ch < channels; ch++) { \ + tdst[ch] = tsrc[map[ch]]; \ + } \ } \ } \ } else { \ bool isstack; \ Uint##bits *tmp = (Uint##bits *) SDL_small_alloc(int, channels, &isstack); /* !!! FIXME: allocate this when setting the channel map instead. */ \ if (tmp) { \ - for (int i = 0; i < num_frames; i++, tsrc += channels, tdst += channels) { \ - for (int ch = 0; ch < channels; ch++) { \ - tmp[ch] = tsrc[map[ch]]; \ + if (has_null_mappings) { \ + const Uint##bits silence = (Uint##bits) SDL_GetSilenceValueForFormat(fmt); \ + for (int i = 0; i < num_frames; i++, tsrc += channels, tdst += channels) { \ + for (int ch = 0; ch < channels; ch++) { \ + const int m = map[ch]; \ + tmp[ch] = (m == -1) ? silence : tsrc[m]; \ + } \ + for (int ch = 0; ch < channels; ch++) { \ + tdst[ch] = tmp[ch]; \ + } \ } \ - for (int ch = 0; ch < channels; ch++) { \ - tdst[ch] = tmp[ch]; \ + } else { \ + for (int i = 0; i < num_frames; i++, tsrc += channels, tdst += channels) { \ + for (int ch = 0; ch < channels; ch++) { \ + tmp[ch] = tsrc[map[ch]]; \ + } \ + for (int ch = 0; ch < channels; ch++) { \ + tdst[ch] = tmp[ch]; \ + } \ } \ } \ SDL_small_free(tmp, isstack); \ @@ -221,9 +254,7 @@ void ConvertAudio(int num_frames, SDL_Log("SDL_AUDIO_CONVERT: Convert format %04x->%04x, channels %u->%u", src_format, dst_format, src_channels, dst_channels); #endif - const int src_bitsize = (int) SDL_AUDIO_BITSIZE(src_format); const int dst_bitsize = (int) SDL_AUDIO_BITSIZE(dst_format); - const int dst_sample_frame_size = (dst_bitsize / 8) * dst_channels; /* Type conversion goes like this now: @@ -245,7 +276,7 @@ void ConvertAudio(int num_frames, // swizzle input to "standard" format if necessary. if (src_map) { void* buf = scratch ? scratch : dst; // use scratch if available, since it has to be big enough to hold src, unless it's NULL, then dst has to be. - SwizzleAudio(num_frames, buf, src, src_channels, src_map, src_bitsize); + SwizzleAudio(num_frames, buf, src, src_channels, src_map, src_format); src = buf; } @@ -254,7 +285,7 @@ void ConvertAudio(int num_frames, if (src_format == dst_format) { // nothing to do, we're already in the right format, just copy it over if necessary. if (dst_map) { - SwizzleAudio(num_frames, dst, src, dst_channels, dst_map, dst_bitsize); + SwizzleAudio(num_frames, dst, src, dst_channels, dst_map, dst_format); } else if (src != dst) { SDL_memcpy(dst, src, num_frames * dst_sample_frame_size); } @@ -264,7 +295,7 @@ void ConvertAudio(int num_frames, // just a byteswap needed? if ((src_format ^ dst_format) == SDL_AUDIO_MASK_BIG_ENDIAN) { if (dst_map) { // do this first, in case we duplicate channels, we can avoid an extra copy if src != dst. - SwizzleAudio(num_frames, dst, src, dst_channels, dst_map, dst_bitsize); + SwizzleAudio(num_frames, dst, src, dst_channels, dst_map, dst_format); src = dst; } ConvertAudioSwapEndian(dst, src, num_frames * dst_channels, dst_bitsize); @@ -348,7 +379,7 @@ void ConvertAudio(int num_frames, SDL_assert(src == dst); // if we got here, we _had_ to have done _something_. Otherwise, we should have memcpy'd! if (dst_map) { - SwizzleAudio(num_frames, dst, src, dst_channels, dst_map, dst_bitsize); + SwizzleAudio(num_frames, dst, src, dst_channels, dst_map, dst_format); } } diff --git a/libs/SDL3/src/audio/SDL_sysaudio.h b/libs/SDL3/src/audio/SDL_sysaudio.h index 0ddc0af9..265f98c8 100644 --- a/libs/SDL3/src/audio/SDL_sysaudio.h +++ b/libs/SDL3/src/audio/SDL_sysaudio.h @@ -91,6 +91,9 @@ extern SDL_AudioDevice *SDL_FindPhysicalAudioDeviceByCallback(bool (*callback)(S // Backends should call this if they change the device format, channels, freq, or sample_frames to keep other state correct. extern void SDL_UpdatedAudioDeviceFormat(SDL_AudioDevice *device); +// Backends can call this to get a reasonable default sample frame count for a device's sample rate. +int SDL_GetDefaultSampleFramesFromFreq(const int freq); + // Backends can call this to get a standardized name for a thread to power a specific audio device. extern char *SDL_GetAudioThreadName(SDL_AudioDevice *device, char *buf, size_t buflen); diff --git a/libs/SDL3/src/audio/aaudio/SDL_aaudio.c b/libs/SDL3/src/audio/aaudio/SDL_aaudio.c index c53157ee..2ce1f6cc 100644 --- a/libs/SDL3/src/audio/aaudio/SDL_aaudio.c +++ b/libs/SDL3/src/audio/aaudio/SDL_aaudio.c @@ -298,7 +298,6 @@ static bool BuildAAudioStream(SDL_AudioDevice *device) #endif aaudio_format_t format; -#ifdef SET_AUDIO_FORMAT if ((device->spec.format == SDL_AUDIO_S32) && (SDL_GetAndroidSDKVersion() >= 31)) { format = AAUDIO_FORMAT_PCM_I32; } else if (device->spec.format == SDL_AUDIO_F32) { @@ -308,7 +307,6 @@ static bool BuildAAudioStream(SDL_AudioDevice *device) } ctx.AAudioStreamBuilder_setFormat(builder, format); ctx.AAudioStreamBuilder_setSampleRate(builder, device->spec.freq); -#endif ctx.AAudioStreamBuilder_setChannelCount(builder, device->spec.channels); const aaudio_direction_t direction = (recording ? AAUDIO_DIRECTION_INPUT : AAUDIO_DIRECTION_OUTPUT); @@ -316,13 +314,16 @@ static bool BuildAAudioStream(SDL_AudioDevice *device) ctx.AAudioStreamBuilder_setErrorCallback(builder, AAUDIO_errorCallback, device); ctx.AAudioStreamBuilder_setDataCallback(builder, AAUDIO_dataCallback, device); // Some devices have flat sounding audio when low latency mode is enabled, but this is a better experience for most people - if (SDL_GetHintBoolean("SDL_ANDROID_LOW_LATENCY_AUDIO", true)) { + if (SDL_GetHintBoolean(SDL_HINT_ANDROID_LOW_LATENCY_AUDIO, true)) { + SDL_Log("Low latency audio enabled\n"); ctx.AAudioStreamBuilder_setPerformanceMode(builder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY); + } else { + SDL_Log("Low latency audio disabled\n"); } - LOGI("AAudio Try to open %u hz %u bit %u channels %s samples %u", - device->spec.freq, SDL_AUDIO_BITSIZE(device->spec.format), - device->spec.channels, SDL_AUDIO_ISBIGENDIAN(device->spec.format) ? "BE" : "LE", device->sample_frames); + LOGI("AAudio Try to open %u hz %s %u channels samples %u", + device->spec.freq, SDL_GetAudioFormatName(device->spec.format), + device->spec.channels, device->sample_frames); res = ctx.AAudioStreamBuilder_openStream(builder, &hidden->stream); if (res != AAUDIO_OK) { @@ -354,8 +355,9 @@ static bool BuildAAudioStream(SDL_AudioDevice *device) SDL_UpdatedAudioDeviceFormat(device); - // Allocate a double buffered mixing buffer - hidden->num_buffers = 2; + // Allocate a triple buffered mixing buffer + // Two buffers can be in the process of being filled while the third is being read + hidden->num_buffers = 3; hidden->mixbuf_bytes = (hidden->num_buffers * device->buffer_size); hidden->mixbuf = (Uint8 *)SDL_aligned_alloc(SDL_GetSIMDAlignment(), hidden->mixbuf_bytes); if (!hidden->mixbuf) { @@ -370,9 +372,9 @@ static bool BuildAAudioStream(SDL_AudioDevice *device) return false; } - LOGI("AAudio Actually opened %u hz %u bit %u channels %s samples %u, buffers %d", - device->spec.freq, SDL_AUDIO_BITSIZE(device->spec.format), - device->spec.channels, SDL_AUDIO_ISBIGENDIAN(device->spec.format) ? "BE" : "LE", device->sample_frames, hidden->num_buffers); + LOGI("AAudio Actually opened %u hz %s %u channels samples %u, buffers %d", + device->spec.freq, SDL_GetAudioFormatName(device->spec.format), + device->spec.channels, device->sample_frames, hidden->num_buffers); res = ctx.AAudioStream_requestStart(hidden->stream); if (res != AAUDIO_OK) { diff --git a/libs/SDL3/src/audio/alsa/SDL_alsa_audio.c b/libs/SDL3/src/audio/alsa/SDL_alsa_audio.c index 827b54de..2b1c12cb 100644 --- a/libs/SDL3/src/audio/alsa/SDL_alsa_audio.c +++ b/libs/SDL3/src/audio/alsa/SDL_alsa_audio.c @@ -31,18 +31,22 @@ #define SDL_ALSA_HOTPLUG_THREAD 1 #endif -// Allow access to a raw mixing buffer - -#include -#include // For kill() -#include +// this turns off debug logging completely (but by default this goes to the bitbucket). +#ifndef SDL_ALSA_DEBUG +#define SDL_ALSA_DEBUG 1 +#endif #include "../SDL_sysaudio.h" #include "SDL_alsa_audio.h" -#ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC +#if SDL_ALSA_DEBUG +#define LOGDEBUG(...) SDL_LogDebug(SDL_LOG_CATEGORY_AUDIO, "ALSA: " __VA_ARGS__) +#else +#define LOGDEBUG(...) #endif +//TODO: cleanup once the code settled down + static int (*ALSA_snd_pcm_open)(snd_pcm_t **, const char *, snd_pcm_stream_t, int); static int (*ALSA_snd_pcm_close)(snd_pcm_t *pcm); static int (*ALSA_snd_pcm_start)(snd_pcm_t *pcm); @@ -81,10 +85,30 @@ static int (*ALSA_snd_device_name_hint)(int, const char *, void ***); static char *(*ALSA_snd_device_name_get_hint)(const void *, const char *); static int (*ALSA_snd_device_name_free_hint)(void **); static snd_pcm_sframes_t (*ALSA_snd_pcm_avail)(snd_pcm_t *); -#ifdef SND_CHMAP_API_VERSION -static snd_pcm_chmap_t *(*ALSA_snd_pcm_get_chmap)(snd_pcm_t *); -static int (*ALSA_snd_pcm_chmap_print)(const snd_pcm_chmap_t *map, size_t maxlen, char *buf); -#endif +static size_t (*ALSA_snd_ctl_card_info_sizeof)(void); +static size_t (*ALSA_snd_pcm_info_sizeof)(void); +static int (*ALSA_snd_card_next)(int*); +static int (*ALSA_snd_ctl_open)(snd_ctl_t **,const char *,int); +static int (*ALSA_snd_ctl_close)(snd_ctl_t *); +static int (*ALSA_snd_ctl_card_info)(snd_ctl_t *, snd_ctl_card_info_t *); +static int (*ALSA_snd_ctl_pcm_next_device)(snd_ctl_t *, int *); +static unsigned int (*ALSA_snd_pcm_info_get_subdevices_count)(const snd_pcm_info_t *); +static void (*ALSA_snd_pcm_info_set_device)(snd_pcm_info_t *, unsigned int); +static void (*ALSA_snd_pcm_info_set_subdevice)(snd_pcm_info_t *, unsigned int); +static void (*ALSA_snd_pcm_info_set_stream)(snd_pcm_info_t *, snd_pcm_stream_t); +static int (*ALSA_snd_ctl_pcm_info)(snd_ctl_t *, snd_pcm_info_t *); +static unsigned int (*ALSA_snd_pcm_info_get_subdevices_count)(const snd_pcm_info_t *); +static const char *(*ALSA_snd_ctl_card_info_get_id)(const snd_ctl_card_info_t *); +static const char *(*ALSA_snd_pcm_info_get_name)(const snd_pcm_info_t *); +static const char *(*ALSA_snd_pcm_info_get_subdevice_name)(const snd_pcm_info_t *); +static const char *(*ALSA_snd_ctl_card_info_get_name)(const snd_ctl_card_info_t *); +static void (*ALSA_snd_ctl_card_info_clear)(snd_ctl_card_info_t *); +static int (*ALSA_snd_pcm_hw_free)(snd_pcm_t *); +static int (*ALSA_snd_pcm_hw_params_set_channels_near)(snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int *); +static snd_pcm_chmap_query_t **(*ALSA_snd_pcm_query_chmaps)(snd_pcm_t *pcm); +static void (*ALSA_snd_pcm_free_chmaps)(snd_pcm_chmap_query_t **maps); +static int (*ALSA_snd_pcm_set_chmap)(snd_pcm_t *, const snd_pcm_chmap_t *); +static int (*ALSA_snd_pcm_chmap_print)(const snd_pcm_chmap_t *, size_t, char *); #ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC #define snd_pcm_hw_params_sizeof ALSA_snd_pcm_hw_params_sizeof @@ -151,10 +175,30 @@ static bool load_alsa_syms(void) SDL_ALSA_SYM(snd_device_name_get_hint); SDL_ALSA_SYM(snd_device_name_free_hint); SDL_ALSA_SYM(snd_pcm_avail); -#ifdef SND_CHMAP_API_VERSION - SDL_ALSA_SYM(snd_pcm_get_chmap); + SDL_ALSA_SYM(snd_ctl_card_info_sizeof); + SDL_ALSA_SYM(snd_pcm_info_sizeof); + SDL_ALSA_SYM(snd_card_next); + SDL_ALSA_SYM(snd_ctl_open); + SDL_ALSA_SYM(snd_ctl_close); + SDL_ALSA_SYM(snd_ctl_card_info); + SDL_ALSA_SYM(snd_ctl_pcm_next_device); + SDL_ALSA_SYM(snd_pcm_info_get_subdevices_count); + SDL_ALSA_SYM(snd_pcm_info_set_device); + SDL_ALSA_SYM(snd_pcm_info_set_subdevice); + SDL_ALSA_SYM(snd_pcm_info_set_stream); + SDL_ALSA_SYM(snd_ctl_pcm_info); + SDL_ALSA_SYM(snd_pcm_info_get_subdevices_count); + SDL_ALSA_SYM(snd_ctl_card_info_get_id); + SDL_ALSA_SYM(snd_pcm_info_get_name); + SDL_ALSA_SYM(snd_pcm_info_get_subdevice_name); + SDL_ALSA_SYM(snd_ctl_card_info_get_name); + SDL_ALSA_SYM(snd_ctl_card_info_clear); + SDL_ALSA_SYM(snd_pcm_hw_free); + SDL_ALSA_SYM(snd_pcm_hw_params_set_channels_near); + SDL_ALSA_SYM(snd_pcm_query_chmaps); + SDL_ALSA_SYM(snd_pcm_free_chmaps); + SDL_ALSA_SYM(snd_pcm_set_chmap); SDL_ALSA_SYM(snd_pcm_chmap_print); -#endif return true; } @@ -203,62 +247,104 @@ static bool LoadALSALibrary(void) #endif // SDL_AUDIO_DRIVER_ALSA_DYNAMIC +static const char *ALSA_device_prefix = NULL; +static void ALSA_guess_device_prefix(void) +{ + if (ALSA_device_prefix) { + return; // already calculated. + } + + // Apparently there are several different ways that ALSA lists + // actual hardware. It could be prefixed with "hw:" or "default:" + // or "sysdefault:" and maybe others. Go through the list and see + // if we can find a preferred prefix for the system. + + static const char *const prefixes[] = { + "hw:", "sysdefault:", "default:" + }; + + void **hints = NULL; + if (ALSA_snd_device_name_hint(-1, "pcm", &hints) == 0) { + for (int i = 0; hints[i]; i++) { + char *name = ALSA_snd_device_name_get_hint(hints[i], "NAME"); + if (name) { + for (int j = 0; j < SDL_arraysize(prefixes); j++) { + const char *prefix = prefixes[j]; + const size_t prefixlen = SDL_strlen(prefix); + if (SDL_strncmp(name, prefix, prefixlen) == 0) { + ALSA_device_prefix = prefix; + break; + } + } + free(name); // This should NOT be SDL_free() + + if (ALSA_device_prefix) { + break; + } + } + } + } + + if (!ALSA_device_prefix) { + ALSA_device_prefix = prefixes[0]; // oh well. + } + + LOGDEBUG("device prefix is probably '%s'", ALSA_device_prefix); +} + typedef struct ALSA_Device { + // the unicity key is the couple (id,recording) + char *id; // empty means canonical default char *name; bool recording; struct ALSA_Device *next; } ALSA_Device; static const ALSA_Device default_playback_handle = { + "", "default", false, NULL }; static const ALSA_Device default_recording_handle = { + "", "default", true, NULL }; -static const char *get_audio_device(void *handle, const int channels) +// TODO: Figure out the "right"(TM) way. For the moment we presume that if a system is using a +// software mixer for application audio sharing which is not the linux native alsa[dmix], for +// instance jack/pulseaudio2[pipewire]/pulseaudio1/esound/etc, we expect the system integrators did +// configure the canonical default to the right alsa PCM plugin for their software mixer. +// +// All the above may be completely wrong. +static char *get_pcm_str(void *handle) { SDL_assert(handle != NULL); // SDL2 used NULL to mean "default" but that's not true in SDL3. - ALSA_Device *dev = (ALSA_Device *)handle; - if (SDL_strcmp(dev->name, "default") == 0) { - const char *device = SDL_GetHint(SDL_HINT_AUDIO_ALSA_DEFAULT_DEVICE); - if (device) { - return device; - } else if (channels == 6) { - return "plug:surround51"; - } else if (channels == 4) { - return "plug:surround40"; + char *pcm_str = NULL; + + if (SDL_strlen(dev->id) == 0) { + // If the user does not want to go thru the default PCM or the canonical default, the + // the configuration space being _massive_, give the user the ability to specify + // its own PCMs using environment variables. It will have to fit SDL constraints though. + const char *devname = SDL_GetHint(dev->recording ? SDL_HINT_AUDIO_ALSA_DEFAULT_RECORDING_DEVICE : SDL_HINT_AUDIO_ALSA_DEFAULT_PLAYBACK_DEVICE); + if (!devname) { + devname = SDL_GetHint(SDL_HINT_AUDIO_ALSA_DEFAULT_DEVICE); + if (!devname) { + devname = "default"; + } } - return "default"; + pcm_str = SDL_strdup(devname); + } else { + SDL_asprintf(&pcm_str, "%sCARD=%s", ALSA_device_prefix, dev->id); } - - return dev->name; + return pcm_str; } -// Swizzle channels to match SDL defaults. -// These are swizzles _from_ SDL's layouts to what ALSA wants. - -// 5.1 swizzle: -// https://bugzilla.libsdl.org/show_bug.cgi?id=110 -// "For Linux ALSA, this is FL-FR-RL-RR-C-LFE -// and for Windows DirectX [and CoreAudio], this is FL-FR-C-LFE-RL-RR" -static const int swizzle_alsa_channels_6[6] = { 0, 1, 4, 5, 2, 3 }; - -// 7.1 swizzle: -// https://docs.microsoft.com/en-us/windows-hardware/drivers/audio/mapping-stream-formats-to-speaker-configurations -// For Linux ALSA, this appears to be FL-FR-RL-RR-C-LFE-SL-SR -// and for Windows DirectX [and CoreAudio], this is FL-FR-C-LFE-SL-SR-RL-RR" -static const int swizzle_alsa_channels_8[8] = { 0, 1, 6, 7, 2, 3, 4, 5 }; - - - // This function waits until it is possible to write a full sound buffer static bool ALSA_WaitDevice(SDL_AudioDevice *device) { @@ -266,9 +352,9 @@ static bool ALSA_WaitDevice(SDL_AudioDevice *device) const int delay = SDL_max(fulldelay, 10); while (!SDL_GetAtomicInt(&device->shutdown)) { - const int rc = ALSA_snd_pcm_wait(device->hidden->pcm_handle, delay); + const int rc = ALSA_snd_pcm_wait(device->hidden->pcm, delay); if (rc < 0 && (rc != -EAGAIN)) { - const int status = ALSA_snd_pcm_recover(device->hidden->pcm_handle, rc, 0); + const int status = ALSA_snd_pcm_recover(device->hidden->pcm, rc, 0); if (status < 0) { // Hmm, not much we can do - abort SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "ALSA: snd_pcm_wait failed (unrecoverable): %s", ALSA_snd_strerror(rc)); @@ -295,12 +381,12 @@ static bool ALSA_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int bu snd_pcm_uframes_t frames_left = (snd_pcm_uframes_t) (buflen / frame_size); while ((frames_left > 0) && !SDL_GetAtomicInt(&device->shutdown)) { - const int rc = ALSA_snd_pcm_writei(device->hidden->pcm_handle, sample_buf, frames_left); + const int rc = ALSA_snd_pcm_writei(device->hidden->pcm, sample_buf, frames_left); //SDL_LogInfo(SDL_LOG_CATEGORY_AUDIO, "ALSA PLAYDEVICE: WROTE %d of %d bytes", (rc >= 0) ? ((int) (rc * frame_size)) : rc, (int) (frames_left * frame_size)); SDL_assert(rc != 0); // assuming this can't happen if we used snd_pcm_wait and queried for available space. if (rc < 0) { SDL_assert(rc != -EAGAIN); // assuming this can't happen if we used snd_pcm_wait and queried for available space. snd_pcm_recover won't handle it! - const int status = ALSA_snd_pcm_recover(device->hidden->pcm_handle, rc, 0); + const int status = ALSA_snd_pcm_recover(device->hidden->pcm, rc, 0); if (status < 0) { // Hmm, not much we can do - abort SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "ALSA write failed (unrecoverable): %s", ALSA_snd_strerror(rc)); @@ -318,12 +404,12 @@ static bool ALSA_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int bu static Uint8 *ALSA_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size) { - snd_pcm_sframes_t rc = ALSA_snd_pcm_avail(device->hidden->pcm_handle); + snd_pcm_sframes_t rc = ALSA_snd_pcm_avail(device->hidden->pcm); if (rc <= 0) { // Wait a bit and try again, maybe the hardware isn't quite ready yet? SDL_Delay(1); - rc = ALSA_snd_pcm_avail(device->hidden->pcm_handle); + rc = ALSA_snd_pcm_avail(device->hidden->pcm); if (rc <= 0) { // We'll catch it next time *buffer_size = 0; @@ -344,15 +430,15 @@ static int ALSA_RecordDevice(SDL_AudioDevice *device, void *buffer, int buflen) const int frame_size = SDL_AUDIO_FRAMESIZE(device->spec); SDL_assert((buflen % frame_size) == 0); - const snd_pcm_sframes_t total_available = ALSA_snd_pcm_avail(device->hidden->pcm_handle); + const snd_pcm_sframes_t total_available = ALSA_snd_pcm_avail(device->hidden->pcm); const int total_frames = SDL_min(buflen / frame_size, total_available); - const int rc = ALSA_snd_pcm_readi(device->hidden->pcm_handle, buffer, total_frames); + const int rc = ALSA_snd_pcm_readi(device->hidden->pcm, buffer, total_frames); SDL_assert(rc != -EAGAIN); // assuming this can't happen if we used snd_pcm_wait and queried for available space. snd_pcm_recover won't handle it! if (rc < 0) { - const int status = ALSA_snd_pcm_recover(device->hidden->pcm_handle, rc, 0); + const int status = ALSA_snd_pcm_recover(device->hidden->pcm, rc, 0); if (status < 0) { // Hmm, not much we can do - abort SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "ALSA read failed (unrecoverable): %s", ALSA_snd_strerror(rc)); @@ -368,455 +454,968 @@ static int ALSA_RecordDevice(SDL_AudioDevice *device, void *buffer, int buflen) static void ALSA_FlushRecording(SDL_AudioDevice *device) { - ALSA_snd_pcm_reset(device->hidden->pcm_handle); + ALSA_snd_pcm_reset(device->hidden->pcm); } static void ALSA_CloseDevice(SDL_AudioDevice *device) { if (device->hidden) { - if (device->hidden->pcm_handle) { + if (device->hidden->pcm) { // Wait for the submitted audio to drain. ALSA_snd_pcm_drop() can hang, so don't use that. SDL_Delay(((device->sample_frames * 1000) / device->spec.freq) * 2); - ALSA_snd_pcm_close(device->hidden->pcm_handle); + ALSA_snd_pcm_close(device->hidden->pcm); } SDL_free(device->hidden->mixbuf); SDL_free(device->hidden); } } -static int ALSA_set_buffer_size(SDL_AudioDevice *device, snd_pcm_hw_params_t *params) + +// To make easier to track parameters during the whole alsa pcm configuration: +struct ALSA_pcm_cfg_ctx { + SDL_AudioDevice *device; + + snd_pcm_hw_params_t *hwparams; + snd_pcm_sw_params_t *swparams; + + SDL_AudioFormat matched_sdl_format; + unsigned int chans_n; + unsigned int target_chans_n; + unsigned int rate; + snd_pcm_uframes_t persize; // alsa period size, SDL audio device sample_frames + snd_pcm_chmap_query_t **chmap_queries; + unsigned int sdl_chmap[SDL_AUDIO_ALSA__CHMAP_CHANS_N_MAX]; + unsigned int alsa_chmap_installed[SDL_AUDIO_ALSA__CHMAP_CHANS_N_MAX]; + + unsigned int periods; +}; +// The following are SDL channel maps with alsa position values, from 0 channels to 8 channels. +// See SDL3/SDL_audio.h +// Strictly speaking those are "parameters" of channel maps, like alsa hwparams and swparams, they +// have to be "reduced/refined" until an exact channel map. Only the 6 channels map requires such +// "reduction/refine". +static enum snd_pcm_chmap_position sdl_channel_maps[SDL_AUDIO_ALSA__SDL_CHMAPS_N][SDL_AUDIO_ALSA__CHMAP_CHANS_N_MAX] = { + // 0 channels + { + }, + // 1 channel + { + SND_CHMAP_MONO, + }, + // 2 channels + { + SND_CHMAP_FL, + SND_CHMAP_FR, + }, + // 3 channels + { + SND_CHMAP_FL, + SND_CHMAP_FR, + SND_CHMAP_LFE, + }, + // 4 channels + { + SND_CHMAP_FL, + SND_CHMAP_FR, + SND_CHMAP_RL, + SND_CHMAP_RR, + }, + // 5 channels + { + SND_CHMAP_FL, + SND_CHMAP_FR, + SND_CHMAP_LFE, + SND_CHMAP_RL, + SND_CHMAP_RR, + }, + // 6 channels + // XXX: here we encode not a uniq channel map but a set of channel maps. We will reduce it each + // time we are going to work with an alsa 6 channels map. + { + SND_CHMAP_FL, + SND_CHMAP_FR, + SND_CHMAP_FC, + SND_CHMAP_LFE, + // The 2 following channel positions are (SND_CHMAP_SL,SND_CHMAP_SR) or + // (SND_CHMAP_RL,SND_CHMAP_RR) + SND_CHMAP_UNKNOWN, + SND_CHMAP_UNKNOWN, + }, + // 7 channels + { + SND_CHMAP_FL, + SND_CHMAP_FR, + SND_CHMAP_FC, + SND_CHMAP_LFE, + SND_CHMAP_RC, + SND_CHMAP_SL, + SND_CHMAP_SR, + }, + // 8 channels + { + SND_CHMAP_FL, + SND_CHMAP_FR, + SND_CHMAP_FC, + SND_CHMAP_LFE, + SND_CHMAP_RL, + SND_CHMAP_RR, + SND_CHMAP_SL, + SND_CHMAP_SR, + }, +}; + +// Helper for the function right below. +static bool has_pos(unsigned int *chmap, unsigned int pos) +{ + for (unsigned int chan_idx = 0; ; chan_idx++) { + if (chan_idx == 6) { + return false; + } + if (chmap[chan_idx] == pos) { + return true; + } + } + SDL_assert(!"Shouldn't hit this code."); + return false; +} + +// XXX: Each time we are going to work on an alsa 6 channels map, we must reduce the set of channel +// maps which is encoded in sdl_channel_maps[6] to a uniq one. +#define HAVE_NONE 0 +#define HAVE_REAR 1 +#define HAVE_SIDE 2 +#define HAVE_BOTH 3 +static void sdl_6chans_set_rear_or_side_channels_from_alsa_6chans(unsigned int *sdl_6chans, unsigned int *alsa_6chans) +{ + // For alsa channel maps with 6 channels and with SND_CHMAP_FL,SND_CHMAP_FR,SND_CHMAP_FC, + // SND_CHMAP_LFE, reduce our 6 channels maps to a uniq one. + if ( !has_pos(alsa_6chans, SND_CHMAP_FL) || + !has_pos(alsa_6chans, SND_CHMAP_FR) || + !has_pos(alsa_6chans, SND_CHMAP_FC) || + !has_pos(alsa_6chans, SND_CHMAP_LFE)) { + sdl_6chans[4] = SND_CHMAP_UNKNOWN; + sdl_6chans[5] = SND_CHMAP_UNKNOWN; + LOGDEBUG("6channels:unsupported channel map"); + return; + } + + unsigned int state = HAVE_NONE; + for (unsigned int chan_idx = 0; chan_idx < 6; chan_idx++) { + if ((alsa_6chans[chan_idx] == SND_CHMAP_SL) || (alsa_6chans[chan_idx] == SND_CHMAP_SR)) { + if (state == HAVE_NONE) { + state = HAVE_SIDE; + } else if (state == HAVE_REAR) { + state = HAVE_BOTH; + break; + } + } else if ((alsa_6chans[chan_idx] == SND_CHMAP_RL) || (alsa_6chans[chan_idx] == SND_CHMAP_RR)) { + if (state == HAVE_NONE) { + state = HAVE_REAR; + } else if (state == HAVE_SIDE) { + state = HAVE_BOTH; + break; + } + } + } + + if ((state == HAVE_BOTH) || (state == HAVE_NONE)) { + sdl_6chans[4] = SND_CHMAP_UNKNOWN; + sdl_6chans[5] = SND_CHMAP_UNKNOWN; + LOGDEBUG("6channels:unsupported channel map"); + } else if (state == HAVE_REAR) { + sdl_6chans[4] = SND_CHMAP_RL; + sdl_6chans[5] = SND_CHMAP_RR; + LOGDEBUG("6channels:sdl map set to rear"); + } else { // state == HAVE_SIDE + sdl_6chans[4] = SND_CHMAP_SL; + sdl_6chans[5] = SND_CHMAP_SR; + LOGDEBUG("6channels:sdl map set to side"); + } +} +#undef HAVE_NONE +#undef HAVE_REAR +#undef HAVE_SIDE +#undef HAVE_BOTH + +static void swizzle_map_compute_alsa_subscan(struct ALSA_pcm_cfg_ctx *ctx, int *swizzle_map, unsigned int sdl_pos_idx) +{ + swizzle_map[sdl_pos_idx] = -1; + for (unsigned int alsa_pos_idx = 0; ; alsa_pos_idx++) { + SDL_assert(alsa_pos_idx != ctx->chans_n); // no 0 channels or not found matching position should happen here (actually enforce playback/recording symmetry). + if (ctx->alsa_chmap_installed[alsa_pos_idx] == ctx->sdl_chmap[sdl_pos_idx]) { + LOGDEBUG("swizzle SDL %u <-> alsa %u", sdl_pos_idx,alsa_pos_idx); + swizzle_map[sdl_pos_idx] = (int) alsa_pos_idx; + return; + } + } +} + +// XXX: this must stay playback/recording symetric. +static void swizzle_map_compute(struct ALSA_pcm_cfg_ctx *ctx, int *swizzle_map, bool *needs_swizzle) +{ + *needs_swizzle = false; + for (unsigned int sdl_pos_idx = 0; sdl_pos_idx != ctx->chans_n; sdl_pos_idx++) { + swizzle_map_compute_alsa_subscan(ctx, swizzle_map, sdl_pos_idx); + if (swizzle_map[sdl_pos_idx] != sdl_pos_idx) { + *needs_swizzle = true; + } + } +} + +#define CHMAP_INSTALLED 0 +#define CHANS_N_NEXT 1 +#define CHMAP_NOT_FOUND 2 +// Should always be a queried alsa channel map unless the queried alsa channel map was of type VAR, +// namely we can program the channel positions directly from the SDL channel map. +static int alsa_chmap_install(struct ALSA_pcm_cfg_ctx *ctx, unsigned int *chmap) +{ + bool isstack; + snd_pcm_chmap_t *chmap_to_install = (snd_pcm_chmap_t*)SDL_small_alloc(unsigned int, 1 + ctx->chans_n, &isstack); + if (!chmap_to_install) { + return -1; + } + + chmap_to_install->channels = ctx->chans_n; + SDL_memcpy(chmap_to_install->pos, chmap, sizeof (unsigned int) * ctx->chans_n); + + #if SDL_ALSA_DEBUG + char logdebug_chmap_str[128]; + ALSA_snd_pcm_chmap_print(chmap_to_install,sizeof(logdebug_chmap_str),logdebug_chmap_str); + LOGDEBUG("channel map to install:%s",logdebug_chmap_str); + #endif + + int status = ALSA_snd_pcm_set_chmap(ctx->device->hidden->pcm, chmap_to_install); + if (status < 0) { + SDL_SetError("ALSA: failed to install channel map: %s", ALSA_snd_strerror(status)); + return -1; + } + SDL_memcpy(ctx->alsa_chmap_installed, chmap, ctx->chans_n * sizeof (unsigned int)); + + SDL_small_free(chmap_to_install, isstack); + return CHMAP_INSTALLED; +} + +// We restrict the alsa channel maps because in the unordered matches we do only simple accounting. +// In the end, this will handle mostly alsa channel maps with more than one SND_CHMAP_NA position fillers. +static bool alsa_chmap_has_duplicate_position(struct ALSA_pcm_cfg_ctx *ctx, unsigned int *pos) +{ + if (ctx->chans_n < 2) {// we need at least 2 positions + LOGDEBUG("channel map:no duplicate"); + return false; + } + + for (unsigned int chan_idx = 1; chan_idx != ctx->chans_n; chan_idx++) { + for (unsigned int seen_idx = 0; seen_idx != chan_idx; seen_idx++) { + if (pos[seen_idx] == pos[chan_idx]) { + LOGDEBUG("channel map:have duplicate"); + return true; + } + } + } + + LOGDEBUG("channel map:no duplicate"); + return false; +} + +static int alsa_chmap_cfg_ordered_fixed_or_paired(struct ALSA_pcm_cfg_ctx *ctx) +{ + for (snd_pcm_chmap_query_t **chmap_query = ctx->chmap_queries; *chmap_query; chmap_query++) { + if ( ((*chmap_query)->map.channels != ctx->chans_n) || + (((*chmap_query)->type != SND_CHMAP_TYPE_FIXED) && ((*chmap_query)->type != SND_CHMAP_TYPE_PAIRED)) ) { + continue; + } + + #if SDL_ALSA_DEBUG + char logdebug_chmap_str[128]; + ALSA_snd_pcm_chmap_print(&(*chmap_query)->map,sizeof(logdebug_chmap_str),logdebug_chmap_str); + LOGDEBUG("channel map:ordered:fixed|paired:%s",logdebug_chmap_str); + #endif + + for (int i = 0; i < ctx->chans_n; i++) { + ctx->sdl_chmap[i] = (unsigned int) sdl_channel_maps[ctx->chans_n][i]; + } + + unsigned int *alsa_chmap = (*chmap_query)->map.pos; + if (ctx->chans_n == 6) { + sdl_6chans_set_rear_or_side_channels_from_alsa_6chans(ctx->sdl_chmap, alsa_chmap); + } + if (alsa_chmap_has_duplicate_position(ctx, alsa_chmap)) { + continue; + } + + for (unsigned int chan_idx = 0; ctx->sdl_chmap[chan_idx] == alsa_chmap[chan_idx]; chan_idx++) { + if (chan_idx == ctx->chans_n) { + return alsa_chmap_install(ctx, alsa_chmap); + } + } + } + return CHMAP_NOT_FOUND; +} + +// Here, the alsa channel positions can be programmed in the alsa frame (cf HDMI). +// If the alsa channel map is VAR, we only check we have the unordered set of channel positions we +// are looking for. +static int alsa_chmap_cfg_ordered_var(struct ALSA_pcm_cfg_ctx *ctx) +{ + for (snd_pcm_chmap_query_t **chmap_query = ctx->chmap_queries; *chmap_query; chmap_query++) { + if (((*chmap_query)->map.channels != ctx->chans_n) || ((*chmap_query)->type != SND_CHMAP_TYPE_VAR)) { + continue; + } + + #if SDL_ALSA_DEBUG + char logdebug_chmap_str[128]; + ALSA_snd_pcm_chmap_print(&(*chmap_query)->map,sizeof(logdebug_chmap_str),logdebug_chmap_str); + LOGDEBUG("channel map:ordered:var:%s",logdebug_chmap_str); + #endif + + for (int i = 0; i < ctx->chans_n; i++) { + ctx->sdl_chmap[i] = (unsigned int) sdl_channel_maps[ctx->chans_n][i]; + } + + unsigned int *alsa_chmap = (*chmap_query)->map.pos; + if (ctx->chans_n == 6) { + sdl_6chans_set_rear_or_side_channels_from_alsa_6chans(ctx->sdl_chmap, alsa_chmap); + } + if (alsa_chmap_has_duplicate_position(ctx, alsa_chmap)) { + continue; + } + + unsigned int pos_matches_n = 0; + for (unsigned int chan_idx = 0; chan_idx != ctx->chans_n; chan_idx++) { + for (unsigned int subscan_chan_idx = 0; subscan_chan_idx != ctx->chans_n; subscan_chan_idx++) { + if (ctx->sdl_chmap[chan_idx] == alsa_chmap[subscan_chan_idx]) { + pos_matches_n++; + break; + } + } + } + + if (pos_matches_n == ctx->chans_n) { + return alsa_chmap_install(ctx, ctx->sdl_chmap); // XXX: we program the SDL chmap here + } + } + + return CHMAP_NOT_FOUND; +} + +static int alsa_chmap_cfg_ordered(struct ALSA_pcm_cfg_ctx *ctx) +{ + const int status = alsa_chmap_cfg_ordered_fixed_or_paired(ctx); + return (status != CHMAP_NOT_FOUND) ? status : alsa_chmap_cfg_ordered_var(ctx); +} + +// In the unordered case, we are just interested to get the same unordered set of alsa channel +// positions than in the SDL channel map since we will swizzle (no duplicate channel position). +static int alsa_chmap_cfg_unordered(struct ALSA_pcm_cfg_ctx *ctx) +{ + for (snd_pcm_chmap_query_t **chmap_query = ctx->chmap_queries; *chmap_query; chmap_query++) { + if ( ((*chmap_query)->map.channels != ctx->chans_n) || + (((*chmap_query)->type != SND_CHMAP_TYPE_FIXED) && ((*chmap_query)->type != SND_CHMAP_TYPE_PAIRED)) ) { + continue; + } + + #if SDL_ALSA_DEBUG + char logdebug_chmap_str[128]; + ALSA_snd_pcm_chmap_print(&(*chmap_query)->map,sizeof(logdebug_chmap_str),logdebug_chmap_str); + LOGDEBUG("channel map:unordered:fixed|paired:%s",logdebug_chmap_str); + #endif + + for (int i = 0; i < ctx->chans_n; i++) { + ctx->sdl_chmap[i] = (unsigned int) sdl_channel_maps[ctx->chans_n][i]; + } + + unsigned int *alsa_chmap = (*chmap_query)->map.pos; + if (ctx->chans_n == 6) { + sdl_6chans_set_rear_or_side_channels_from_alsa_6chans(ctx->sdl_chmap, alsa_chmap); + } + + if (alsa_chmap_has_duplicate_position(ctx, alsa_chmap)) { + continue; + } + + unsigned int pos_matches_n = 0; + for (unsigned int chan_idx = 0; chan_idx != ctx->chans_n; chan_idx++) { + for (unsigned int subscan_chan_idx = 0; subscan_chan_idx != ctx->chans_n; subscan_chan_idx++) { + if (ctx->sdl_chmap[chan_idx] == alsa_chmap[subscan_chan_idx]) { + pos_matches_n++; + break; + } + } + } + + if (pos_matches_n == ctx->chans_n) { + return alsa_chmap_install(ctx, alsa_chmap); + } + } + + return CHMAP_NOT_FOUND; +} + +static int alsa_chmap_cfg(struct ALSA_pcm_cfg_ctx *ctx) { int status; - snd_pcm_hw_params_t *hwparams; - snd_pcm_uframes_t persize; - unsigned int periods; - // Copy the hardware parameters for this setup - snd_pcm_hw_params_alloca(&hwparams); - ALSA_snd_pcm_hw_params_copy(hwparams, params); - - // Attempt to match the period size to the requested buffer size - persize = device->sample_frames; - status = ALSA_snd_pcm_hw_params_set_period_size_near( - device->hidden->pcm_handle, hwparams, &persize, NULL); - if (status < 0) { - return -1; + ctx->chmap_queries = ALSA_snd_pcm_query_chmaps(ctx->device->hidden->pcm); + if (ctx->chmap_queries == NULL) { + // We couldn't query the channel map, assume no swizzle necessary + LOGDEBUG("couldn't query channel map, swizzling off"); + return CHMAP_INSTALLED; } - // Need to at least double buffer - periods = 2; - status = ALSA_snd_pcm_hw_params_set_periods_min( - device->hidden->pcm_handle, hwparams, &periods, NULL); - if (status < 0) { - return -1; + //---------------------------------------------------------------------------------------------- + status = alsa_chmap_cfg_ordered(ctx); // we prefer first channel maps we don't need to swizzle + if (status == CHMAP_INSTALLED) { + LOGDEBUG("swizzling off"); + return status; + } else if (status != CHMAP_NOT_FOUND) { + return status; // < 0 error code } - status = ALSA_snd_pcm_hw_params_set_periods_first( - device->hidden->pcm_handle, hwparams, &periods, NULL); - if (status < 0) { - return -1; + // Fall-thru + //---------------------------------------------------------------------------------------------- + status = alsa_chmap_cfg_unordered(ctx); // those we will have to swizzle + if (status == CHMAP_INSTALLED) { + LOGDEBUG("swizzling on"); + + bool isstack; + int *swizzle_map = SDL_small_alloc(int, ctx->chans_n, &isstack); + if (!swizzle_map) { + status = -1; + } else { + bool needs_swizzle; + swizzle_map_compute(ctx, swizzle_map, &needs_swizzle); // fine grained swizzle configuration + if (needs_swizzle) { + // let SDL's swizzler handle this one. + ctx->device->chmap = SDL_ChannelMapDup(swizzle_map, ctx->chans_n); + if (!ctx->device->chmap) { + status = -1; + } + } + SDL_small_free(swizzle_map, isstack); + } } - // "set" the hardware with the desired parameters - status = ALSA_snd_pcm_hw_params(device->hidden->pcm_handle, hwparams); - if (status < 0) { - return -1; + if (status == CHMAP_NOT_FOUND) { + return CHANS_N_NEXT; } - device->sample_frames = persize; - - // This is useful for debugging - snd_pcm_uframes_t bufsize; - ALSA_snd_pcm_hw_params_get_buffer_size(hwparams, &bufsize); - SDL_LogDebug(SDL_LOG_CATEGORY_AUDIO, - "ALSA: period size = %ld, periods = %u, buffer size = %lu", - persize, periods, bufsize); - return 0; + return status; // < 0 error code } +#define CHANS_N_SCAN_MODE__EQUAL_OR_ABOVE_REQUESTED_CHANS_N 0 // target more hardware pressure +#define CHANS_N_SCAN_MODE__BELOW_REQUESTED_CHANS_N 1 // target less hardware pressure +#define CHANS_N_CONFIGURED 0 +#define CHANS_N_NOT_CONFIGURED 1 +static int ALSA_pcm_cfg_hw_chans_n_scan(struct ALSA_pcm_cfg_ctx *ctx, unsigned int mode) +{ + unsigned int target_chans_n = ctx->device->spec.channels; // we start at what was specified + if (mode == CHANS_N_SCAN_MODE__BELOW_REQUESTED_CHANS_N) { + target_chans_n--; + } + while (true) { + if (mode == CHANS_N_SCAN_MODE__EQUAL_OR_ABOVE_REQUESTED_CHANS_N) { + if (target_chans_n > SDL_AUDIO_ALSA__CHMAP_CHANS_N_MAX) { + return CHANS_N_NOT_CONFIGURED; + } + // else: CHANS_N_SCAN_MODE__BELOW_REQUESTED_CHANS_N + } else if (target_chans_n == 0) { + return CHANS_N_NOT_CONFIGURED; + } + + LOGDEBUG("target chans_n is %u\n", target_chans_n); + + int status = ALSA_snd_pcm_hw_params_any(ctx->device->hidden->pcm, ctx->hwparams); + if (status < 0) { + SDL_SetError("ALSA: Couldn't get hardware config: %s", ALSA_snd_strerror(status)); + return -1; + } + // SDL only uses interleaved sample output + status = ALSA_snd_pcm_hw_params_set_access(ctx->device->hidden->pcm, ctx->hwparams, SND_PCM_ACCESS_RW_INTERLEAVED); + if (status < 0) { + SDL_SetError("ALSA: Couldn't set interleaved access: %s", ALSA_snd_strerror(status)); + return -1; + } + // Try for a closest match on audio format + snd_pcm_format_t alsa_format = 0; + const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(ctx->device->spec.format); + ctx->matched_sdl_format = 0; + while ((ctx->matched_sdl_format = *(closefmts++)) != 0) { + // XXX: we are forcing the same endianness, namely we won't need byte swapping upon + // writing/reading to/from the SDL audio buffer. + switch (ctx->matched_sdl_format) { + case SDL_AUDIO_U8: + alsa_format = SND_PCM_FORMAT_U8; + break; + case SDL_AUDIO_S8: + alsa_format = SND_PCM_FORMAT_S8; + break; + case SDL_AUDIO_S16LE: + alsa_format = SND_PCM_FORMAT_S16_LE; + break; + case SDL_AUDIO_S16BE: + alsa_format = SND_PCM_FORMAT_S16_BE; + break; + case SDL_AUDIO_S32LE: + alsa_format = SND_PCM_FORMAT_S32_LE; + break; + case SDL_AUDIO_S32BE: + alsa_format = SND_PCM_FORMAT_S32_BE; + break; + case SDL_AUDIO_F32LE: + alsa_format = SND_PCM_FORMAT_FLOAT_LE; + break; + case SDL_AUDIO_F32BE: + alsa_format = SND_PCM_FORMAT_FLOAT_BE; + break; + default: + continue; + } + if (ALSA_snd_pcm_hw_params_set_format(ctx->device->hidden->pcm, ctx->hwparams, alsa_format) >= 0) { + break; + } + } + if (ctx->matched_sdl_format == 0) { + SDL_SetError("ALSA: Unsupported audio format: %s", ALSA_snd_strerror(status)); + return -1; + } + // let alsa approximate the number of channels + ctx->chans_n = target_chans_n; + status = ALSA_snd_pcm_hw_params_set_channels_near(ctx->device->hidden->pcm, ctx->hwparams, &(ctx->chans_n)); + if (status < 0) { + SDL_SetError("ALSA: Couldn't set audio channels: %s", ALSA_snd_strerror(status)); + return -1; + } + // let alsa approximate the audio rate + ctx->rate = ctx->device->spec.freq; + status = ALSA_snd_pcm_hw_params_set_rate_near(ctx->device->hidden->pcm, ctx->hwparams, &(ctx->rate), NULL); + if (status < 0) { + SDL_SetError("ALSA: Couldn't set audio frequency: %s", ALSA_snd_strerror(status)); + return -1; + } + // let approximate the period size to the requested buffer size + ctx->persize = ctx->device->sample_frames; + status = ALSA_snd_pcm_hw_params_set_period_size_near(ctx->device->hidden->pcm, ctx->hwparams, &(ctx->persize), NULL); + if (status < 0) { + SDL_SetError("ALSA: Couldn't set the period size: %s", ALSA_snd_strerror(status)); + return -1; + } + // let approximate the minimun number of periods per buffer (we target a double buffer) + ctx->periods = 2; + status = ALSA_snd_pcm_hw_params_set_periods_min(ctx->device->hidden->pcm, ctx->hwparams, &(ctx->periods), NULL); + if (status < 0) { + SDL_SetError("ALSA: Couldn't set the minimum number of periods per buffer: %s", ALSA_snd_strerror(status)); + return -1; + } + // restrict the number of periods per buffer to an approximation of the approximated minimum + // number of periods per buffer done right above + status = ALSA_snd_pcm_hw_params_set_periods_first(ctx->device->hidden->pcm, ctx->hwparams, &(ctx->periods), NULL); + if (status < 0) { + SDL_SetError("ALSA: Couldn't set the number of periods per buffer: %s", ALSA_snd_strerror(status)); + return -1; + } + // install the hw parameters + status = ALSA_snd_pcm_hw_params(ctx->device->hidden->pcm, ctx->hwparams); + if (status < 0) { + SDL_SetError("ALSA: installation of hardware parameter failed: %s", ALSA_snd_strerror(status)); + return -1; + } + //========================================================================================== + // Here the alsa pcm is in SND_PCM_STATE_PREPARED state, let's figure out a good fit for + // SDL channel map, it may request to change the target number of channels though. + status = alsa_chmap_cfg(ctx); + if (status < 0) { + return status; // we forward the SDL error + } else if (status == CHMAP_INSTALLED) { + return CHANS_N_CONFIGURED; // we are finished here + } + + // status == CHANS_N_NEXT + ALSA_snd_pcm_free_chmaps(ctx->chmap_queries); + ALSA_snd_pcm_hw_free(ctx->device->hidden->pcm); // uninstall those hw params + + if (mode == CHANS_N_SCAN_MODE__EQUAL_OR_ABOVE_REQUESTED_CHANS_N) { + target_chans_n++; + } else { // CHANS_N_SCAN_MODE__BELOW_REQUESTED_CHANS_N + target_chans_n--; + } + } + + SDL_assert(!"Shouldn't reach this code."); + return CHANS_N_NOT_CONFIGURED; +} +#undef CHMAP_INSTALLED +#undef CHANS_N_NEXT +#undef CHMAP_NOT_FOUND + +static bool ALSA_pcm_cfg_hw(struct ALSA_pcm_cfg_ctx *ctx) +{ + LOGDEBUG("target chans_n, equal or above requested chans_n mode\n"); + int status = ALSA_pcm_cfg_hw_chans_n_scan(ctx, CHANS_N_SCAN_MODE__EQUAL_OR_ABOVE_REQUESTED_CHANS_N); + if (status < 0) { // something went too wrong + return false; + } else if (status == CHANS_N_CONFIGURED) { + return true; + } + + // Here, status == CHANS_N_NOT_CONFIGURED + LOGDEBUG("target chans_n, below requested chans_n mode\n"); + status = ALSA_pcm_cfg_hw_chans_n_scan(ctx, CHANS_N_SCAN_MODE__BELOW_REQUESTED_CHANS_N); + if (status < 0) { // something went too wrong + return false; + } else if (status == CHANS_N_CONFIGURED) { + return true; + } + + // Here, status == CHANS_N_NOT_CONFIGURED + return SDL_SetError("ALSA: Coudn't configure targetting any SDL supported channel number"); +} +#undef CHANS_N_SCAN_MODE__EQUAL_OR_ABOVE_REQUESTED_CHANS_N +#undef CHANS_N_SCAN_MODE__BELOW_REQUESTED_CHANS_N +#undef CHANS_N_CONFIGURED +#undef CHANS_N_NOT_CONFIGURED + + +static bool ALSA_pcm_cfg_sw(struct ALSA_pcm_cfg_ctx *ctx) +{ + int status; + + status = ALSA_snd_pcm_sw_params_current(ctx->device->hidden->pcm, ctx->swparams); + if (status < 0) { + return SDL_SetError("ALSA: Couldn't get software config: %s", ALSA_snd_strerror(status)); + } + + status = ALSA_snd_pcm_sw_params_set_avail_min(ctx->device->hidden->pcm, ctx->swparams, ctx->persize); // will become device->sample_frames if the alsa pcm configuration is successful + if (status < 0) { + return SDL_SetError("Couldn't set minimum available samples: %s", ALSA_snd_strerror(status)); + } + + status = ALSA_snd_pcm_sw_params_set_start_threshold(ctx->device->hidden->pcm, ctx->swparams, 1); + if (status < 0) { + return SDL_SetError("ALSA: Couldn't set start threshold: %s", ALSA_snd_strerror(status)); + } + status = ALSA_snd_pcm_sw_params(ctx->device->hidden->pcm, ctx->swparams); + if (status < 0) { + return SDL_SetError("Couldn't set software audio parameters: %s", ALSA_snd_strerror(status)); + } + return true; +} + + static bool ALSA_OpenDevice(SDL_AudioDevice *device) { const bool recording = device->recording; + struct ALSA_pcm_cfg_ctx cfg_ctx; // used to track everything here + char *pcm_str; int status = 0; + //device->spec.channels = 8; + //SDL_SetLogPriority(SDL_LOG_CATEGORY_AUDIO, SDL_LOG_PRIORITY_VERBOSE); + LOGDEBUG("channels requested %u",device->spec.channels); + // XXX: We do not use the SDL internal swizzler yet. + device->chmap = NULL; + + SDL_zero(cfg_ctx); + cfg_ctx.device = device; + // Initialize all variables that we clean on shutdown - device->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*device->hidden)); - if (!device->hidden) { + cfg_ctx.device->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*cfg_ctx.device->hidden)); + if (!cfg_ctx.device->hidden) { return false; } // Open the audio device - // Name of device should depend on # channels in spec - snd_pcm_t *pcm_handle = NULL; - status = ALSA_snd_pcm_open(&pcm_handle, - get_audio_device(device->handle, device->spec.channels), + pcm_str = get_pcm_str(cfg_ctx.device->handle); + if (pcm_str == NULL) { + goto err_free_device_hidden; + } + LOGDEBUG("PCM open '%s'", pcm_str); + status = ALSA_snd_pcm_open(&cfg_ctx.device->hidden->pcm, + pcm_str, recording ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); - + SDL_free(pcm_str); if (status < 0) { - return SDL_SetError("ALSA: Couldn't open audio device: %s", ALSA_snd_strerror(status)); + SDL_SetError("ALSA: Couldn't open audio device: %s", ALSA_snd_strerror(status)); + goto err_free_device_hidden; } - device->hidden->pcm_handle = pcm_handle; + // Now we need to configure the opened pcm as close as possible from the requested parameters we + // can reasonably deal with (and that could change) + snd_pcm_hw_params_alloca(&(cfg_ctx.hwparams)); + snd_pcm_sw_params_alloca(&(cfg_ctx.swparams)); - // Figure out what the hardware is capable of - snd_pcm_hw_params_t *hwparams = NULL; - snd_pcm_hw_params_alloca(&hwparams); - status = ALSA_snd_pcm_hw_params_any(pcm_handle, hwparams); - if (status < 0) { - return SDL_SetError("ALSA: Couldn't get hardware config: %s", ALSA_snd_strerror(status)); + if (!ALSA_pcm_cfg_hw(&cfg_ctx)) { // alsa pcm "hardware" part of the pcm + goto err_close_pcm; } - // SDL only uses interleaved sample output - status = ALSA_snd_pcm_hw_params_set_access(pcm_handle, hwparams, - SND_PCM_ACCESS_RW_INTERLEAVED); - if (status < 0) { - return SDL_SetError("ALSA: Couldn't set interleaved access: %s", ALSA_snd_strerror(status)); - } - - // Try for a closest match on audio format - snd_pcm_format_t format = 0; - const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(device->spec.format); - SDL_AudioFormat test_format; - while ((test_format = *(closefmts++)) != 0) { - switch (test_format) { - case SDL_AUDIO_U8: - format = SND_PCM_FORMAT_U8; - break; - case SDL_AUDIO_S8: - format = SND_PCM_FORMAT_S8; - break; - case SDL_AUDIO_S16LE: - format = SND_PCM_FORMAT_S16_LE; - break; - case SDL_AUDIO_S16BE: - format = SND_PCM_FORMAT_S16_BE; - break; - case SDL_AUDIO_S32LE: - format = SND_PCM_FORMAT_S32_LE; - break; - case SDL_AUDIO_S32BE: - format = SND_PCM_FORMAT_S32_BE; - break; - case SDL_AUDIO_F32LE: - format = SND_PCM_FORMAT_FLOAT_LE; - break; - case SDL_AUDIO_F32BE: - format = SND_PCM_FORMAT_FLOAT_BE; - break; - default: - continue; - } - if (ALSA_snd_pcm_hw_params_set_format(pcm_handle, hwparams, format) >= 0) { - break; - } - } - if (!test_format) { - return SDL_SetError("ALSA: Unsupported audio format"); - } - device->spec.format = test_format; - - // Set the number of channels - status = ALSA_snd_pcm_hw_params_set_channels(pcm_handle, hwparams, - device->spec.channels); - unsigned int channels = device->spec.channels; - if (status < 0) { - status = ALSA_snd_pcm_hw_params_get_channels(hwparams, &channels); - if (status < 0) { - return SDL_SetError("ALSA: Couldn't set audio channels"); - } - device->spec.channels = channels; - } - - const int *swizmap = NULL; - if (channels == 6) { - swizmap = swizzle_alsa_channels_6; - } else if (channels == 8) { - swizmap = swizzle_alsa_channels_8; - } - -#ifdef SND_CHMAP_API_VERSION - if (swizmap) { - snd_pcm_chmap_t *chmap = ALSA_snd_pcm_get_chmap(pcm_handle); - if (chmap) { - char chmap_str[64]; - if (ALSA_snd_pcm_chmap_print(chmap, sizeof(chmap_str), chmap_str) > 0) { - if ( (channels == 6) && - ((SDL_strcmp("FL FR FC LFE RL RR", chmap_str) == 0) || - (SDL_strcmp("FL FR FC LFE SL SR", chmap_str) == 0)) ) { - swizmap = NULL; - } else if ((channels == 8) && (SDL_strcmp("FL FR FC LFE SL SR RL RR", chmap_str) == 0)) { - swizmap = NULL; - } - } - free(chmap); // This should NOT be SDL_free() - } - } -#endif // SND_CHMAP_API_VERSION - - // Validate number of channels and determine if swizzling is necessary. - // Assume original swizzling, until proven otherwise. - if (swizmap) { - device->chmap = SDL_ChannelMapDup(swizmap, channels); - if (!device->chmap) { - return false; - } - } - - // Set the audio rate - unsigned int rate = device->spec.freq; - status = ALSA_snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, - &rate, NULL); - if (status < 0) { - return SDL_SetError("ALSA: Couldn't set audio frequency: %s", ALSA_snd_strerror(status)); - } - device->spec.freq = rate; - - // Set the buffer size, in samples - status = ALSA_set_buffer_size(device, hwparams); - if (status < 0) { - return SDL_SetError("Couldn't set hardware audio parameters: %s", ALSA_snd_strerror(status)); - } - - // Set the software parameters - snd_pcm_sw_params_t *swparams = NULL; - snd_pcm_sw_params_alloca(&swparams); - status = ALSA_snd_pcm_sw_params_current(pcm_handle, swparams); - if (status < 0) { - return SDL_SetError("ALSA: Couldn't get software config: %s", ALSA_snd_strerror(status)); - } - status = ALSA_snd_pcm_sw_params_set_avail_min(pcm_handle, swparams, device->sample_frames); - if (status < 0) { - return SDL_SetError("Couldn't set minimum available samples: %s", ALSA_snd_strerror(status)); - } - status = - ALSA_snd_pcm_sw_params_set_start_threshold(pcm_handle, swparams, 1); - if (status < 0) { - return SDL_SetError("ALSA: Couldn't set start threshold: %s", ALSA_snd_strerror(status)); - } - status = ALSA_snd_pcm_sw_params(pcm_handle, swparams); - if (status < 0) { - return SDL_SetError("Couldn't set software audio parameters: %s", ALSA_snd_strerror(status)); + // from here, we get only the alsa chmap queries in cfg_ctx to explicitely clean, hwparams is + // uninstalled upon pcm closing + + // This is useful for debugging + #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, + "ALSA: period size = %ld, periods = %u, buffer size = %lu", + cfg_ctx.persize, cfg_ctx.periods, bufsize); + #endif + + if (!ALSA_pcm_cfg_sw(&cfg_ctx)) { // alsa pcm "software" part of the pcm + goto err_cleanup_ctx; } + // Now we can update the following parameters in the spec: + cfg_ctx.device->spec.format = cfg_ctx.matched_sdl_format; + cfg_ctx.device->spec.channels = cfg_ctx.chans_n; + cfg_ctx.device->spec.freq = cfg_ctx.rate; + cfg_ctx.device->sample_frames = cfg_ctx.persize; // Calculate the final parameters for this audio specification - SDL_UpdatedAudioDeviceFormat(device); + SDL_UpdatedAudioDeviceFormat(cfg_ctx.device); // Allocate mixing buffer if (!recording) { - device->hidden->mixbuf = (Uint8 *)SDL_malloc(device->buffer_size); - if (!device->hidden->mixbuf) { - return false; + cfg_ctx.device->hidden->mixbuf = (Uint8 *)SDL_malloc(cfg_ctx.device->buffer_size); + if (cfg_ctx.device->hidden->mixbuf == NULL) { + goto err_cleanup_ctx; } - SDL_memset(device->hidden->mixbuf, device->silence_value, device->buffer_size); + SDL_memset(cfg_ctx.device->hidden->mixbuf, cfg_ctx.device->silence_value, cfg_ctx.device->buffer_size); } #if !SDL_ALSA_NON_BLOCKING if (!recording) { - ALSA_snd_pcm_nonblock(pcm_handle, 0); + ALSA_snd_pcm_nonblock(cfg_ctx.device->hidden->pcm, 0); } #endif - - ALSA_snd_pcm_start(pcm_handle); - + ALSA_snd_pcm_start(cfg_ctx.device->hidden->pcm); return true; // We're ready to rock and roll. :-) -} -static void add_device(const bool recording, const char *name, void *hint, ALSA_Device **pSeen) -{ - ALSA_Device *dev = SDL_malloc(sizeof(ALSA_Device)); - char *desc; - char *ptr; - - if (!dev) { - return; - } - - // Not all alsa devices are enumerable via snd_device_name_get_hint - // (i.e. bluetooth devices). Therefore if hint is passed in to this - // function as NULL, assume name contains desc. - // Make sure not to free the storage associated with desc in this case - if (hint) { - desc = ALSA_snd_device_name_get_hint(hint, "DESC"); - if (!desc) { - SDL_free(dev); - return; - } - } else { - desc = (char *)name; - } - - SDL_assert(name != NULL); - - // some strings have newlines, like "HDA NVidia, HDMI 0\nHDMI Audio Output". - // just chop the extra lines off, this seems to get a reasonable device - // name without extra details. - ptr = SDL_strchr(desc, '\n'); - if (ptr) { - *ptr = '\0'; - } - - //SDL_LogInfo(SDL_LOG_CATEGORY_AUDIO, "ALSA: adding %s device '%s' (%s)", recording ? "recording" : "playback", name, desc); - - dev->name = SDL_strdup(name); - if (!dev->name) { - if (hint) { - free(desc); // This should NOT be SDL_free() - } - SDL_free(dev->name); - SDL_free(dev); - return; - } - - // Note that spec is NULL, because we are required to open the device before - // acquiring the mix format, making this information inaccessible at - // enumeration time - SDL_AddAudioDevice(recording, desc, NULL, dev); - if (hint) { - free(desc); // This should NOT be SDL_free() - } - - dev->recording = recording; - dev->next = *pSeen; - *pSeen = dev; +err_cleanup_ctx: + ALSA_snd_pcm_free_chmaps(cfg_ctx.chmap_queries); +err_close_pcm: + ALSA_snd_pcm_close(cfg_ctx.device->hidden->pcm); +err_free_device_hidden: + SDL_free(cfg_ctx.device->hidden); + cfg_ctx.device->hidden = NULL; + return false; } static ALSA_Device *hotplug_devices = NULL; -static void ALSA_HotplugIteration(bool *has_default_playback, bool *has_default_recording) +static int hotplug_device_process(snd_ctl_t *ctl, snd_ctl_card_info_t *ctl_card_info, int dev_idx, + snd_pcm_stream_t direction, ALSA_Device **unseen, ALSA_Device **seen) { - void **hints = NULL; - ALSA_Device *unseen = NULL; - ALSA_Device *seen = NULL; + unsigned int subdevs_n = 1; // we have at least one subdevice (substream since the direction is a stream in alsa terminology) + unsigned int subdev_idx = 0; + const bool recording = direction == SND_PCM_STREAM_CAPTURE ? true : false; // used for the unicity of the device + bool isstack; + snd_pcm_info_t *pcm_info = (snd_pcm_info_t*)SDL_small_alloc(Uint8, ALSA_snd_pcm_info_sizeof(), &isstack); + SDL_memset(pcm_info, 0, ALSA_snd_pcm_info_sizeof()); - if (ALSA_snd_device_name_hint(-1, "pcm", &hints) == 0) { - const char *match = NULL; - int bestmatch = 0xFFFF; - int has_default = -1; - size_t match_len = 0; - static const char *const prefixes[] = { - "hw:", "sysdefault:", "default:", NULL - }; + while (true) { + ALSA_snd_pcm_info_set_stream(pcm_info, direction); + ALSA_snd_pcm_info_set_device(pcm_info, dev_idx); + ALSA_snd_pcm_info_set_subdevice(pcm_info, subdev_idx); // we have at least one subdevice (substream) of index 0 - unseen = hotplug_devices; - seen = NULL; + const int r = ALSA_snd_ctl_pcm_info(ctl, pcm_info); + if (r < 0) { + SDL_small_free(pcm_info, isstack); + // first call to ALSA_snd_ctl_pcm_info + if (subdev_idx == 0 && r == -ENOENT) { // no such direction/stream for this device + return 0; + } + return -1; + } - // Apparently there are several different ways that ALSA lists - // actual hardware. It could be prefixed with "hw:" or "default:" - // or "sysdefault:" and maybe others. Go through the list and see - // if we can find a preferred prefix for the system. - for (int i = 0; hints[i]; i++) { - char *name = ALSA_snd_device_name_get_hint(hints[i], "NAME"); - if (!name) { - continue; + if (subdev_idx == 0) { + subdevs_n = ALSA_snd_pcm_info_get_subdevices_count(pcm_info); + } + + // building the unseen list scanning the list of hotplug devices, if it is already there + // using the id, move it to the seen list. + ALSA_Device *unseen_prev_adev = NULL; + ALSA_Device *adev; + for (adev = *unseen; adev; adev = adev->next) { + // the unicity key is the couple (id,recording) + if ((SDL_strcmp(adev->id, ALSA_snd_ctl_card_info_get_id(ctl_card_info)) == 0) && (adev->recording == recording)) { + // unchain from unseen + if (*unseen == adev) { // head + *unseen = adev->next; + } else { + unseen_prev_adev->next = adev->next; + } + // chain to seen + adev->next = *seen; + *seen = adev; + break; + } + unseen_prev_adev = adev; + } + + if (adev == NULL) { // newly seen device + adev = SDL_calloc(1, sizeof(*adev)); + if (adev == NULL) { + SDL_small_free(pcm_info, isstack); + return -1; } - if (SDL_strcmp(name, "default") == 0) { - if (has_default < 0) { - has_default = i; - } + adev->id = SDL_strdup(ALSA_snd_ctl_card_info_get_id(ctl_card_info)); + if (adev->id == NULL) { + SDL_small_free(pcm_info, isstack); + SDL_free(adev); + return -1; + } + + if (SDL_asprintf(&adev->name, "%s:%s", ALSA_snd_ctl_card_info_get_name(ctl_card_info), ALSA_snd_pcm_info_get_name(pcm_info)) == -1) { + SDL_small_free(pcm_info, isstack); + SDL_free(adev->id); + SDL_free(adev); + return -1; + } + + if (direction == SND_PCM_STREAM_CAPTURE) { + adev->recording = true; } else { - for (int j = 0; prefixes[j]; j++) { - const char *prefix = prefixes[j]; - const size_t prefixlen = SDL_strlen(prefix); - if (SDL_strncmp(name, prefix, prefixlen) == 0) { - if (j < bestmatch) { - bestmatch = j; - match = prefix; - match_len = prefixlen; - } - } - } - - free(name); // This should NOT be SDL_free() + adev->recording = false; } - } - // look through the list of device names to find matches - if (match || (has_default >= 0)) { // did we find a device name prefix we like at all...? - for (int i = 0; hints[i]; i++) { - char *name = ALSA_snd_device_name_get_hint(hints[i], "NAME"); - if (!name) { - continue; - } - - // only want physical hardware interfaces - const bool is_default = (has_default == i); - if (is_default || (match && SDL_strncmp(name, match, match_len) == 0)) { - char *ioid = ALSA_snd_device_name_get_hint(hints[i], "IOID"); - const bool isoutput = (!ioid) || (SDL_strcmp(ioid, "Output") == 0); - const bool isinput = (!ioid) || (SDL_strcmp(ioid, "Input") == 0); - bool have_output = false; - bool have_input = false; - - free(ioid); // This should NOT be SDL_free() - - if (!isoutput && !isinput) { - free(name); // This should NOT be SDL_free() - continue; - } - - if (is_default) { - if (has_default_playback && isoutput) { - *has_default_playback = true; - } else if (has_default_recording && isinput) { - *has_default_recording = true; - } - free(name); // This should NOT be SDL_free() - continue; - } - - ALSA_Device *prev = NULL; - ALSA_Device *next; - for (ALSA_Device *dev = unseen; dev; dev = next) { - next = dev->next; - if ((SDL_strcmp(dev->name, name) == 0) && (((isinput) && dev->recording) || ((isoutput) && !dev->recording))) { - if (prev) { - prev->next = next; - } else { - unseen = next; - } - dev->next = seen; - seen = dev; - if (isinput) { - have_input = true; - } - if (isoutput) { - have_output = true; - } - } else { - prev = dev; - } - } - - if (isinput && !have_input) { - add_device(true, name, hints[i], &seen); - } - if (isoutput && !have_output) { - add_device(false, name, hints[i], &seen); - } - } - - free(name); // This should NOT be SDL_free() + if (SDL_AddAudioDevice(recording, adev->name, NULL, adev) == NULL) { + SDL_small_free(pcm_info, isstack); + SDL_free(adev->id); + SDL_free(adev->name); + SDL_free(adev); + return -1; } + + adev->next = *seen; + *seen = adev; } - ALSA_snd_device_name_free_hint(hints); - - hotplug_devices = seen; // now we have a known-good list of attached devices. - - // report anything still in unseen as removed. - ALSA_Device *next = NULL; - for (ALSA_Device *dev = unseen; dev; dev = next) { - //SDL_LogInfo(SDL_LOG_CATEGORY_AUDIO, "ALSA: removing %s device '%s'", dev->recording ? "recording" : "playback", dev->name); - next = dev->next; - SDL_AudioDeviceDisconnected(SDL_FindPhysicalAudioDeviceByHandle(dev)); - SDL_free(dev->name); - SDL_free(dev); + subdev_idx++; + if (subdev_idx == subdevs_n) { + SDL_small_free(pcm_info, isstack); + return 0; } + + SDL_memset(pcm_info, 0, ALSA_snd_pcm_info_sizeof()); } + + SDL_small_free(pcm_info, isstack); + SDL_assert(!"Shouldn't reach this code"); + return -1; } +static void ALSA_HotplugIteration(bool *has_default_output, bool *has_default_recording) +{ + if (has_default_output != NULL) { + *has_default_output = true; + } + + if (has_default_recording != NULL) { + *has_default_recording = true; + } + + bool isstack; + snd_ctl_card_info_t *ctl_card_info = (snd_ctl_card_info_t *) SDL_small_alloc(Uint8, ALSA_snd_ctl_card_info_sizeof(), &isstack); + if (!ctl_card_info) { + return; // oh well. + } + + SDL_memset(ctl_card_info, 0, ALSA_snd_ctl_card_info_sizeof()); + + snd_ctl_t *ctl = NULL; + ALSA_Device *unseen = hotplug_devices; + ALSA_Device *seen = NULL; + int card_idx = -1; + while (true) { + int r = ALSA_snd_card_next(&card_idx); + if (r < 0) { + goto failed; + } else if (card_idx == -1) { + break; + } + + char ctl_name[64]; + SDL_snprintf(ctl_name, sizeof (ctl_name), "%s%d", ALSA_device_prefix, card_idx); // card_idx >= 0 + LOGDEBUG("hotplug ctl_name = '%s'", ctl_name); + + r = ALSA_snd_ctl_open(&ctl, ctl_name, 0); + if (r < 0) { + continue; + } + + r = ALSA_snd_ctl_card_info(ctl, ctl_card_info); + if (r < 0) { + goto failed; + } + + int dev_idx = -1; + while (true) { + r = ALSA_snd_ctl_pcm_next_device(ctl, &dev_idx); + if (r < 0) { + goto failed; + } else if (dev_idx == -1) { + break; + } + + r = hotplug_device_process(ctl, ctl_card_info, dev_idx, SND_PCM_STREAM_PLAYBACK, &unseen, &seen); + if (r < 0) { + goto failed; + } + + r = hotplug_device_process(ctl, ctl_card_info, dev_idx, SND_PCM_STREAM_CAPTURE, &unseen, &seen); + if (r < 0) { + goto failed; + } + } + ALSA_snd_ctl_close(ctl); + ALSA_snd_ctl_card_info_clear(ctl_card_info); + } + + // remove only the unseen devices + while (unseen) { + SDL_AudioDeviceDisconnected(SDL_FindPhysicalAudioDeviceByHandle(unseen)); + SDL_free(unseen->name); + SDL_free(unseen->id); + ALSA_Device *next = unseen->next; + SDL_free(unseen); + unseen = next; + } + + // update hotplug devices to be the seen devices + hotplug_devices = seen; + SDL_small_free(ctl_card_info, isstack); + return; + +failed: + if (ctl) { + ALSA_snd_ctl_close(ctl); + } + + // remove the unseen + while (unseen) { + SDL_AudioDeviceDisconnected(SDL_FindPhysicalAudioDeviceByHandle(unseen)); + SDL_free(unseen->name); + SDL_free(unseen->id); + ALSA_Device *next = unseen->next; + SDL_free(unseen); + unseen = next; + } + + // remove the seen + while (seen) { + SDL_AudioDeviceDisconnected(SDL_FindPhysicalAudioDeviceByHandle(seen)); + SDL_free(seen->name); + SDL_free(seen->id); + ALSA_Device *next = seen->next; + SDL_free(seen); + seen = next; + } + + hotplug_devices = NULL; + SDL_small_free(ctl_card_info, isstack); +} + + #if SDL_ALSA_HOTPLUG_THREAD static SDL_AtomicInt ALSA_hotplug_shutdown; static SDL_Thread *ALSA_hotplug_thread; @@ -828,7 +1427,7 @@ static int SDLCALL ALSA_HotplugThread(void *arg) while (!SDL_GetAtomicInt(&ALSA_hotplug_shutdown)) { // Block awhile before checking again, unless we're told to stop. const Uint64 ticks = SDL_GetTicks() + 5000; - while (!SDL_GetAtomicInt(&ALSA_hotplug_shutdown) && SDL_GetTicks() < ticks) { + while (!SDL_GetAtomicInt(&ALSA_hotplug_shutdown) && (SDL_GetTicks() < ticks)) { SDL_Delay(100); } @@ -841,6 +1440,8 @@ static int SDLCALL ALSA_HotplugThread(void *arg) static void ALSA_DetectDevices(SDL_AudioDevice **default_playback, SDL_AudioDevice **default_recording) { + ALSA_guess_device_prefix(); + // ALSA doesn't have a concept of a changeable default device, afaik, so we expose a generic default // device here. It's the best we can do at this level. bool has_default_playback = false, has_default_recording = false; diff --git a/libs/SDL3/src/audio/alsa/SDL_alsa_audio.h b/libs/SDL3/src/audio/alsa/SDL_alsa_audio.h index 1209fe2d..e840b2f6 100644 --- a/libs/SDL3/src/audio/alsa/SDL_alsa_audio.h +++ b/libs/SDL3/src/audio/alsa/SDL_alsa_audio.h @@ -27,16 +27,15 @@ #include "../SDL_sysaudio.h" +#define SDL_AUDIO_ALSA__CHMAP_CHANS_N_MAX 8 +#define SDL_AUDIO_ALSA__SDL_CHMAPS_N 9 // from 0 channels to 8 channels struct SDL_PrivateAudioData { // The audio device handle - snd_pcm_t *pcm_handle; + snd_pcm_t *pcm; // Raw mixing buffer Uint8 *mixbuf; - - // swizzle function - void (*swizzle_func)(SDL_AudioDevice *_this, void *buffer, Uint32 bufferlen); }; #endif // SDL_ALSA_audio_h_ diff --git a/libs/SDL3/src/audio/coreaudio/SDL_coreaudio.m b/libs/SDL3/src/audio/coreaudio/SDL_coreaudio.m index 9b6b8198..76b6e326 100644 --- a/libs/SDL3/src/audio/coreaudio/SDL_coreaudio.m +++ b/libs/SDL3/src/audio/coreaudio/SDL_coreaudio.m @@ -436,7 +436,12 @@ static bool UpdateAudioSession(SDL_AudioDevice *device, bool open, bool allow_pl } } } else if (data.playback && data.recording) { - category = AVAudioSessionCategoryPlayAndRecord; + if (allow_playandrecord) { + category = AVAudioSessionCategoryPlayAndRecord; + } else { + // We already failed play and record with AVAudioSessionErrorCodeResourceNotAvailable + return false; + } } else if (data.recording) { category = AVAudioSessionCategoryRecord; } @@ -494,12 +499,15 @@ static bool UpdateAudioSession(SDL_AudioDevice *device, bool open, bool allow_pl if (![session setActive:YES error:&err]) { if ([err code] == AVAudioSessionErrorCodeResourceNotAvailable && category == AVAudioSessionCategoryPlayAndRecord) { - return UpdateAudioSession(device, open, false); + if (UpdateAudioSession(device, open, false)) { + return true; + } else { + return SDL_SetError("Could not activate Audio Session: Resource not available"); + } } NSString *desc = err.description; - SDL_SetError("Could not activate Audio Session: %s", desc.UTF8String); - return false; + return SDL_SetError("Could not activate Audio Session: %s", desc.UTF8String); } session_active = true; ResumeAudioDevices(); @@ -748,30 +756,39 @@ static bool PrepareAudioQueue(SDL_AudioDevice *device) SDL_zero(layout); switch (device->spec.channels) { case 1: + // a standard mono stream layout.mChannelLayoutTag = kAudioChannelLayoutTag_Mono; break; case 2: + // a standard stereo stream (L R) - implied playback layout.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo; break; case 3: + // L R LFE layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_4; break; case 4: + // front left, front right, back left, back right layout.mChannelLayoutTag = kAudioChannelLayoutTag_Quadraphonic; break; case 5: - layout.mChannelLayoutTag = kAudioChannelLayoutTag_MPEG_5_0_A; + // L R LFE Ls Rs + layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_6; break; case 6: - layout.mChannelLayoutTag = kAudioChannelLayoutTag_MPEG_5_1_A; + // L R C LFE Ls Rs + layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_12; break; case 7: - // FIXME: Need to move channel[4] (BC) to channel[6] - layout.mChannelLayoutTag = kAudioChannelLayoutTag_MPEG_6_1_A; + // L R C LFE Cs Ls Rs + layout.mChannelLayoutTag = kAudioChannelLayoutTag_WAVE_6_1; break; case 8: - layout.mChannelLayoutTag = kAudioChannelLayoutTag_MPEG_7_1_A; + // L R C LFE Rls Rrs Ls Rs + layout.mChannelLayoutTag = kAudioChannelLayoutTag_WAVE_7_1; break; + default: + return SDL_SetError("Unsupported audio channels"); } if (layout.mChannelLayoutTag != 0) { result = AudioQueueSetProperty(device->hidden->audioQueue, kAudioQueueProperty_ChannelLayout, &layout, sizeof(layout)); diff --git a/libs/SDL3/src/audio/emscripten/SDL_emscriptenaudio.c b/libs/SDL3/src/audio/emscripten/SDL_emscriptenaudio.c index 4d99bc5c..e25083d1 100644 --- a/libs/SDL3/src/audio/emscripten/SDL_emscriptenaudio.c +++ b/libs/SDL3/src/audio/emscripten/SDL_emscriptenaudio.c @@ -168,7 +168,7 @@ static bool EMSCRIPTENAUDIO_OpenDevice(SDL_AudioDevice *device) SDL3.audioContext = new webkitAudioContext(); } if (SDL3.audioContext) { - if ((typeof navigator.userActivation) === 'undefined') { // Firefox doesn't have this (as of August 2023), use autoResumeAudioContext instead. + if ((typeof navigator.userActivation) === 'undefined') { autoResumeAudioContext(SDL3.audioContext); } } @@ -190,6 +190,7 @@ static bool EMSCRIPTENAUDIO_OpenDevice(SDL_AudioDevice *device) // limit to native freq device->spec.freq = EM_ASM_INT({ return Module['SDL3'].audioContext.sampleRate; }); + device->sample_frames = SDL_GetDefaultSampleFramesFromFreq(device->spec.freq); SDL_UpdatedAudioDeviceFormat(device); @@ -283,7 +284,7 @@ static bool EMSCRIPTENAUDIO_OpenDevice(SDL_AudioDevice *device) SDL3.audio_playback.silenceBuffer = SDL3.audioContext.createBuffer($0, $1, SDL3.audioContext.sampleRate); SDL3.audio_playback.silenceBuffer.getChannelData(0).fill(0.0); var silence_callback = function() { - if ((typeof navigator.userActivation) !== 'undefined') { // Almost everything modern except Firefox (as of August 2023) + if ((typeof navigator.userActivation) !== 'undefined') { if (navigator.userActivation.hasBeenActive) { SDL3.audioContext.resume(); } diff --git a/libs/SDL3/src/audio/pipewire/SDL_pipewire.c b/libs/SDL3/src/audio/pipewire/SDL_pipewire.c index d803c8d7..775c558d 100644 --- a/libs/SDL3/src/audio/pipewire/SDL_pipewire.c +++ b/libs/SDL3/src/audio/pipewire/SDL_pipewire.c @@ -939,7 +939,21 @@ static bool PIPEWIRE_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, in static void output_callback(void *data) { - SDL_PlaybackAudioThreadIterate((SDL_AudioDevice *)data); + SDL_AudioDevice *device = (SDL_AudioDevice *) data; + + // this callback can fire in a background thread during OpenDevice, while we're still blocking + // _with the device lock_ until the stream is ready, causing a deadlock. Write silence in this case. + if (device->hidden->stream_init_status != PW_READY_FLAG_ALL_BITS) { + int bufsize = 0; + Uint8 *buf = PIPEWIRE_GetDeviceBuf(device, &bufsize); + if (buf && bufsize) { + SDL_memset(buf, device->silence_value, bufsize); + } + PIPEWIRE_PlayDevice(device, buf, bufsize); + return; + } + + SDL_PlaybackAudioThreadIterate(device); } static void PIPEWIRE_FlushRecording(SDL_AudioDevice *device) @@ -980,7 +994,16 @@ static int PIPEWIRE_RecordDevice(SDL_AudioDevice *device, void *buffer, int bufl static void input_callback(void *data) { - SDL_RecordingAudioThreadIterate((SDL_AudioDevice *)data); + SDL_AudioDevice *device = (SDL_AudioDevice *) data; + + // this callback can fire in a background thread during OpenDevice, while we're still blocking + // _with the device lock_ until the stream is ready, causing a deadlock. Drop data in this case. + if (device->hidden->stream_init_status != PW_READY_FLAG_ALL_BITS) { + PIPEWIRE_FlushRecording(device); + return; + } + + SDL_RecordingAudioThreadIterate(device); } static void stream_add_buffer_callback(void *data, struct pw_buffer *buffer) diff --git a/libs/SDL3/src/audio/wasapi/SDL_wasapi.c b/libs/SDL3/src/audio/wasapi/SDL_wasapi.c index 6c8eee6c..a6f15774 100644 --- a/libs/SDL3/src/audio/wasapi/SDL_wasapi.c +++ b/libs/SDL3/src/audio/wasapi/SDL_wasapi.c @@ -43,13 +43,22 @@ #define AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM 0x80000000 #endif +// handle to Avrt.dll--Vista and later!--for flagging the callback thread as "Pro Audio" (low latency). +static HMODULE libavrt = NULL; +typedef HANDLE(WINAPI *pfnAvSetMmThreadCharacteristicsW)(LPCWSTR, LPDWORD); +typedef BOOL(WINAPI *pfnAvRevertMmThreadCharacteristics)(HANDLE); +static pfnAvSetMmThreadCharacteristicsW pAvSetMmThreadCharacteristicsW = NULL; +static pfnAvRevertMmThreadCharacteristics pAvRevertMmThreadCharacteristics = NULL; + // Some GUIDs we need to know without linking to libraries that aren't available before Vista. static const IID SDL_IID_IAudioRenderClient = { 0xf294acfc, 0x3146, 0x4483, { 0xa7, 0xbf, 0xad, 0xdc, 0xa7, 0xc2, 0x60, 0xe2 } }; static const IID SDL_IID_IAudioCaptureClient = { 0xc8adbd64, 0xe71e, 0x48a0, { 0xa4, 0xde, 0x18, 0x5c, 0x39, 0x5c, 0xd3, 0x17 } }; +static const IID SDL_IID_IAudioClient = { 0x1cb9ad4c, 0xdbfa, 0x4c32, { 0xb1, 0x78, 0xc2, 0xf5, 0x68, 0xa7, 0x03, 0xb2 } }; #ifdef __IAudioClient3_INTERFACE_DEFINED__ static const IID SDL_IID_IAudioClient3 = { 0x7ed4ee07, 0x8e67, 0x4cd4, { 0x8c, 0x1a, 0x2b, 0x7a, 0x59, 0x87, 0xad, 0x42 } }; #endif // +static bool immdevice_initialized = false; // WASAPI is _really_ particular about various things happening on the same thread, for COM and such, // so we proxy various stuff to a single background thread to manage. @@ -155,15 +164,83 @@ bool WASAPI_ProxyToManagementThread(ManagementThreadTask task, void *userdata, b return true; // successfully added (and possibly executed)! } +static bool mgmtthrtask_AudioDeviceDisconnected(void *userdata) +{ + SDL_AudioDevice *device = (SDL_AudioDevice *) userdata; + SDL_AudioDeviceDisconnected(device); + UnrefPhysicalAudioDevice(device); // make sure this lived until the task completes. + return true; +} + +static void AudioDeviceDisconnected(SDL_AudioDevice *device) +{ + // don't wait on this, IMMDevice's own thread needs to return or everything will deadlock. + if (device) { + RefPhysicalAudioDevice(device); // make sure this lives until the task completes. + WASAPI_ProxyToManagementThread(mgmtthrtask_AudioDeviceDisconnected, device, NULL); + } +} + +static bool mgmtthrtask_DefaultAudioDeviceChanged(void *userdata) +{ + SDL_AudioDevice *device = (SDL_AudioDevice *) userdata; + SDL_DefaultAudioDeviceChanged(device); + UnrefPhysicalAudioDevice(device); // make sure this lived until the task completes. + return true; +} + +static void DefaultAudioDeviceChanged(SDL_AudioDevice *new_default_device) +{ + // don't wait on this, IMMDevice's own thread needs to return or everything will deadlock. + if (new_default_device) { + RefPhysicalAudioDevice(new_default_device); // make sure this lives until the task completes. + WASAPI_ProxyToManagementThread(mgmtthrtask_DefaultAudioDeviceChanged, new_default_device, NULL); + } +} + +static void StopWasapiHotplug(void) +{ + if (immdevice_initialized) { + SDL_IMMDevice_Quit(); + immdevice_initialized = false; + } +} + +static void Deinit(void) +{ + if (libavrt) { + FreeLibrary(libavrt); + libavrt = NULL; + } + + pAvSetMmThreadCharacteristicsW = NULL; + pAvRevertMmThreadCharacteristics = NULL; + + StopWasapiHotplug(); + + WIN_CoUninitialize(); +} + static bool ManagementThreadPrepare(void) { - if (!WASAPI_PlatformInit()) { - return false; + const SDL_IMMDevice_callbacks callbacks = { AudioDeviceDisconnected, DefaultAudioDeviceChanged }; + if (FAILED(WIN_CoInitialize())) { + return SDL_SetError("CoInitialize() failed"); + } else if (!SDL_IMMDevice_Init(&callbacks)) { + return false; // Error string is set by SDL_IMMDevice_Init + } + + immdevice_initialized = true; + + libavrt = LoadLibrary(TEXT("avrt.dll")); // this library is available in Vista and later. No WinXP, so have to LoadLibrary to use it for now! + if (libavrt) { + pAvSetMmThreadCharacteristicsW = (pfnAvSetMmThreadCharacteristicsW)GetProcAddress(libavrt, "AvSetMmThreadCharacteristicsW"); + pAvRevertMmThreadCharacteristics = (pfnAvRevertMmThreadCharacteristics)GetProcAddress(libavrt, "AvRevertMmThreadCharacteristics"); } ManagementThreadLock = SDL_CreateMutex(); if (!ManagementThreadLock) { - WASAPI_PlatformDeinit(); + Deinit(); return false; } @@ -171,7 +248,7 @@ static bool ManagementThreadPrepare(void) if (!ManagementThreadCondition) { SDL_DestroyMutex(ManagementThreadLock); ManagementThreadLock = NULL; - WASAPI_PlatformDeinit(); + Deinit(); return false; } @@ -197,7 +274,7 @@ static int ManagementThreadEntry(void *userdata) SDL_SignalSemaphore(data->ready_sem); // unblock calling thread. ManagementThreadMainloop(); - WASAPI_PlatformDeinit(); + Deinit(); return 0; } @@ -260,7 +337,7 @@ typedef struct static bool mgmtthrtask_DetectDevices(void *userdata) { mgmtthrtask_DetectDevicesData *data = (mgmtthrtask_DetectDevicesData *)userdata; - WASAPI_EnumerateEndpoints(data->default_playback, data->default_recording); + SDL_IMMDevice_EnumerateEndpoints(data->default_playback, data->default_recording); return true; } @@ -373,7 +450,29 @@ static void ResetWasapiDevice(SDL_AudioDevice *device) static bool mgmtthrtask_ActivateDevice(void *userdata) { - return WASAPI_ActivateDevice((SDL_AudioDevice *)userdata); + SDL_AudioDevice *device = (SDL_AudioDevice *) userdata; + + IMMDevice *immdevice = NULL; + if (!SDL_IMMDevice_Get(device, &immdevice, device->recording)) { + device->hidden->client = NULL; + return false; // This is already set by SDL_IMMDevice_Get + } + + // this is _not_ async in standard win32, yay! + HRESULT ret = IMMDevice_Activate(immdevice, &SDL_IID_IAudioClient, CLSCTX_ALL, NULL, (void **)&device->hidden->client); + IMMDevice_Release(immdevice); + + if (FAILED(ret)) { + SDL_assert(device->hidden->client == NULL); + return WIN_SetErrorFromHRESULT("WASAPI can't activate audio endpoint", ret); + } + + SDL_assert(device->hidden->client != NULL); + if (!WASAPI_PrepDevice(device)) { // not async, fire it right away. + return false; + } + + return true; // good to go. } static bool ActivateWasapiDevice(SDL_AudioDevice *device) @@ -580,12 +679,7 @@ static bool mgmtthrtask_PrepDevice(void *userdata) IAudioClient *client = device->hidden->client; SDL_assert(client != NULL); -#if defined(SDL_PLATFORM_GDK) // CreateEventEx() arrived in Vista, so we need an #ifdef for XP. - device->hidden->event = CreateEventEx(NULL, NULL, 0, EVENT_ALL_ACCESS); -#else - device->hidden->event = CreateEventW(NULL, 0, 0, NULL); -#endif - + device->hidden->event = CreateEvent(NULL, FALSE, FALSE, NULL); if (!device->hidden->event) { return WIN_SetError("WASAPI can't create an event handle"); } @@ -773,17 +867,37 @@ static bool WASAPI_OpenDevice(SDL_AudioDevice *device) static void WASAPI_ThreadInit(SDL_AudioDevice *device) { - WASAPI_PlatformThreadInit(device); + // this thread uses COM. + if (SUCCEEDED(WIN_CoInitialize())) { // can't report errors, hope it worked! + device->hidden->coinitialized = true; + } + + // Set this thread to very high "Pro Audio" priority. + if (pAvSetMmThreadCharacteristicsW) { + DWORD idx = 0; + device->hidden->task = pAvSetMmThreadCharacteristicsW(L"Pro Audio", &idx); + } else { + SDL_SetCurrentThreadPriority(device->recording ? SDL_THREAD_PRIORITY_HIGH : SDL_THREAD_PRIORITY_TIME_CRITICAL); + } } static void WASAPI_ThreadDeinit(SDL_AudioDevice *device) { - WASAPI_PlatformThreadDeinit(device); + // Set this thread back to normal priority. + if (device->hidden->task && pAvRevertMmThreadCharacteristics) { + pAvRevertMmThreadCharacteristics(device->hidden->task); + device->hidden->task = NULL; + } + + if (device->hidden->coinitialized) { + WIN_CoUninitialize(); + device->hidden->coinitialized = false; + } } static bool mgmtthrtask_FreeDeviceHandle(void *userdata) { - WASAPI_PlatformFreeDeviceHandle((SDL_AudioDevice *)userdata); + SDL_IMMDevice_FreeDeviceHandle((SDL_AudioDevice *) userdata); return true; } @@ -795,7 +909,7 @@ static void WASAPI_FreeDeviceHandle(SDL_AudioDevice *device) static bool mgmtthrtask_DeinitializeStart(void *userdata) { - WASAPI_PlatformDeinitializeStart(); + StopWasapiHotplug(); return true; } diff --git a/libs/SDL3/src/audio/wasapi/SDL_wasapi.h b/libs/SDL3/src/audio/wasapi/SDL_wasapi.h index 3d4744cf..6c5f45f4 100644 --- a/libs/SDL3/src/audio/wasapi/SDL_wasapi.h +++ b/libs/SDL3/src/audio/wasapi/SDL_wasapi.h @@ -53,17 +53,6 @@ void WASAPI_DisconnectDevice(SDL_AudioDevice *device); // don't hold the device typedef bool (*ManagementThreadTask)(void *userdata); bool WASAPI_ProxyToManagementThread(ManagementThreadTask task, void *userdata, bool *wait_until_complete); -// These are functions that are (were...?) implemented differently for various Windows versions. -// UNLESS OTHERWISE NOTED THESE ALL HAPPEN ON THE MANAGEMENT THREAD. -bool WASAPI_PlatformInit(void); -void WASAPI_PlatformDeinit(void); -void WASAPI_PlatformDeinitializeStart(void); -void WASAPI_EnumerateEndpoints(SDL_AudioDevice **default_playback, SDL_AudioDevice **default_recording); -bool WASAPI_ActivateDevice(SDL_AudioDevice *device); -void WASAPI_PlatformThreadInit(SDL_AudioDevice *device); // this happens on the audio device thread, not the management thread. -void WASAPI_PlatformThreadDeinit(SDL_AudioDevice *device); // this happens on the audio device thread, not the management thread. -void WASAPI_PlatformFreeDeviceHandle(SDL_AudioDevice *device); - #ifdef __cplusplus } #endif diff --git a/libs/SDL3/src/audio/wasapi/SDL_wasapi_win32.c b/libs/SDL3/src/audio/wasapi/SDL_wasapi_win32.c deleted file mode 100644 index 5ef776d4..00000000 --- a/libs/SDL3/src/audio/wasapi/SDL_wasapi_win32.c +++ /dev/null @@ -1,200 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2024 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "SDL_internal.h" - -/* !!! FIXME: merge this all into SDL_wasapi.c, now that WinRT is gone. - This is code that Windows uses to talk to WASAPI-related system APIs. - This is for non-WinRT desktop apps. The C++/CX implementation of these - functions, exclusive to WinRT, are in SDL_wasapi_winrt.cpp. - The code in SDL_wasapi.c is used by both standard Windows and WinRT builds - to deal with audio and calls into these functions. */ - -#if defined(SDL_AUDIO_DRIVER_WASAPI) - -#include "../../core/windows/SDL_windows.h" -#include "../../core/windows/SDL_immdevice.h" -#include "../SDL_sysaudio.h" - -#include - -#include "SDL_wasapi.h" - -// handle to Avrt.dll--Vista and later!--for flagging the callback thread as "Pro Audio" (low latency). -static HMODULE libavrt = NULL; -typedef HANDLE(WINAPI *pfnAvSetMmThreadCharacteristicsW)(LPCWSTR, LPDWORD); -typedef BOOL(WINAPI *pfnAvRevertMmThreadCharacteristics)(HANDLE); -static pfnAvSetMmThreadCharacteristicsW pAvSetMmThreadCharacteristicsW = NULL; -static pfnAvRevertMmThreadCharacteristics pAvRevertMmThreadCharacteristics = NULL; - -static bool immdevice_initialized = false; - -// Some GUIDs we need to know without linking to libraries that aren't available before Vista. -static const IID SDL_IID_IAudioClient = { 0x1cb9ad4c, 0xdbfa, 0x4c32, { 0xb1, 0x78, 0xc2, 0xf5, 0x68, 0xa7, 0x03, 0xb2 } }; - -static bool mgmtthrtask_AudioDeviceDisconnected(void *userdata) -{ - SDL_AudioDevice *device = (SDL_AudioDevice *) userdata; - SDL_AudioDeviceDisconnected(device); - UnrefPhysicalAudioDevice(device); // make sure this lived until the task completes. - return true; -} - -static void WASAPI_AudioDeviceDisconnected(SDL_AudioDevice *device) -{ - // don't wait on this, IMMDevice's own thread needs to return or everything will deadlock. - if (device) { - RefPhysicalAudioDevice(device); // make sure this lives until the task completes. - WASAPI_ProxyToManagementThread(mgmtthrtask_AudioDeviceDisconnected, device, NULL); - } -} - -static bool mgmtthrtask_DefaultAudioDeviceChanged(void *userdata) -{ - SDL_AudioDevice *device = (SDL_AudioDevice *) userdata; - SDL_DefaultAudioDeviceChanged(device); - UnrefPhysicalAudioDevice(device); // make sure this lived until the task completes. - return true; -} - -static void WASAPI_DefaultAudioDeviceChanged(SDL_AudioDevice *new_default_device) -{ - // don't wait on this, IMMDevice's own thread needs to return or everything will deadlock. - if (new_default_device) { - RefPhysicalAudioDevice(new_default_device); // make sure this lives until the task completes. - WASAPI_ProxyToManagementThread(mgmtthrtask_DefaultAudioDeviceChanged, new_default_device, NULL); - } -} - -bool WASAPI_PlatformInit(void) -{ - const SDL_IMMDevice_callbacks callbacks = { WASAPI_AudioDeviceDisconnected, WASAPI_DefaultAudioDeviceChanged }; - if (FAILED(WIN_CoInitialize())) { - return SDL_SetError("CoInitialize() failed"); - } else if (!SDL_IMMDevice_Init(&callbacks)) { - return false; // Error string is set by SDL_IMMDevice_Init - } - - immdevice_initialized = true; - - libavrt = LoadLibrary(TEXT("avrt.dll")); // this library is available in Vista and later. No WinXP, so have to LoadLibrary to use it for now! - if (libavrt) { - pAvSetMmThreadCharacteristicsW = (pfnAvSetMmThreadCharacteristicsW)GetProcAddress(libavrt, "AvSetMmThreadCharacteristicsW"); - pAvRevertMmThreadCharacteristics = (pfnAvRevertMmThreadCharacteristics)GetProcAddress(libavrt, "AvRevertMmThreadCharacteristics"); - } - - return true; -} - -static void StopWasapiHotplug(void) -{ - if (immdevice_initialized) { - SDL_IMMDevice_Quit(); - immdevice_initialized = false; - } -} - -void WASAPI_PlatformDeinit(void) -{ - if (libavrt) { - FreeLibrary(libavrt); - libavrt = NULL; - } - - pAvSetMmThreadCharacteristicsW = NULL; - pAvRevertMmThreadCharacteristics = NULL; - - StopWasapiHotplug(); - - WIN_CoUninitialize(); -} - -void WASAPI_PlatformDeinitializeStart(void) -{ - StopWasapiHotplug(); -} - -void WASAPI_PlatformThreadInit(SDL_AudioDevice *device) -{ - // this thread uses COM. - if (SUCCEEDED(WIN_CoInitialize())) { // can't report errors, hope it worked! - device->hidden->coinitialized = true; - } - - // Set this thread to very high "Pro Audio" priority. - if (pAvSetMmThreadCharacteristicsW) { - DWORD idx = 0; - device->hidden->task = pAvSetMmThreadCharacteristicsW(L"Pro Audio", &idx); - } else { - SDL_SetCurrentThreadPriority(device->recording ? SDL_THREAD_PRIORITY_HIGH : SDL_THREAD_PRIORITY_TIME_CRITICAL); - } - -} - -void WASAPI_PlatformThreadDeinit(SDL_AudioDevice *device) -{ - // Set this thread back to normal priority. - if (device->hidden->task && pAvRevertMmThreadCharacteristics) { - pAvRevertMmThreadCharacteristics(device->hidden->task); - device->hidden->task = NULL; - } - - if (device->hidden->coinitialized) { - WIN_CoUninitialize(); - device->hidden->coinitialized = false; - } -} - -bool WASAPI_ActivateDevice(SDL_AudioDevice *device) -{ - IMMDevice *immdevice = NULL; - if (!SDL_IMMDevice_Get(device, &immdevice, device->recording)) { - device->hidden->client = NULL; - return false; // This is already set by SDL_IMMDevice_Get - } - - // this is _not_ async in standard win32, yay! - HRESULT ret = IMMDevice_Activate(immdevice, &SDL_IID_IAudioClient, CLSCTX_ALL, NULL, (void **)&device->hidden->client); - IMMDevice_Release(immdevice); - - if (FAILED(ret)) { - SDL_assert(device->hidden->client == NULL); - return WIN_SetErrorFromHRESULT("WASAPI can't activate audio endpoint", ret); - } - - SDL_assert(device->hidden->client != NULL); - if (!WASAPI_PrepDevice(device)) { // not async, fire it right away. - return false; - } - - return true; // good to go. -} - -void WASAPI_EnumerateEndpoints(SDL_AudioDevice **default_playback, SDL_AudioDevice **default_recording) -{ - SDL_IMMDevice_EnumerateEndpoints(default_playback, default_recording); -} - -void WASAPI_PlatformFreeDeviceHandle(SDL_AudioDevice *device) -{ - SDL_IMMDevice_FreeDeviceHandle(device); -} - -#endif // SDL_AUDIO_DRIVER_WASAPI diff --git a/libs/SDL3/src/camera/SDL_camera.c b/libs/SDL3/src/camera/SDL_camera.c index c9c8f3b6..c11f08af 100644 --- a/libs/SDL3/src/camera/SDL_camera.c +++ b/libs/SDL3/src/camera/SDL_camera.c @@ -1422,7 +1422,7 @@ bool SDL_CameraInit(const char *driver_name) return false; } - SDL_HashTable *device_hash = SDL_CreateHashTable(NULL, 8, HashCameraID, MatchCameraID, NukeCameraHashItem, false); + SDL_HashTable *device_hash = SDL_CreateHashTable(NULL, 8, HashCameraID, MatchCameraID, NukeCameraHashItem, false, false); if (!device_hash) { SDL_DestroyRWLock(device_hash_lock); return false; diff --git a/libs/SDL3/src/camera/pipewire/SDL_camera_pipewire.c b/libs/SDL3/src/camera/pipewire/SDL_camera_pipewire.c index 8460d995..f5cdad95 100644 --- a/libs/SDL3/src/camera/pipewire/SDL_camera_pipewire.c +++ b/libs/SDL3/src/camera/pipewire/SDL_camera_pipewire.c @@ -98,9 +98,9 @@ static struct pw_properties *(*PIPEWIRE_pw_properties_new_dict)(const struct spa static int (*PIPEWIRE_pw_properties_set)(struct pw_properties *, const char *, const char *); static int (*PIPEWIRE_pw_properties_setf)(struct pw_properties *, const char *, const char *, ...) SPA_PRINTF_FUNC(3, 4); -#ifdef SDL_AUDIO_DRIVER_PIPEWIRE_DYNAMIC +#ifdef SDL_CAMERA_DRIVER_PIPEWIRE_DYNAMIC -static const char *pipewire_library = SDL_AUDIO_DRIVER_PIPEWIRE_DYNAMIC; +static const char *pipewire_library = SDL_CAMERA_DRIVER_PIPEWIRE_DYNAMIC; static SDL_SharedObject *pipewire_handle = NULL; static bool pipewire_dlsym(const char *fn, void **addr) @@ -146,7 +146,7 @@ static void unload_pipewire_library(void) // Nothing to do } -#endif // SDL_AUDIO_DRIVER_PIPEWIRE_DYNAMIC +#endif // SDL_CAMERA_DRIVER_PIPEWIRE_DYNAMIC static bool load_pipewire_syms(void) { diff --git a/libs/SDL3/src/core/android/SDL_android.c b/libs/SDL3/src/core/android/SDL_android.c index 904237a2..e24a7547 100644 --- a/libs/SDL3/src/core/android/SDL_android.c +++ b/libs/SDL3/src/core/android/SDL_android.c @@ -2601,6 +2601,11 @@ bool Android_JNI_GetLocale(char *buf, size_t buflen) AConfiguration_getLanguage(cfg, language); AConfiguration_getCountry(cfg, country); + // Indonesian is "id" according to ISO 639.2, but on Android is "in" because of Java backwards compatibility + if (language[0] == 'i' && language[1] == 'n') { + language[1] = 'd'; + } + // copy language (not null terminated) if (language[0]) { buf[id++] = language[0]; diff --git a/libs/SDL3/src/core/linux/SDL_evdev.c b/libs/SDL3/src/core/linux/SDL_evdev.c index e655eea6..456a04f1 100644 --- a/libs/SDL3/src/core/linux/SDL_evdev.c +++ b/libs/SDL3/src/core/linux/SDL_evdev.c @@ -346,8 +346,9 @@ void SDL_EVDEV_Poll(void) switch (event->type) { case EV_KEY: if (event->code >= BTN_MOUSE && event->code < BTN_MOUSE + SDL_arraysize(EVDEV_MouseButtons)) { + Uint64 timestamp = SDL_EVDEV_GetEventTimestamp(event); mouse_button = event->code - BTN_MOUSE; - SDL_SendMouseButton(SDL_EVDEV_GetEventTimestamp(event), mouse->focus, (SDL_MouseID)item->fd, EVDEV_MouseButtons[mouse_button], (event->value != 0)); + SDL_SendMouseButton(timestamp, mouse->focus, (SDL_MouseID)item->fd, EVDEV_MouseButtons[mouse_button], (event->value != 0)); break; } @@ -367,13 +368,16 @@ void SDL_EVDEV_Poll(void) } // Probably keyboard - scancode = SDL_EVDEV_translate_keycode(event->code); - if (event->value == 0) { - SDL_SendKeyboardKey(SDL_EVDEV_GetEventTimestamp(event), (SDL_KeyboardID)item->fd, event->code, scancode, false); - } else if (event->value == 1 || event->value == 2 /* key repeated */) { - SDL_SendKeyboardKey(SDL_EVDEV_GetEventTimestamp(event), (SDL_KeyboardID)item->fd, event->code, scancode, true); + { + Uint64 timestamp = SDL_EVDEV_GetEventTimestamp(event); + scancode = SDL_EVDEV_translate_keycode(event->code); + if (event->value == 0) { + SDL_SendKeyboardKey(timestamp, (SDL_KeyboardID)item->fd, event->code, scancode, false); + } else if (event->value == 1 || event->value == 2 /* key repeated */) { + SDL_SendKeyboardKey(timestamp, (SDL_KeyboardID)item->fd, event->code, scancode, true); + } + SDL_EVDEV_kbd_keycode(_this->kbd, event->code, event->value); } - SDL_EVDEV_kbd_keycode(_this->kbd, event->code, event->value); break; case EV_ABS: switch (event->code) { @@ -485,7 +489,8 @@ void SDL_EVDEV_Poll(void) // Send mouse axis changes together to ensure consistency and reduce event processing overhead if (item->relative_mouse) { if (item->mouse_x != 0 || item->mouse_y != 0) { - SDL_SendMouseMotion(SDL_EVDEV_GetEventTimestamp(event), mouse->focus, (SDL_MouseID)item->fd, item->relative_mouse, (float)item->mouse_x, (float)item->mouse_y); + Uint64 timestamp = SDL_EVDEV_GetEventTimestamp(event); + SDL_SendMouseMotion(timestamp, mouse->focus, (SDL_MouseID)item->fd, item->relative_mouse, (float)item->mouse_x, (float)item->mouse_y); item->mouse_x = item->mouse_y = 0; } } else if (item->range_x > 0 && item->range_y > 0) { @@ -508,10 +513,12 @@ void SDL_EVDEV_Poll(void) } if (item->mouse_wheel != 0 || item->mouse_hwheel != 0) { - SDL_SendMouseWheel(SDL_EVDEV_GetEventTimestamp(event), + Uint64 timestamp = SDL_EVDEV_GetEventTimestamp(event); + const float denom = (item->high_res_hwheel ? 120.0f : 1.0f); + SDL_SendMouseWheel(timestamp, mouse->focus, (SDL_MouseID)item->fd, - item->mouse_hwheel / (item->high_res_hwheel ? 120.0f : 1.0f), - item->mouse_wheel / (item->high_res_wheel ? 120.0f : 1.0f), + item->mouse_hwheel / denom, + item->mouse_wheel / denom, SDL_MOUSEWHEEL_NORMAL); item->mouse_wheel = item->mouse_hwheel = 0; } diff --git a/libs/SDL3/src/core/openbsd/SDL_wscons_kbd.c b/libs/SDL3/src/core/openbsd/SDL_wscons_kbd.c index 79566ef5..09e41ed9 100644 --- a/libs/SDL3/src/core/openbsd/SDL_wscons_kbd.c +++ b/libs/SDL3/src/core/openbsd/SDL_wscons_kbd.c @@ -550,7 +550,7 @@ static void Translate_to_text(SDL_WSCONS_input_data *input, keysym_t ksym) } } -static void Translate_to_keycode(SDL_WSCONS_input_data *input, int type, keysym_t ksym) +static void Translate_to_keycode(SDL_WSCONS_input_data *input, int type, keysym_t ksym, Uint64 timestamp) { struct wscons_keymap keyDesc = input->keymap.map[ksym]; keysym_t *group = &keyDesc.group1[KS_GROUP(keyDesc.group1[0]) == KS_GROUP_Keypad && IS_NUMLOCK_ON ? !IS_SHIFT_HELD : 0]; @@ -560,22 +560,30 @@ static void Translate_to_keycode(SDL_WSCONS_input_data *input, int type, keysym_ switch (keyDesc.command) { case KS_Cmd_ScrollBack: { - SDL_SendKeyboardKey(0, input->keyboardID, 0, SDL_SCANCODE_PAGEUP, (type == WSCONS_EVENT_KEY_DOWN)); + SDL_SendKeyboardKey(timestamp, input->keyboardID, 0, SDL_SCANCODE_PAGEUP, (type == WSCONS_EVENT_KEY_DOWN)); return; } case KS_Cmd_ScrollFwd: { - SDL_SendKeyboardKey(0, input->keyboardID, 0, SDL_SCANCODE_PAGEDOWN, (type == WSCONS_EVENT_KEY_DOWN)); + SDL_SendKeyboardKey(timestamp, input->keyboardID, 0, SDL_SCANCODE_PAGEDOWN, (type == WSCONS_EVENT_KEY_DOWN)); return; } + default: + break; } - for (i = 0; i < sizeof(conversion_table) / sizeof(struct wscons_keycode_to_SDL); i++) { + for (i = 0; i < SDL_arraysize(conversion_table); i++) { if (conversion_table[i].sourcekey == group[0]) { - SDL_SendKeyboardKey(0, input->keyboardID, group[0], conversion_table[i].targetKey, (type == WSCONS_EVENT_KEY_DOWN)); + SDL_SendKeyboardKey(timestamp, input->keyboardID, group[0], conversion_table[i].targetKey, (type == WSCONS_EVENT_KEY_DOWN)); return; } } - SDL_SendKeyboardKey(0, input->keyboardID, group[0], SDL_SCANCODE_UNKNOWN, (type == WSCONS_EVENT_KEY_DOWN)); + SDL_SendKeyboardKey(timestamp, input->keyboardID, group[0], SDL_SCANCODE_UNKNOWN, (type == WSCONS_EVENT_KEY_DOWN)); +} + +static Uint64 GetEventTimestamp(struct timespec *time) +{ + // FIXME: Get the event time in the SDL tick time base + return SDL_GetTicksNS(); } static void updateKeyboard(SDL_WSCONS_input_data *input) @@ -592,6 +600,7 @@ static void updateKeyboard(SDL_WSCONS_input_data *input) if ((n = read(input->fd, events, sizeof(events))) > 0) { n /= sizeof(struct wscons_event); for (i = 0; i < n; i++) { + Uint64 timestamp = GetEventTimestamp(&events[i].time); type = events[i].type; switch (type) { case WSCONS_EVENT_KEY_DOWN: @@ -809,15 +818,18 @@ static void updateKeyboard(SDL_WSCONS_input_data *input) } break; case WSCONS_EVENT_ALL_KEYS_UP: for (i = 0; i < SDL_SCANCODE_COUNT; i++) { - SDL_SendKeyboardKey(0, input->keyboardID, 0, (SDL_Scancode)i, false); + SDL_SendKeyboardKey(timestamp, input->keyboardID, 0, (SDL_Scancode)i, false); } break; + default: + break; } - if (input->type == WSKBD_TYPE_USB && events[i].value <= 0xE7) - SDL_SendKeyboardKey(0, input->keyboardID, 0, (SDL_Scancode)events[i].value, (type == WSCONS_EVENT_KEY_DOWN)); - else - Translate_to_keycode(input, type, events[i].value); + if (input->type == WSKBD_TYPE_USB && events[i].value <= 0xE7) { + SDL_SendKeyboardKey(timestamp, input->keyboardID, 0, (SDL_Scancode)events[i].value, (type == WSCONS_EVENT_KEY_DOWN)); + } else { + Translate_to_keycode(input, type, events[i].value, timestamp); + } if (type == WSCONS_EVENT_KEY_UP) { continue; @@ -910,7 +922,7 @@ static void updateKeyboard(SDL_WSCONS_input_data *input) } if (IS_ALT_HELD) { if (input->encoding & KB_METAESC) { - Translate_to_keycode(input, WSCONS_EVENT_KEY_DOWN, KS_Escape); + Translate_to_keycode(input, WSCONS_EVENT_KEY_DOWN, KS_Escape, 0); Translate_to_text(input, result); continue; } else { diff --git a/libs/SDL3/src/core/openbsd/SDL_wscons_mouse.c b/libs/SDL3/src/core/openbsd/SDL_wscons_mouse.c index 5ba7a500..81f336f6 100644 --- a/libs/SDL3/src/core/openbsd/SDL_wscons_mouse.c +++ b/libs/SDL3/src/core/openbsd/SDL_wscons_mouse.c @@ -63,6 +63,12 @@ SDL_WSCONS_mouse_input_data *SDL_WSCONS_Init_Mouse(void) return input; } +static Uint64 GetEventTimestamp(struct timespec *time) +{ + // FIXME: Get the event time in the SDL tick time base + return SDL_GetTicksNS(); +} + void updateMouse(SDL_WSCONS_mouse_input_data *input) { struct wscons_event events[64]; @@ -73,56 +79,39 @@ void updateMouse(SDL_WSCONS_mouse_input_data *input) int i; n /= sizeof(struct wscons_event); for (i = 0; i < n; i++) { + Uint64 timestamp = GetEventTimestamp(&events[i].time); int type = events[i].type; switch (type) { case WSCONS_EVENT_MOUSE_DOWN: - { - switch (events[i].value) { - case 0: // Left Mouse Button. - SDL_SendMouseButton(0, mouse->focus, input->mouseID, SDL_BUTTON_LEFT, true); - break; - case 1: // Middle Mouse Button. - SDL_SendMouseButton(0, mouse->focus, input->mouseID, SDL_BUTTON_MIDDLE, true); - break; - case 2: // Right Mouse Button. - SDL_SendMouseButton(0, mouse->focus, input->mouseID, SDL_BUTTON_RIGHT, true); - break; - } - } break; case WSCONS_EVENT_MOUSE_UP: { - switch (events[i].value) { - case 0: // Left Mouse Button. - SDL_SendMouseButton(0, mouse->focus, input->mouseID, SDL_BUTTON_LEFT, false); - break; - case 1: // Middle Mouse Button. - SDL_SendMouseButton(0, mouse->focus, input->mouseID, SDL_BUTTON_MIDDLE, false); - break; - case 2: // Right Mouse Button. - SDL_SendMouseButton(0, mouse->focus, input->mouseID, SDL_BUTTON_RIGHT, false); - break; - } - } break; + Uint8 button = SDL_BUTTON_LEFT + events[i].value; + bool down = (type == WSCONS_EVENT_MOUSE_DOWN); + SDL_SendMouseButton(timestamp, mouse->focus, input->mouseID, button, down); + break; + } case WSCONS_EVENT_MOUSE_DELTA_X: { - SDL_SendMouseMotion(0, mouse->focus, input->mouseID, 1, (float)events[i].value, 0.0f); + SDL_SendMouseMotion(timestamp, mouse->focus, input->mouseID, true, (float)events[i].value, 0.0f); break; } case WSCONS_EVENT_MOUSE_DELTA_Y: { - SDL_SendMouseMotion(0, mouse->focus, input->mouseID, 1, 0.0f, -(float)events[i].value); + SDL_SendMouseMotion(timestamp, mouse->focus, input->mouseID, true, 0.0f, -(float)events[i].value); break; } case WSCONS_EVENT_MOUSE_DELTA_W: { - SDL_SendMouseWheel(0, mouse->focus, input->mouseID, events[i].value, 0, SDL_MOUSEWHEEL_NORMAL); + SDL_SendMouseWheel(timestamp, mouse->focus, input->mouseID, events[i].value, 0, SDL_MOUSEWHEEL_NORMAL); break; } case WSCONS_EVENT_MOUSE_DELTA_Z: { - SDL_SendMouseWheel(0, mouse->focus, input->mouseID, 0, -events[i].value, SDL_MOUSEWHEEL_NORMAL); + SDL_SendMouseWheel(timestamp, mouse->focus, input->mouseID, 0, -events[i].value, SDL_MOUSEWHEEL_NORMAL); break; } + default: + break; } } } diff --git a/libs/SDL3/src/core/windows/SDL_immdevice.h b/libs/SDL3/src/core/windows/SDL_immdevice.h index 9ebd142e..5181e34d 100644 --- a/libs/SDL3/src/core/windows/SDL_immdevice.h +++ b/libs/SDL3/src/core/windows/SDL_immdevice.h @@ -26,20 +26,20 @@ #include #include -typedef struct SDL_AudioDevice SDL_AudioDevice; // this is defined in src/audio/SDL_sysaudio.h +struct SDL_AudioDevice; // defined in src/audio/SDL_sysaudio.h typedef struct SDL_IMMDevice_callbacks { - void (*audio_device_disconnected)(SDL_AudioDevice *device); - void (*default_audio_device_changed)(SDL_AudioDevice *new_default_device); + void (*audio_device_disconnected)(struct SDL_AudioDevice *device); + void (*default_audio_device_changed)(struct SDL_AudioDevice *new_default_device); } SDL_IMMDevice_callbacks; bool SDL_IMMDevice_Init(const SDL_IMMDevice_callbacks *callbacks); void SDL_IMMDevice_Quit(void); -bool SDL_IMMDevice_Get(SDL_AudioDevice *device, IMMDevice **immdevice, bool recording); -void SDL_IMMDevice_EnumerateEndpoints(SDL_AudioDevice **default_playback, SDL_AudioDevice **default_recording); -LPGUID SDL_IMMDevice_GetDirectSoundGUID(SDL_AudioDevice *device); -LPCWSTR SDL_IMMDevice_GetDevID(SDL_AudioDevice *device); -void SDL_IMMDevice_FreeDeviceHandle(SDL_AudioDevice *device); +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); +LPGUID SDL_IMMDevice_GetDirectSoundGUID(struct SDL_AudioDevice *device); +LPCWSTR SDL_IMMDevice_GetDevID(struct SDL_AudioDevice *device); +void SDL_IMMDevice_FreeDeviceHandle(struct SDL_AudioDevice *device); #endif // SDL_IMMDEVICE_H diff --git a/libs/SDL3/src/core/windows/version.rc b/libs/SDL3/src/core/windows/version.rc index 025a07eb..93c7dca0 100644 --- a/libs/SDL3/src/core/windows/version.rc +++ b/libs/SDL3/src/core/windows/version.rc @@ -9,8 +9,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // VS_VERSION_INFO VERSIONINFO - FILEVERSION 3,1,6,0 - PRODUCTVERSION 3,1,6,0 + FILEVERSION 3,1,7,0 + PRODUCTVERSION 3,1,7,0 FILEFLAGSMASK 0x3fL FILEFLAGS 0x0L FILEOS 0x40004L @@ -23,12 +23,12 @@ BEGIN BEGIN VALUE "CompanyName", "\0" VALUE "FileDescription", "SDL\0" - VALUE "FileVersion", "3, 1, 6, 0\0" + VALUE "FileVersion", "3, 1, 7, 0\0" VALUE "InternalName", "SDL\0" VALUE "LegalCopyright", "Copyright (C) 2024 Sam Lantinga\0" VALUE "OriginalFilename", "SDL3.dll\0" VALUE "ProductName", "Simple DirectMedia Layer\0" - VALUE "ProductVersion", "3, 1, 6, 0\0" + VALUE "ProductVersion", "3, 1, 7, 0\0" END END BLOCK "VarFileInfo" diff --git a/libs/SDL3/src/dialog/SDL_dialog.c b/libs/SDL3/src/dialog/SDL_dialog.c new file mode 100644 index 00000000..4831362b --- /dev/null +++ b/libs/SDL3/src/dialog/SDL_dialog.c @@ -0,0 +1,131 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2024 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_internal.h" + +#include "SDL_dialog.h" +#include "SDL_dialog_utils.h" + +void SDL_ShowFileDialogWithProperties(SDL_FileDialogType type, SDL_DialogFileCallback callback, void *userdata, SDL_PropertiesID props) +{ + if (!callback) { + return; + } +#ifdef SDL_DIALOG_DISABLED + SDL_SetError("SDL not built with dialog support"); + callback(userdata, NULL, -1); +#else + SDL_DialogFileFilter *filters = SDL_GetPointerProperty(props, SDL_PROP_FILE_DIALOG_FILTERS_POINTER, NULL); + int nfilters = (int) SDL_GetNumberProperty(props, SDL_PROP_FILE_DIALOG_NFILTERS_NUMBER, -1); + + if (filters && nfilters == -1) { + SDL_SetError("Set filter pointers, but didn't set number of filters (SDL_PROP_FILE_DIALOG_NFILTERS_NUMBER)"); + callback(userdata, NULL, -1); + return; + } + + const char *msg = validate_filters(filters, nfilters); + + if (msg) { + SDL_SetError("Invalid dialog file filters: %s", msg); + callback(userdata, NULL, -1); + return; + } + + switch (type) { + case SDL_FILEDIALOG_OPENFILE: + case SDL_FILEDIALOG_SAVEFILE: + case SDL_FILEDIALOG_OPENFOLDER: + SDL_SYS_ShowFileDialogWithProperties(type, callback, userdata, props); + break; + + default: + SDL_SetError("Unsupported file dialog type: %d", (int) type); + callback(userdata, NULL, -1); + break; + }; +#endif +} + +void SDL_ShowOpenFileDialog(SDL_DialogFileCallback callback, void *userdata, SDL_Window *window, const SDL_DialogFileFilter *filters, int nfilters, const char *default_location, bool allow_many) +{ +#ifdef SDL_DIALOG_DISABLED + if (!callback) { + return; + } + SDL_SetError("SDL not built with dialog support"); + callback(userdata, NULL, -1); +#else + SDL_PropertiesID props = SDL_CreateProperties(); + + SDL_SetPointerProperty(props, SDL_PROP_FILE_DIALOG_FILTERS_POINTER, (void *) filters); + SDL_SetNumberProperty(props, SDL_PROP_FILE_DIALOG_NFILTERS_NUMBER, nfilters); + SDL_SetPointerProperty(props, SDL_PROP_FILE_DIALOG_WINDOW_POINTER, window); + SDL_SetStringProperty(props, SDL_PROP_FILE_DIALOG_LOCATION_STRING, default_location); + SDL_SetBooleanProperty(props, SDL_PROP_FILE_DIALOG_MANY_BOOLEAN, allow_many); + + SDL_ShowFileDialogWithProperties(SDL_FILEDIALOG_OPENFILE, callback, userdata, props); + + SDL_DestroyProperties(props); +#endif +} + +void SDL_ShowSaveFileDialog(SDL_DialogFileCallback callback, void *userdata, SDL_Window *window, const SDL_DialogFileFilter *filters, int nfilters, const char *default_location) +{ +#ifdef SDL_DIALOG_DISABLED + if (!callback) { + return; + } + SDL_SetError("SDL not built with dialog support"); + callback(userdata, NULL, -1); +#else + SDL_PropertiesID props = SDL_CreateProperties(); + + SDL_SetPointerProperty(props, SDL_PROP_FILE_DIALOG_FILTERS_POINTER, (void *) filters); + SDL_SetNumberProperty(props, SDL_PROP_FILE_DIALOG_NFILTERS_NUMBER, nfilters); + SDL_SetPointerProperty(props, SDL_PROP_FILE_DIALOG_WINDOW_POINTER, window); + SDL_SetStringProperty(props, SDL_PROP_FILE_DIALOG_LOCATION_STRING, default_location); + + SDL_ShowFileDialogWithProperties(SDL_FILEDIALOG_SAVEFILE, callback, userdata, props); + + SDL_DestroyProperties(props); +#endif +} + +void SDL_ShowOpenFolderDialog(SDL_DialogFileCallback callback, void *userdata, SDL_Window *window, const char *default_location, bool allow_many) +{ +#ifdef SDL_DIALOG_DISABLED + if (!callback) { + return; + } + SDL_SetError("SDL not built with dialog support"); + callback(userdata, NULL, -1); +#else + SDL_PropertiesID props = SDL_CreateProperties(); + + SDL_SetPointerProperty(props, SDL_PROP_FILE_DIALOG_WINDOW_POINTER, window); + SDL_SetStringProperty(props, SDL_PROP_FILE_DIALOG_LOCATION_STRING, default_location); + SDL_SetBooleanProperty(props, SDL_PROP_FILE_DIALOG_MANY_BOOLEAN, allow_many); + + SDL_ShowFileDialogWithProperties(SDL_FILEDIALOG_OPENFOLDER, callback, userdata, props); + + SDL_DestroyProperties(props); +#endif +} diff --git a/libs/SDL3/src/video/ngage/SDL_ngageevents_c.h b/libs/SDL3/src/dialog/SDL_dialog.h similarity index 86% rename from libs/SDL3/src/video/ngage/SDL_ngageevents_c.h rename to libs/SDL3/src/dialog/SDL_dialog.h index 46fa9088..b3d3318f 100644 --- a/libs/SDL3/src/video/ngage/SDL_ngageevents_c.h +++ b/libs/SDL3/src/dialog/SDL_dialog.h @@ -19,8 +19,4 @@ 3. This notice may not be removed or altered from any source distribution. */ -#include "SDL_internal.h" - -#include "SDL_ngagevideo.h" - -extern void NGAGE_PumpEvents(SDL_VideoDevice *_this); +void SDL_SYS_ShowFileDialogWithProperties(SDL_FileDialogType type, SDL_DialogFileCallback callback, void *userdata, SDL_PropertiesID props); diff --git a/libs/SDL3/src/dialog/SDL_dialog_utils.c b/libs/SDL3/src/dialog/SDL_dialog_utils.c index ef3fbdf2..9d7acbe5 100644 --- a/libs/SDL3/src/dialog/SDL_dialog_utils.c +++ b/libs/SDL3/src/dialog/SDL_dialog_utils.c @@ -59,7 +59,7 @@ char *convert_filters(const SDL_DialogFileFilter *filters, int nfilters, return NULL; } - terminator = f[1].name ? separator : suffix; + terminator = ((i + 1) < nfilters) ? separator : suffix; new_length = SDL_strlen(combined) + SDL_strlen(converted) + SDL_strlen(terminator) + 1; @@ -78,22 +78,19 @@ char *convert_filters(const SDL_DialogFileFilter *filters, int nfilters, SDL_free(converted); } - // If the filter list is empty, put the suffix - if (!filters->name || !filters->pattern) { - new_length = SDL_strlen(combined) + SDL_strlen(suffix) + 1; + new_length = SDL_strlen(combined) + SDL_strlen(suffix) + 1; - new_combined = (char *)SDL_realloc(combined, new_length); + new_combined = (char *)SDL_realloc(combined, new_length); - if (!new_combined) { - SDL_free(combined); - return NULL; - } - - combined = new_combined; - - SDL_strlcat(combined, suffix, new_length); + if (!new_combined) { + SDL_free(combined); + return NULL; } + combined = new_combined; + + SDL_strlcat(combined, suffix, new_length); + return combined; } diff --git a/libs/SDL3/src/dialog/android/SDL_androiddialog.c b/libs/SDL3/src/dialog/android/SDL_androiddialog.c index fd9e102e..d4723f84 100644 --- a/libs/SDL3/src/dialog/android/SDL_androiddialog.c +++ b/libs/SDL3/src/dialog/android/SDL_androiddialog.c @@ -20,26 +20,39 @@ */ #include "SDL_internal.h" +#include "../SDL_dialog.h" #include "../../core/android/SDL_android.h" -void SDLCALL SDL_ShowOpenFileDialog(SDL_DialogFileCallback callback, void *userdata, SDL_Window *window, const SDL_DialogFileFilter *filters, int nfilters, const char *default_location, bool allow_many) +void SDL_SYS_ShowFileDialogWithProperties(SDL_FileDialogType type, SDL_DialogFileCallback callback, void *userdata, SDL_PropertiesID props) { - if (!Android_JNI_OpenFileDialog(callback, userdata, filters, nfilters, false, allow_many)) { + SDL_DialogFileFilter *filters = SDL_GetPointerProperty(props, SDL_PROP_FILE_DIALOG_FILTERS_POINTER, NULL); + int nfilters = (int) SDL_GetNumberProperty(props, SDL_PROP_FILE_DIALOG_NFILTERS_NUMBER, 0); + bool allow_many = SDL_GetBooleanProperty(props, SDL_PROP_FILE_DIALOG_MANY_BOOLEAN, false); + bool is_save; + + if (SDL_GetHint(SDL_HINT_FILE_DIALOG_DRIVER) != NULL) { + SDL_SetError("File dialog driver unsupported (don't set SDL_HINT_FILE_DIALOG_DRIVER)"); + callback(userdata, NULL, -1); + return; + } + + switch (type) { + case SDL_FILEDIALOG_OPENFILE: + is_save = false; + break; + + case SDL_FILEDIALOG_SAVEFILE: + is_save = true; + break; + + case SDL_FILEDIALOG_OPENFOLDER: + SDL_Unsupported(); + callback(userdata, NULL, -1); + return; + }; + + if (!Android_JNI_OpenFileDialog(callback, userdata, filters, nfilters, is_save, allow_many)) { // SDL_SetError is already called when it fails callback(userdata, NULL, -1); } } - -void SDLCALL SDL_ShowSaveFileDialog(SDL_DialogFileCallback callback, void *userdata, SDL_Window *window, const SDL_DialogFileFilter *filters, int nfilters, const char *default_location) -{ - if (!Android_JNI_OpenFileDialog(callback, userdata, filters, nfilters, true, false)) { - // SDL_SetError is already called when it fails - callback(userdata, NULL, -1); - } -} - -void SDLCALL SDL_ShowOpenFolderDialog(SDL_DialogFileCallback callback, void *userdata, SDL_Window *window, const char *default_location, bool allow_many) -{ - SDL_Unsupported(); - callback(userdata, NULL, -1); -} diff --git a/libs/SDL3/src/dialog/cocoa/SDL_cocoadialog.m b/libs/SDL3/src/dialog/cocoa/SDL_cocoadialog.m index 25006637..22f67b6c 100644 --- a/libs/SDL3/src/dialog/cocoa/SDL_cocoadialog.m +++ b/libs/SDL3/src/dialog/cocoa/SDL_cocoadialog.m @@ -19,6 +19,7 @@ 3. This notice may not be removed or altered from any source distribution. */ #include "SDL_internal.h" +#include "../SDL_dialog.h" #include "../SDL_dialog_utils.h" #ifdef SDL_PLATFORM_MACOS @@ -26,15 +27,16 @@ #import #import -typedef enum +void SDL_SYS_ShowFileDialogWithProperties(SDL_FileDialogType type, SDL_DialogFileCallback callback, void *userdata, SDL_PropertiesID props) { - FDT_SAVE, - FDT_OPEN, - FDT_OPENFOLDER -} cocoa_FileDialogType; + SDL_Window* window = SDL_GetPointerProperty(props, SDL_PROP_FILE_DIALOG_WINDOW_POINTER, NULL); + SDL_DialogFileFilter *filters = SDL_GetPointerProperty(props, SDL_PROP_FILE_DIALOG_FILTERS_POINTER, NULL); + int nfilters = (int) SDL_GetNumberProperty(props, SDL_PROP_FILE_DIALOG_NFILTERS_NUMBER, 0); + bool allow_many = SDL_GetBooleanProperty(props, SDL_PROP_FILE_DIALOG_MANY_BOOLEAN, false); + const char* default_location = SDL_GetStringProperty(props, SDL_PROP_FILE_DIALOG_LOCATION_STRING, NULL); + const char* title = SDL_GetStringProperty(props, SDL_PROP_FILE_DIALOG_TITLE_STRING, NULL); + const char* accept = SDL_GetStringProperty(props, SDL_PROP_FILE_DIALOG_ACCEPT_STRING, NULL); -void show_file_dialog(cocoa_FileDialogType type, SDL_DialogFileCallback callback, void* userdata, SDL_Window* window, const SDL_DialogFileFilter *filters, int nfilters, const char* default_location, bool allow_many) -{ if (filters) { const char *msg = validate_filters(filters, nfilters); @@ -46,7 +48,7 @@ void show_file_dialog(cocoa_FileDialogType type, SDL_DialogFileCallback callback } if (SDL_GetHint(SDL_HINT_FILE_DIALOG_DRIVER) != NULL) { - SDL_SetError("File dialog driver unsupported"); + SDL_SetError("File dialog driver unsupported (don't set SDL_HINT_FILE_DIALOG_DRIVER)"); callback(userdata, NULL, -1); return; } @@ -56,15 +58,17 @@ void show_file_dialog(cocoa_FileDialogType type, SDL_DialogFileCallback callback NSOpenPanel *dialog_as_open; switch (type) { - case FDT_SAVE: + case SDL_FILEDIALOG_SAVEFILE: dialog = [NSSavePanel savePanel]; break; - case FDT_OPEN: + + case SDL_FILEDIALOG_OPENFILE: dialog_as_open = [NSOpenPanel openPanel]; [dialog_as_open setAllowsMultipleSelection:((allow_many == true) ? YES : NO)]; dialog = dialog_as_open; break; - case FDT_OPENFOLDER: + + case SDL_FILEDIALOG_OPENFOLDER: dialog_as_open = [NSOpenPanel openPanel]; [dialog_as_open setCanChooseFiles:NO]; [dialog_as_open setCanChooseDirectories:YES]; @@ -73,6 +77,14 @@ void show_file_dialog(cocoa_FileDialogType type, SDL_DialogFileCallback callback break; }; + if (title) { + [dialog setTitle:[NSString stringWithUTF8String:title]]; + } + + if (accept) { + [dialog setPrompt:[NSString stringWithUTF8String:accept]]; + } + if (filters) { // On macOS 11.0 and up, this is an array of UTType. Prior to that, it's an array of NSString NSMutableArray *types = [[NSMutableArray alloc] initWithCapacity:nfilters ]; @@ -175,19 +187,4 @@ void show_file_dialog(cocoa_FileDialogType type, SDL_DialogFileCallback callback } } -void SDL_ShowOpenFileDialog(SDL_DialogFileCallback callback, void* userdata, SDL_Window* window, const SDL_DialogFileFilter *filters, int nfilters, const char* default_location, bool allow_many) -{ - show_file_dialog(FDT_OPEN, callback, userdata, window, filters, nfilters, default_location, allow_many); -} - -void SDL_ShowSaveFileDialog(SDL_DialogFileCallback callback, void* userdata, SDL_Window* window, const SDL_DialogFileFilter *filters, int nfilters, const char* default_location) -{ - show_file_dialog(FDT_SAVE, callback, userdata, window, filters, nfilters, default_location, 0); -} - -void SDL_ShowOpenFolderDialog(SDL_DialogFileCallback callback, void* userdata, SDL_Window* window, const char* default_location, bool allow_many) -{ - show_file_dialog(FDT_OPENFOLDER, callback, userdata, window, NULL, 0, default_location, allow_many); -} - #endif // SDL_PLATFORM_MACOS diff --git a/libs/SDL3/src/dialog/dummy/SDL_dummydialog.c b/libs/SDL3/src/dialog/dummy/SDL_dummydialog.c index 81ba1f59..16cf1802 100644 --- a/libs/SDL3/src/dialog/dummy/SDL_dummydialog.c +++ b/libs/SDL3/src/dialog/dummy/SDL_dummydialog.c @@ -20,21 +20,11 @@ */ #include "SDL_internal.h" +#include "../SDL_dialog.h" + #ifdef SDL_DIALOG_DUMMY -void SDL_ShowOpenFileDialog(SDL_DialogFileCallback callback, void* userdata, SDL_Window* window, const SDL_DialogFileFilter *filters, int nfilters, const char* default_location, bool allow_many) -{ - SDL_Unsupported(); - callback(userdata, NULL, -1); -} - -void SDL_ShowSaveFileDialog(SDL_DialogFileCallback callback, void* userdata, SDL_Window* window, const SDL_DialogFileFilter *filters, int nfilters, const char* default_location) -{ - SDL_Unsupported(); - callback(userdata, NULL, -1); -} - -void SDL_ShowOpenFolderDialog(SDL_DialogFileCallback callback, void* userdata, SDL_Window* window, const char* default_location, bool allow_many) +void SDL_SYS_ShowFileDialogWithProperties(SDL_FileDialogType type, SDL_DialogFileCallback callback, void *userdata, SDL_PropertiesID props) { SDL_Unsupported(); callback(userdata, NULL, -1); diff --git a/libs/SDL3/src/dialog/haiku/SDL_haikudialog.cc b/libs/SDL3/src/dialog/haiku/SDL_haikudialog.cc index 9d6e3730..1ebf000a 100644 --- a/libs/SDL3/src/dialog/haiku/SDL_haikudialog.cc +++ b/libs/SDL3/src/dialog/haiku/SDL_haikudialog.cc @@ -20,9 +20,11 @@ */ #include "SDL_internal.h" extern "C" { +#include "../SDL_dialog.h" #include "../SDL_dialog_utils.h" } #include "../../core/haiku/SDL_BeApp.h" +#include "../../video/haiku/SDL_BWin.h" #include #include @@ -190,8 +192,35 @@ private: SDLBRefFilter *m_filter; }; -void ShowDialog(bool save, SDL_DialogFileCallback callback, void *userdata, bool many, bool modal, const SDL_DialogFileFilter *filters, int nfilters, bool folder, const char *location) +void SDL_SYS_ShowFileDialogWithProperties(SDL_FileDialogType type, SDL_DialogFileCallback callback, void *userdata, SDL_PropertiesID props) { + SDL_Window* window = (SDL_Window*) SDL_GetPointerProperty(props, SDL_PROP_FILE_DIALOG_WINDOW_POINTER, NULL); + SDL_DialogFileFilter* filters = (SDL_DialogFileFilter*) SDL_GetPointerProperty(props, SDL_PROP_FILE_DIALOG_FILTERS_POINTER, NULL); + int nfilters = (int) SDL_GetNumberProperty(props, SDL_PROP_FILE_DIALOG_NFILTERS_NUMBER, 0); + bool many = SDL_GetBooleanProperty(props, SDL_PROP_FILE_DIALOG_MANY_BOOLEAN, false); + const char* location = SDL_GetStringProperty(props, SDL_PROP_FILE_DIALOG_LOCATION_STRING, NULL); + const char* title = SDL_GetStringProperty(props, SDL_PROP_FILE_DIALOG_TITLE_STRING, NULL); + const char* accept = SDL_GetStringProperty(props, SDL_PROP_FILE_DIALOG_ACCEPT_STRING, NULL); + const char* cancel = SDL_GetStringProperty(props, SDL_PROP_FILE_DIALOG_CANCEL_STRING, NULL); + + bool modal = !!window; + + bool save = false; + bool folder = false; + + switch (type) { + case SDL_FILEDIALOG_SAVEFILE: + save = true; + break; + + case SDL_FILEDIALOG_OPENFILE: + break; + + case SDL_FILEDIALOG_OPENFOLDER: + folder = true; + break; + }; + if (!SDL_InitBeApp()) { char* err = SDL_strdup(SDL_GetError()); SDL_SetError("Couldn't init Be app: %s", err); @@ -238,22 +267,27 @@ void ShowDialog(bool save, SDL_DialogFileCallback callback, void *userdata, bool } BFilePanel *panel = new BFilePanel(save ? B_SAVE_PANEL : B_OPEN_PANEL, messenger, location ? &entryref : NULL, folder ? B_DIRECTORY_NODE : B_FILE_NODE, many, NULL, filter, modal); + + if (title) { + panel->Window()->SetTitle(title); + } + + if (accept) { + panel->SetButtonLabel(B_DEFAULT_BUTTON, accept); + } + + if (cancel) { + panel->SetButtonLabel(B_CANCEL_BUTTON, cancel); + } + + if (window) { + SDL_BWin *bwin = (SDL_BWin *)(window->internal); + panel->Window()->SetLook(B_MODAL_WINDOW_LOOK); + panel->Window()->SetFeel(B_MODAL_SUBSET_WINDOW_FEEL); + panel->Window()->AddToSubset(bwin); + } + looper->SetToBeFreed(messenger, panel, filter); looper->Run(); panel->Show(); } - -void SDL_ShowOpenFileDialog(SDL_DialogFileCallback callback, void *userdata, SDL_Window *window, const SDL_DialogFileFilter *filters, int nfilters, const char *default_location, bool allow_many) -{ - ShowDialog(false, callback, userdata, allow_many == true, !!window, filters, nfilters, false, default_location); -} - -void SDL_ShowSaveFileDialog(SDL_DialogFileCallback callback, void *userdata, SDL_Window *window, const SDL_DialogFileFilter *filters, int nfilters, const char *default_location) -{ - ShowDialog(true, callback, userdata, false, !!window, filters, nfilters, false, default_location); -} - -void SDL_ShowOpenFolderDialog(SDL_DialogFileCallback callback, void *userdata, SDL_Window *window, const char* default_location, bool allow_many) -{ - ShowDialog(false, callback, userdata, allow_many == true, !!window, NULL, 0, true, default_location); -} diff --git a/libs/SDL3/src/dialog/unix/SDL_portaldialog.c b/libs/SDL3/src/dialog/unix/SDL_portaldialog.c index ac6b575c..2d0567dd 100644 --- a/libs/SDL3/src/dialog/unix/SDL_portaldialog.c +++ b/libs/SDL3/src/dialog/unix/SDL_portaldialog.c @@ -275,8 +275,43 @@ not_our_signal: return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } -static void DBus_OpenDialog(const char *method, const char *method_title, SDL_DialogFileCallback callback, void* userdata, SDL_Window* window, const SDL_DialogFileFilter *filters, int nfilters, const char* default_location, bool allow_many, int open_folders) +void SDL_Portal_ShowFileDialogWithProperties(SDL_FileDialogType type, SDL_DialogFileCallback callback, void *userdata, SDL_PropertiesID props) { + const char *method; + const char *method_title; + + SDL_Window* window = SDL_GetPointerProperty(props, SDL_PROP_FILE_DIALOG_WINDOW_POINTER, NULL); + SDL_DialogFileFilter *filters = SDL_GetPointerProperty(props, SDL_PROP_FILE_DIALOG_FILTERS_POINTER, NULL); + int nfilters = (int) SDL_GetNumberProperty(props, SDL_PROP_FILE_DIALOG_NFILTERS_NUMBER, 0); + bool allow_many = SDL_GetBooleanProperty(props, SDL_PROP_FILE_DIALOG_MANY_BOOLEAN, false); + const char* default_location = SDL_GetStringProperty(props, SDL_PROP_FILE_DIALOG_LOCATION_STRING, NULL); + const char* accept = SDL_GetStringProperty(props, SDL_PROP_FILE_DIALOG_ACCEPT_STRING, NULL); + bool open_folders = false; + + switch (type) { + case SDL_FILEDIALOG_OPENFILE: + method = "OpenFile"; + method_title = SDL_GetStringProperty(props, SDL_PROP_FILE_DIALOG_TITLE_STRING, "Open File"); + break; + + case SDL_FILEDIALOG_SAVEFILE: + method = "SaveFile"; + method_title = SDL_GetStringProperty(props, SDL_PROP_FILE_DIALOG_TITLE_STRING, "Save File"); + break; + + case SDL_FILEDIALOG_OPENFOLDER: + method = "OpenFile"; + method_title = SDL_GetStringProperty(props, SDL_PROP_FILE_DIALOG_TITLE_STRING, "Open Folder"); + open_folders = true; + break; + + default: + /* This is already checked in ../SDL_dialog.c; this silences compiler warnings */ + SDL_SetError("Invalid file dialog type: %d", type); + callback(userdata, NULL, -1); + return; + } + SDL_DBusContext *dbus = SDL_DBus_GetContext(); DBusMessage *msg; DBusMessageIter params, options; @@ -285,7 +320,7 @@ static void DBus_OpenDialog(const char *method, const char *method_title, SDL_Di int filter_len; static uint32_t handle_id = 0; static char *default_parent_window = ""; - SDL_PropertiesID props = SDL_GetWindowProperties(window); + SDL_PropertiesID window_props = SDL_GetWindowProperties(window); const char *err_msg = validate_filters(filters, nfilters); @@ -311,8 +346,8 @@ static void DBus_OpenDialog(const char *method, const char *method_title, SDL_Di dbus->message_iter_init_append(msg, ¶ms); handle_str = default_parent_window; - if (props) { - const char *parent_handle = SDL_GetStringProperty(props, SDL_PROP_WINDOW_WAYLAND_XDG_TOPLEVEL_EXPORT_HANDLE_STRING, NULL); + if (window_props) { + const char *parent_handle = SDL_GetStringProperty(window_props, SDL_PROP_WINDOW_WAYLAND_XDG_TOPLEVEL_EXPORT_HANDLE_STRING, NULL); if (parent_handle) { size_t len = SDL_strlen(parent_handle); len += sizeof(WAYLAND_HANDLE_PREFIX) + 1; @@ -324,7 +359,7 @@ static void DBus_OpenDialog(const char *method, const char *method_title, SDL_Di SDL_snprintf(handle_str, len, "%s%s", WAYLAND_HANDLE_PREFIX, parent_handle); } else { - const Uint64 xid = (Uint64)SDL_GetNumberProperty(props, SDL_PROP_WINDOW_X11_WINDOW_NUMBER, 0); + const Uint64 xid = (Uint64)SDL_GetNumberProperty(window_props, SDL_PROP_WINDOW_X11_WINDOW_NUMBER, 0); if (xid) { const size_t len = sizeof(X11_HANDLE_PREFIX) + 24; // A 64-bit number can be 20 characters max. handle_str = SDL_malloc(len * sizeof(char)); @@ -357,7 +392,7 @@ static void DBus_OpenDialog(const char *method, const char *method_title, SDL_Di SDL_free(handle_str); DBus_AppendBoolOption(dbus, &options, "modal", !!window); - if (allow_many == true) { + if (allow_many) { DBus_AppendBoolOption(dbus, &options, "multiple", 1); } if (open_folders) { @@ -369,6 +404,9 @@ static void DBus_OpenDialog(const char *method, const char *method_title, SDL_Di if (default_location) { DBus_AppendByteArray(dbus, &options, "current_folder", default_location); } + if (accept) { + DBus_AppendStringOption(dbus, &options, "accept_label", accept); + } dbus->message_iter_close_container(¶ms, &options); DBusMessage *reply = dbus->connection_send_with_reply_and_block(dbus->session_conn, msg, DBUS_TIMEOUT_INFINITE, NULL); @@ -425,21 +463,6 @@ incorrect_type: dbus->message_unref(reply); } -void SDL_Portal_ShowOpenFileDialog(SDL_DialogFileCallback callback, void* userdata, SDL_Window* window, const SDL_DialogFileFilter *filters, int nfilters, const char* default_location, bool allow_many) -{ - DBus_OpenDialog("OpenFile", "Open File", callback, userdata, window, filters, nfilters, default_location, allow_many, 0); -} - -void SDL_Portal_ShowSaveFileDialog(SDL_DialogFileCallback callback, void* userdata, SDL_Window* window, const SDL_DialogFileFilter *filters, int nfilters, const char* default_location) -{ - DBus_OpenDialog("SaveFile", "Save File", callback, userdata, window, filters, nfilters, default_location, 0, 0); -} - -void SDL_Portal_ShowOpenFolderDialog(SDL_DialogFileCallback callback, void* userdata, SDL_Window* window, const char* default_location, bool allow_many) -{ - DBus_OpenDialog("OpenFile", "Open Folder", callback, userdata, window, NULL, 0, default_location, allow_many, 1); -} - bool SDL_Portal_detect(void) { SDL_DBusContext *dbus = SDL_DBus_GetContext(); @@ -500,19 +523,7 @@ done: // Dummy implementation to avoid compilation problems -void SDL_Portal_ShowOpenFileDialog(SDL_DialogFileCallback callback, void* userdata, SDL_Window* window, const SDL_DialogFileFilter *filters, int nfilters, const char* default_location, bool allow_many) -{ - SDL_Unsupported(); - callback(userdata, NULL, -1); -} - -void SDL_Portal_ShowSaveFileDialog(SDL_DialogFileCallback callback, void* userdata, SDL_Window* window, const SDL_DialogFileFilter *filters, int nfilters, const char* default_location) -{ - SDL_Unsupported(); - callback(userdata, NULL, -1); -} - -void SDL_Portal_ShowOpenFolderDialog(SDL_DialogFileCallback callback, void* userdata, SDL_Window* window, const char* default_location, bool allow_many) +void SDL_Portal_ShowFileDialogWithProperties(SDL_FileDialogType type, SDL_DialogFileCallback callback, void *userdata, SDL_PropertiesID props) { SDL_Unsupported(); callback(userdata, NULL, -1); diff --git a/libs/SDL3/src/dialog/unix/SDL_portaldialog.h b/libs/SDL3/src/dialog/unix/SDL_portaldialog.h index 495933cf..f7258d02 100644 --- a/libs/SDL3/src/dialog/unix/SDL_portaldialog.h +++ b/libs/SDL3/src/dialog/unix/SDL_portaldialog.h @@ -21,9 +21,7 @@ #include "SDL_internal.h" -void SDL_Portal_ShowOpenFileDialog(SDL_DialogFileCallback callback, void* userdata, SDL_Window* window, const SDL_DialogFileFilter *filters, int nfilters, const char* default_location, bool allow_many); -void SDL_Portal_ShowSaveFileDialog(SDL_DialogFileCallback callback, void* userdata, SDL_Window* window, const SDL_DialogFileFilter *filters, int nfilters, const char* default_location); -void SDL_Portal_ShowOpenFolderDialog(SDL_DialogFileCallback callback, void* userdata, SDL_Window* window, const char* default_location, bool allow_many); +void SDL_Portal_ShowFileDialogWithProperties(SDL_FileDialogType type, SDL_DialogFileCallback callback, void *userdata, SDL_PropertiesID props); /** @returns non-zero if available, zero if unavailable */ bool SDL_Portal_detect(void); diff --git a/libs/SDL3/src/dialog/unix/SDL_unixdialog.c b/libs/SDL3/src/dialog/unix/SDL_unixdialog.c index 5e81b70c..02305f32 100644 --- a/libs/SDL3/src/dialog/unix/SDL_unixdialog.c +++ b/libs/SDL3/src/dialog/unix/SDL_unixdialog.c @@ -20,19 +20,13 @@ */ #include "SDL_internal.h" +#include "../SDL_dialog.h" #include "./SDL_portaldialog.h" #include "./SDL_zenitydialog.h" -static void (*detected_open)(SDL_DialogFileCallback callback, void* userdata, SDL_Window* window, const SDL_DialogFileFilter *filters, int nfilters, const char* default_location, bool allow_many) = NULL; -static void (*detected_save)(SDL_DialogFileCallback callback, void* userdata, SDL_Window* window, const SDL_DialogFileFilter *filters, int nfilters, const char* default_location) = NULL; -static void (*detected_folder)(SDL_DialogFileCallback callback, void* userdata, SDL_Window* window, const char* default_location, bool allow_many) = NULL; +static void (*detected_function)(SDL_FileDialogType type, SDL_DialogFileCallback callback, void *userdata, SDL_PropertiesID props) = NULL; -static int detect_available_methods(const char *value); - -void SDLCALL hint_callback(void *userdata, const char *name, const char *oldValue, const char *newValue) -{ - detect_available_methods(newValue); -} +void SDLCALL hint_callback(void *userdata, const char *name, const char *oldValue, const char *newValue); static void set_callback(void) { @@ -53,58 +47,35 @@ static int detect_available_methods(const char *value) if (driver == NULL || SDL_strcmp(driver, "portal") == 0) { if (SDL_Portal_detect()) { - detected_open = SDL_Portal_ShowOpenFileDialog; - detected_save = SDL_Portal_ShowSaveFileDialog; - detected_folder = SDL_Portal_ShowOpenFolderDialog; + detected_function = SDL_Portal_ShowFileDialogWithProperties; return 1; } } if (driver == NULL || SDL_strcmp(driver, "zenity") == 0) { if (SDL_Zenity_detect()) { - detected_open = SDL_Zenity_ShowOpenFileDialog; - detected_save = SDL_Zenity_ShowSaveFileDialog; - detected_folder = SDL_Zenity_ShowOpenFolderDialog; + detected_function = SDL_Zenity_ShowFileDialogWithProperties; return 2; } } - SDL_SetError("File dialog driver unsupported"); + SDL_SetError("File dialog driver unsupported (supported values for SDL_HINT_FILE_DIALOG_DRIVER are 'zenity' and 'portal')"); return 0; } -void SDL_ShowOpenFileDialog(SDL_DialogFileCallback callback, void* userdata, SDL_Window* window, const SDL_DialogFileFilter *filters, int nfilters, const char* default_location, bool allow_many) +void SDLCALL hint_callback(void *userdata, const char *name, const char *oldValue, const char *newValue) +{ + detect_available_methods(newValue); +} + +void SDL_SYS_ShowFileDialogWithProperties(SDL_FileDialogType type, SDL_DialogFileCallback callback, void *userdata, SDL_PropertiesID props) { // Call detect_available_methods() again each time in case the situation changed - if (!detected_open && !detect_available_methods(NULL)) { + if (!detected_function && !detect_available_methods(NULL)) { // SetError() done by detect_available_methods() callback(userdata, NULL, -1); return; } - detected_open(callback, userdata, window, filters, nfilters, default_location, allow_many); -} - -void SDL_ShowSaveFileDialog(SDL_DialogFileCallback callback, void* userdata, SDL_Window* window, const SDL_DialogFileFilter *filters, int nfilters, const char* default_location) -{ - // Call detect_available_methods() again each time in case the situation changed - if (!detected_save && !detect_available_methods(NULL)) { - // SetError() done by detect_available_methods() - callback(userdata, NULL, -1); - return; - } - - detected_save(callback, userdata, window, filters, nfilters, default_location); -} - -void SDL_ShowOpenFolderDialog(SDL_DialogFileCallback callback, void* userdata, SDL_Window* window, const char* default_location, bool allow_many) -{ - // Call detect_available_methods() again each time in case the situation changed - if (!detected_folder && !detect_available_methods(NULL)) { - // SetError() done by detect_available_methods() - callback(userdata, NULL, -1); - return; - } - - detected_folder(callback, userdata, window, default_location, allow_many); + detected_function(type, callback, userdata, props); } diff --git a/libs/SDL3/src/dialog/unix/SDL_zenitydialog.c b/libs/SDL3/src/dialog/unix/SDL_zenitydialog.c index bca4ed1c..3f0f285e 100644 --- a/libs/SDL3/src/dialog/unix/SDL_zenitydialog.c +++ b/libs/SDL3/src/dialog/unix/SDL_zenitydialog.c @@ -19,53 +19,28 @@ 3. This notice may not be removed or altered from any source distribution. */ #include "SDL_internal.h" + #include "../SDL_dialog_utils.h" -#include -#include -#include -#include - -typedef enum -{ - ZENITY_MULTIPLE = 0x1, - ZENITY_DIRECTORY = 0x2, - ZENITY_SAVE = 0x4 -} zenityFlags; - +#define X11_HANDLE_MAX_WIDTH 28 typedef struct { SDL_DialogFileCallback callback; - void* userdata; - const char* filename; - const SDL_DialogFileFilter *filters; + void *userdata; + void *argv; + + /* Zenity only works with X11 handles apparently */ + char x11_window_handle[X11_HANDLE_MAX_WIDTH]; + /* These are part of argv, but are tracked separately for deallocation purposes */ int nfilters; - Uint32 flags; + char **filters_slice; + char *filename; + char *title; + char *accept; + char *cancel; } zenityArgs; -#define CLEAR_AND_RETURN() \ - { \ - while (--nextarg >= 0) { \ - SDL_free(argv[nextarg]); \ - } \ - SDL_free(argv); \ - return NULL; \ - } - -#define CHECK_OOM() \ - { \ - if (!argv[nextarg - 1]) { \ - CLEAR_AND_RETURN() \ - } \ - \ - if (nextarg > argc) { \ - SDL_SetError("Zenity dialog problem: argc (%d) < nextarg (%d)", \ - argc, nextarg); \ - CLEAR_AND_RETURN() \ - } \ - } - -char *zenity_clean_name(const char *name) +static char *zenity_clean_name(const char *name) { char *newname = SDL_strdup(name); @@ -81,118 +56,168 @@ char *zenity_clean_name(const char *name) return newname; } +static bool get_x11_window_handle(SDL_PropertiesID props, char *out) +{ + SDL_Window *window = SDL_GetPointerProperty(props, SDL_PROP_FILE_DIALOG_WINDOW_POINTER, NULL); + if (!window) { + return false; + } + SDL_PropertiesID window_props = SDL_GetWindowProperties(window); + if (!window_props) { + return false; + } + Uint64 handle = (Uint64)SDL_GetNumberProperty(window_props, SDL_PROP_WINDOW_X11_WINDOW_NUMBER, 0); + if (!handle) { + return false; + } + if (SDL_snprintf(out, X11_HANDLE_MAX_WIDTH, "0x%" SDL_PRIx64, handle) >= X11_HANDLE_MAX_WIDTH) { + return false; + }; + return true; +} + /* Exec call format: * - * /usr/bin/env zenity --file-selection --separator=\n [--multiple] - * [--directory] [--save] [--filename FILENAME] + * zenity --file-selection --separator=\n [--multiple] + * [--directory] [--save --confirm-overwrite] + * [--filename FILENAME] [--modal --attach 0x11w1nd0w] + * [--title TITLE] [--ok-label ACCEPT] + * [--cancel-label CANCEL] * [--file-filter=Filter Name | *.filt *.fn ...]... */ -static char** generate_args(const zenityArgs* info) +static zenityArgs *create_zenity_args(SDL_FileDialogType type, SDL_DialogFileCallback callback, void *userdata, SDL_PropertiesID props) { - int argc = 4; - int nextarg = 0; - char **argv = NULL; - - // ARGC PASS - if (info->flags & ZENITY_MULTIPLE) { - argc++; - } - - if (info->flags & ZENITY_DIRECTORY) { - argc++; - } - - if (info->flags & ZENITY_SAVE) { - argc++; - } - - if (info->filename) { - argc += 2; - } - - if (info->filters) { - argc += info->nfilters; - } - - argv = SDL_malloc(sizeof(char *) * argc + 1); - if (!argv) { + zenityArgs *args = SDL_calloc(1, sizeof(*args)); + if (!args) { return NULL; } + args->callback = callback; + args->userdata = userdata; + args->nfilters = SDL_GetNumberProperty(props, SDL_PROP_FILE_DIALOG_NFILTERS_NUMBER, 0); - argv[nextarg++] = SDL_strdup("/usr/bin/env"); - CHECK_OOM() - argv[nextarg++] = SDL_strdup("zenity"); - CHECK_OOM() - argv[nextarg++] = SDL_strdup("--file-selection"); - CHECK_OOM() - argv[nextarg++] = SDL_strdup("--separator=\n"); - CHECK_OOM() + const char **argv = SDL_malloc( + sizeof(*argv) * (3 /* zenity --file-selection --separator=\n */ + + 1 /* --multiple */ + + 2 /* --directory | --save --confirm-overwrite */ + + 2 /* --filename [file] */ + + 3 /* --modal --attach [handle] */ + + 2 /* --title [title] */ + + 2 /* --ok-label [label] */ + + 2 /* --cancel-label [label] */ + + args->nfilters + 1 /* NULL */)); + if (!argv) { + goto cleanup; + } + args->argv = argv; + + /* Properties can be destroyed as soon as the function returns; copy over what we need. */ +#define COPY_STRING_PROPERTY(dst, prop) \ + { \ + const char *str = SDL_GetStringProperty(props, prop, NULL); \ + if (str) { \ + dst = SDL_strdup(str); \ + if (!dst) { \ + goto cleanup; \ + } \ + } \ + } + + COPY_STRING_PROPERTY(args->filename, SDL_PROP_FILE_DIALOG_LOCATION_STRING); + COPY_STRING_PROPERTY(args->title, SDL_PROP_FILE_DIALOG_TITLE_STRING); + COPY_STRING_PROPERTY(args->accept, SDL_PROP_FILE_DIALOG_ACCEPT_STRING); + COPY_STRING_PROPERTY(args->cancel, SDL_PROP_FILE_DIALOG_CANCEL_STRING); +#undef COPY_STRING_PROPERTY // ARGV PASS - if (info->flags & ZENITY_MULTIPLE) { - argv[nextarg++] = SDL_strdup("--multiple"); - CHECK_OOM() + int argc = 0; + argv[argc++] = "zenity"; + argv[argc++] = "--file-selection"; + argv[argc++] = "--separator=\n"; + + if (SDL_GetBooleanProperty(props, SDL_PROP_FILE_DIALOG_MANY_BOOLEAN, false)) { + argv[argc++] = "--multiple"; } - if (info->flags & ZENITY_DIRECTORY) { - argv[nextarg++] = SDL_strdup("--directory"); - CHECK_OOM() + switch (type) { + case SDL_FILEDIALOG_OPENFILE: + break; + + case SDL_FILEDIALOG_SAVEFILE: + argv[argc++] = "--save"; + /* Asking before overwriting while saving seems like a sane default */ + argv[argc++] = "--confirm-overwrite"; + break; + + case SDL_FILEDIALOG_OPENFOLDER: + argv[argc++] = "--directory"; + break; + }; + + if (args->filename) { + argv[argc++] = "--filename"; + argv[argc++] = args->filename; } - if (info->flags & ZENITY_SAVE) { - argv[nextarg++] = SDL_strdup("--save"); - CHECK_OOM() + if (get_x11_window_handle(props, args->x11_window_handle)) { + argv[argc++] = "--modal"; + argv[argc++] = "--attach"; + argv[argc++] = args->x11_window_handle; } - if (info->filename) { - argv[nextarg++] = SDL_strdup("--filename"); - CHECK_OOM() - - argv[nextarg++] = SDL_strdup(info->filename); - CHECK_OOM() + if (args->title) { + argv[argc++] = "--title"; + argv[argc++] = args->title; } - if (info->filters) { - for (int i = 0; i < info->nfilters; i++) { - char *filter_str = convert_filter(info->filters[i], + if (args->accept) { + argv[argc++] = "--ok-label"; + argv[argc++] = args->accept; + } + + if (args->cancel) { + argv[argc++] = "--cancel-label"; + argv[argc++] = args->cancel; + } + + const SDL_DialogFileFilter *filters = SDL_GetPointerProperty(props, SDL_PROP_FILE_DIALOG_FILTERS_POINTER, NULL); + if (filters) { + args->filters_slice = (char **)&argv[argc]; + for (int i = 0; i < args->nfilters; i++) { + char *filter_str = convert_filter(filters[i], zenity_clean_name, "--file-filter=", " | ", "", "*.", " *.", ""); if (!filter_str) { - CLEAR_AND_RETURN() + while (i--) { + SDL_free(args->filters_slice[i]); + } + goto cleanup; } - argv[nextarg++] = filter_str; - CHECK_OOM() + args->filters_slice[i] = filter_str; } + argc += args->nfilters; } - argv[nextarg++] = NULL; - - return argv; -} - -void free_args(char **argv) -{ - char **ptr = argv; - - while (*ptr) { - SDL_free(*ptr); - ptr++; - } + argv[argc] = NULL; + return args; +cleanup: + SDL_free(args->filename); + SDL_free(args->title); + SDL_free(args->accept); + SDL_free(args->cancel); SDL_free(argv); + SDL_free(args); + return NULL; } // TODO: Zenity survives termination of the parent -static void run_zenity(zenityArgs* arg_struct) +static void run_zenity(SDL_DialogFileCallback callback, void *userdata, void *argv) { - SDL_DialogFileCallback callback = arg_struct->callback; - void* userdata = arg_struct->userdata; SDL_Process *process = NULL; - char **args = NULL; SDL_Environment *env = NULL; int status = -1; size_t bytes_read = 0; @@ -201,11 +226,6 @@ static void run_zenity(zenityArgs* arg_struct) char **array = NULL; bool result = false; - args = generate_args(arg_struct); - if (!args) { - goto done; - } - env = SDL_CreateEnvironment(true); if (!env) { goto done; @@ -221,7 +241,7 @@ static void run_zenity(zenityArgs* arg_struct) SDL_SetEnvironmentVariable(env, "ZENITY_TIMEOUT", "2", true); SDL_PropertiesID props = SDL_CreateProperties(); - SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ARGS_POINTER, args); + SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ARGS_POINTER, argv); SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ENVIRONMENT_POINTER, env); SDL_SetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDIN_NUMBER, SDL_PROCESS_STDIO_NULL); SDL_SetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDOUT_NUMBER, SDL_PROCESS_STDIO_APP); @@ -251,7 +271,7 @@ static void run_zenity(zenityArgs* arg_struct) if (i < bytes_read - 1) { array[narray] = container + i + 1; narray++; - char **new_array = (char **) SDL_realloc(array, (narray + 1) * sizeof(char *)); + char **new_array = (char **)SDL_realloc(array, (narray + 1) * sizeof(char *)); if (!new_array) { goto done; } @@ -263,7 +283,7 @@ static void run_zenity(zenityArgs* arg_struct) // 0 = the user chose one or more files, 1 = the user canceled the dialog if (status == 0 || status == 1) { - callback(userdata, (const char * const*)array, -1); + callback(userdata, (const char *const *)array, -1); } else { SDL_SetError("Could not run zenity: exit code %d", status); callback(userdata, NULL, -1); @@ -274,7 +294,6 @@ static void run_zenity(zenityArgs* arg_struct) done: SDL_free(array); SDL_free(container); - free_args(args); SDL_DestroyEnvironment(env); SDL_DestroyProcess(process); @@ -283,90 +302,41 @@ done: } } -static int run_zenity_thread(void* ptr) +static void free_zenity_args(zenityArgs *args) { - run_zenity(ptr); - SDL_free(ptr); + if (args->filters_slice) { + for (int i = 0; i < args->nfilters; i++) { + SDL_free(args->filters_slice[i]); + } + } + SDL_free(args->filename); + SDL_free(args->title); + SDL_free(args->accept); + SDL_free(args->cancel); + SDL_free(args->argv); + SDL_free(args); +} + +static int run_zenity_thread(void *ptr) +{ + zenityArgs *args = ptr; + run_zenity(args->callback, args->userdata, args->argv); + free_zenity_args(args); return 0; } -void SDL_Zenity_ShowOpenFileDialog(SDL_DialogFileCallback callback, void* userdata, SDL_Window* window, const SDL_DialogFileFilter *filters, int nfilters, const char* default_location, bool allow_many) +void SDL_Zenity_ShowFileDialogWithProperties(SDL_FileDialogType type, SDL_DialogFileCallback callback, void *userdata, SDL_PropertiesID props) { - zenityArgs *args; - SDL_Thread *thread; - - args = SDL_malloc(sizeof(*args)); + zenityArgs *args = create_zenity_args(type, callback, userdata, props); if (!args) { callback(userdata, NULL, -1); return; } - args->callback = callback; - args->userdata = userdata; - args->filename = default_location; - args->filters = filters; - args->nfilters = nfilters; - args->flags = (allow_many == true) ? ZENITY_MULTIPLE : 0; + SDL_Thread *thread = SDL_CreateThread(run_zenity_thread, "SDL_ZenityFileDialog", (void *)args); - thread = SDL_CreateThread(run_zenity_thread, "SDL_ShowOpenFileDialog", (void *) args); - - if (thread == NULL) { - callback(userdata, NULL, -1); - return; - } - - SDL_DetachThread(thread); -} - -void SDL_Zenity_ShowSaveFileDialog(SDL_DialogFileCallback callback, void* userdata, SDL_Window* window, const SDL_DialogFileFilter *filters, int nfilters, const char* default_location) -{ - zenityArgs *args; - SDL_Thread *thread; - - args = SDL_malloc(sizeof(zenityArgs)); - if (args == NULL) { - callback(userdata, NULL, -1); - return; - } - - args->callback = callback; - args->userdata = userdata; - args->filename = default_location; - args->filters = filters; - args->nfilters = nfilters; - args->flags = ZENITY_SAVE; - - thread = SDL_CreateThread(run_zenity_thread, "SDL_ShowSaveFileDialog", (void *) args); - - if (thread == NULL) { - callback(userdata, NULL, -1); - return; - } - - SDL_DetachThread(thread); -} - -void SDL_Zenity_ShowOpenFolderDialog(SDL_DialogFileCallback callback, void* userdata, SDL_Window* window, const char* default_location, bool allow_many) -{ - zenityArgs *args; - SDL_Thread *thread; - - args = SDL_malloc(sizeof(zenityArgs)); - if (args == NULL) { - callback(userdata, NULL, -1); - return; - } - - args->callback = callback; - args->userdata = userdata; - args->filename = default_location; - args->filters = NULL; - args->nfilters = 0; - args->flags = ((allow_many == true) ? ZENITY_MULTIPLE : 0) | ZENITY_DIRECTORY; - - thread = SDL_CreateThread(run_zenity_thread, "SDL_ShowOpenFolderDialog", (void *) args); - - if (thread == NULL) { + if (!thread) { + free_zenity_args(args); callback(userdata, NULL, -1); return; } @@ -377,7 +347,7 @@ void SDL_Zenity_ShowOpenFolderDialog(SDL_DialogFileCallback callback, void* user bool SDL_Zenity_detect(void) { const char *args[] = { - "/usr/bin/env", "zenity", "--version", NULL + "zenity", "--version", NULL }; int status = -1; diff --git a/libs/SDL3/src/dialog/unix/SDL_zenitydialog.h b/libs/SDL3/src/dialog/unix/SDL_zenitydialog.h index b17f79f3..1ccf5f20 100644 --- a/libs/SDL3/src/dialog/unix/SDL_zenitydialog.h +++ b/libs/SDL3/src/dialog/unix/SDL_zenitydialog.h @@ -21,9 +21,7 @@ #include "SDL_internal.h" -extern void SDL_Zenity_ShowOpenFileDialog(SDL_DialogFileCallback callback, void* userdata, SDL_Window* window, const SDL_DialogFileFilter *filters, int nfilters, const char* default_location, bool allow_many); -extern void SDL_Zenity_ShowSaveFileDialog(SDL_DialogFileCallback callback, void* userdata, SDL_Window* window, const SDL_DialogFileFilter *filters, int nfilters, const char* default_location); -extern void SDL_Zenity_ShowOpenFolderDialog(SDL_DialogFileCallback callback, void* userdata, SDL_Window* window, const char* default_location, bool allow_many); +extern void SDL_Zenity_ShowFileDialogWithProperties(SDL_FileDialogType type, SDL_DialogFileCallback callback, void *userdata, SDL_PropertiesID props); /** @returns non-zero if available, zero if unavailable */ extern bool SDL_Zenity_detect(void); diff --git a/libs/SDL3/src/dialog/windows/SDL_windowsdialog.c b/libs/SDL3/src/dialog/windows/SDL_windowsdialog.c index 9bb6d991..9f647ae2 100644 --- a/libs/SDL3/src/dialog/windows/SDL_windowsdialog.c +++ b/libs/SDL3/src/dialog/windows/SDL_windowsdialog.c @@ -19,6 +19,7 @@ 3. This notice may not be removed or altered from any source distribution. */ #include "SDL_internal.h" +#include "../SDL_dialog.h" #include "../SDL_dialog_utils.h" #include @@ -32,7 +33,7 @@ typedef struct { - int is_save; + bool is_save; const SDL_DialogFileFilter *filters; int nfilters; const char* default_file; @@ -40,6 +41,9 @@ typedef struct DWORD flags; SDL_DialogFileCallback callback; void* userdata; + const char* title; + const char* accept; + const char* cancel; } winArgs; typedef struct @@ -48,6 +52,9 @@ typedef struct SDL_DialogFileCallback callback; const char* default_folder; void* userdata; + const char* title; + const char* accept; + const char* cancel; } winFArgs; /** Converts dialog.nFilterIndex to SDL-compatible value */ @@ -56,11 +63,29 @@ int getFilterIndex(int as_reported_by_windows) return as_reported_by_windows - 1; } +char *clear_filt_names(const char *filt) +{ + char *cleared = SDL_strdup(filt); + + for (char *c = cleared; *c; c++) { + /* 0x01 bytes are used as temporary replacement for the various 0x00 + bytes required by Win32 (one null byte between each filter, two at + the end of the filters). Filter out these bytes from the filter names + to avoid early-ending the filters if someone puts two consecutive + 0x01 bytes in their filter names. */ + if (*c == '\x01') { + *c = ' '; + } + } + + return cleared; +} + // TODO: The new version of file dialogs void windows_ShowFileDialog(void *ptr) { winArgs *args = (winArgs *) ptr; - int is_save = args->is_save; + bool is_save = args->is_save; const SDL_DialogFileFilter *filters = args->filters; int nfilters = args->nfilters; const char* default_file = args->default_file; @@ -68,6 +93,7 @@ void windows_ShowFileDialog(void *ptr) DWORD flags = args->flags; SDL_DialogFileCallback callback = args->callback; void* userdata = args->userdata; + const char *title = args->title; /* GetOpenFileName and GetSaveFileName have the same signature (yes, LPOPENFILENAMEW even for the save dialog) */ @@ -153,7 +179,7 @@ void windows_ShowFileDialog(void *ptr) if (filters) { // '\x01' is used in place of a null byte // suffix needs two null bytes in case the filter list is empty - char *filterlist = convert_filters(filters, nfilters, NULL, "", "", + char *filterlist = convert_filters(filters, nfilters, clear_filt_names, "", "", "\x01\x01", "", "\x01", "\x01", "*.", ";*.", ""); @@ -185,6 +211,34 @@ void windows_ShowFileDialog(void *ptr) SDL_free(filterlist); } + wchar_t *title_w = NULL; + + if (title) { + int title_len = (int) SDL_strlen(title); + + /* If the title is longer than 2GB, it might be exploitable. */ + if (title_len < 0) { + title_len = 0; + } + + int title_wlen = MultiByteToWideChar(CP_UTF8, 0, title, -1, NULL, 0); + + if (title_wlen < 0) { + title_wlen = 0; + } + + title_w = (wchar_t *)SDL_malloc(title_wlen * sizeof(wchar_t)); + + if (!title_w) { + SDL_free(filter_wchar); + SDL_free(filebuffer); + callback(userdata, NULL, -1); + return; + } + + MultiByteToWideChar(CP_UTF8, 0, title, -1, title_w, title_wlen); + } + OPENFILENAMEW dialog; dialog.lStructSize = sizeof(OPENFILENAME); dialog.hwndOwner = window; @@ -197,7 +251,7 @@ void windows_ShowFileDialog(void *ptr) dialog.nMaxFile = SELECTLIST_SIZE; dialog.lpstrFileTitle = NULL; dialog.lpstrInitialDir = *initfolder ? initfolder : NULL; - dialog.lpstrTitle = NULL; + dialog.lpstrTitle = title_w; dialog.Flags = flags | OFN_EXPLORER | OFN_HIDEREADONLY | OFN_NOCHANGEDIR; dialog.nFileOffset = 0; dialog.nFileExtension = 0; @@ -211,6 +265,7 @@ void windows_ShowFileDialog(void *ptr) BOOL result = pGetAnyFileName(&dialog); SDL_free(filter_wchar); + SDL_free(title_w); if (result) { if (!(flags & OFN_ALLOWMULTISELECT)) { @@ -388,25 +443,54 @@ void windows_ShowFolderDialog(void* ptr) SDL_DialogFileCallback callback = args->callback; void *userdata = args->userdata; HWND parent = NULL; + const char *title = args->title; if (window) { parent = (HWND) SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_WIN32_HWND_POINTER, NULL); } + wchar_t *title_w = NULL; + + if (title) { + int title_len = (int) SDL_strlen(title); + + /* If the title is longer than 2GB, it might be exploitable. */ + if (title_len < 0) { + title_len = 0; + } + + int title_wlen = MultiByteToWideChar(CP_UTF8, 0, title, title_len, NULL, 0); + + if (title_wlen < 0) { + title_wlen = 0; + } + + title_w = (wchar_t *)SDL_malloc(title_wlen * sizeof(wchar_t)); + + if (!title_w) { + callback(userdata, NULL, -1); + return; + } + + MultiByteToWideChar(CP_UTF8, 0, title, title_len, title_w, title_wlen); + } + wchar_t buffer[MAX_PATH]; BROWSEINFOW dialog; dialog.hwndOwner = parent; dialog.pidlRoot = NULL; - // Windows docs say this is `LPTSTR` - apparently it's actually `LPWSTR` dialog.pszDisplayName = buffer; - dialog.lpszTitle = NULL; + dialog.lpszTitle = title_w; dialog.ulFlags = BIF_USENEWUI; dialog.lpfn = browse_callback_proc; dialog.lParam = (LPARAM)args->default_folder; dialog.iImage = 0; LPITEMIDLIST lpItem = SHBrowseForFolderW(&dialog); + + SDL_free(title_w); + if (lpItem != NULL) { SHGetPathFromIDListW(lpItem, buffer); char *chosen_file = WIN_StringToUTF8W(buffer); @@ -426,45 +510,7 @@ int windows_folder_dialog_thread(void* ptr) return 0; } -void SDL_ShowOpenFileDialog(SDL_DialogFileCallback callback, void* userdata, SDL_Window* window, const SDL_DialogFileFilter *filters, int nfilters, const char* default_location, bool allow_many) -{ - winArgs *args; - SDL_Thread *thread; - - if (SDL_GetHint(SDL_HINT_FILE_DIALOG_DRIVER) != NULL) { - SDL_Log("%s", SDL_GetHint(SDL_HINT_FILE_DIALOG_DRIVER)); - SDL_SetError("File dialog driver unsupported"); - callback(userdata, NULL, -1); - return; - } - - args = (winArgs *)SDL_malloc(sizeof(*args)); - if (args == NULL) { - callback(userdata, NULL, -1); - return; - } - - args->is_save = 0; - args->filters = filters; - args->nfilters = nfilters; - args->default_file = default_location; - args->parent = window; - args->flags = (allow_many != false) ? OFN_ALLOWMULTISELECT : 0; - args->callback = callback; - args->userdata = userdata; - - thread = SDL_CreateThread(windows_file_dialog_thread, "SDL_ShowOpenFileDialog", (void *) args); - - if (thread == NULL) { - callback(userdata, NULL, -1); - SDL_free(args); - return; - } - - SDL_DetachThread(thread); -} - -void SDL_ShowSaveFileDialog(SDL_DialogFileCallback callback, void* userdata, SDL_Window* window, const SDL_DialogFileFilter *filters, int nfilters, const char* default_location) +static void ShowFileDialog(SDL_DialogFileCallback callback, void* userdata, SDL_Window* window, const SDL_DialogFileFilter *filters, int nfilters, const char* default_location, bool allow_many, bool is_save, const char* title, const char* accept, const char* cancel) { winArgs *args; SDL_Thread *thread; @@ -481,16 +527,19 @@ void SDL_ShowSaveFileDialog(SDL_DialogFileCallback callback, void* userdata, SDL return; } - args->is_save = 1; + args->is_save = is_save; args->filters = filters; args->nfilters = nfilters; args->default_file = default_location; args->parent = window; - args->flags = 0; + args->flags = allow_many ? OFN_ALLOWMULTISELECT : 0; args->callback = callback; args->userdata = userdata; + args->title = title; + args->accept = accept; + args->cancel = cancel; - thread = SDL_CreateThread(windows_file_dialog_thread, "SDL_ShowSaveFileDialog", (void *) args); + thread = SDL_CreateThread(windows_file_dialog_thread, "SDL_Windows_ShowFileDialog", (void *) args); if (thread == NULL) { callback(userdata, NULL, -1); @@ -501,7 +550,7 @@ void SDL_ShowSaveFileDialog(SDL_DialogFileCallback callback, void* userdata, SDL SDL_DetachThread(thread); } -void SDL_ShowOpenFolderDialog(SDL_DialogFileCallback callback, void* userdata, SDL_Window* window, const char* default_location, bool allow_many) +void ShowFolderDialog(SDL_DialogFileCallback callback, void* userdata, SDL_Window* window, const char* default_location, bool allow_many, const char* title, const char* accept, const char* cancel) { winFArgs *args; SDL_Thread *thread; @@ -522,8 +571,11 @@ void SDL_ShowOpenFolderDialog(SDL_DialogFileCallback callback, void* userdata, S args->callback = callback; args->default_folder = default_location; args->userdata = userdata; + args->title = title; + args->accept = accept; + args->cancel = cancel; - thread = SDL_CreateThread(windows_folder_dialog_thread, "SDL_ShowOpenFolderDialog", (void *) args); + thread = SDL_CreateThread(windows_folder_dialog_thread, "SDL_Windows_ShowFolderDialog", (void *) args); if (thread == NULL) { callback(userdata, NULL, -1); @@ -533,3 +585,31 @@ void SDL_ShowOpenFolderDialog(SDL_DialogFileCallback callback, void* userdata, S SDL_DetachThread(thread); } + +void SDL_SYS_ShowFileDialogWithProperties(SDL_FileDialogType type, SDL_DialogFileCallback callback, void *userdata, SDL_PropertiesID props) +{ + /* The internal functions will start threads, and the properties may be freed as soon as this function returns. + Save a copy of what we need before invoking the functions and starting the threads. */ + SDL_Window* window = SDL_GetPointerProperty(props, SDL_PROP_FILE_DIALOG_WINDOW_POINTER, NULL); + SDL_DialogFileFilter *filters = SDL_GetPointerProperty(props, SDL_PROP_FILE_DIALOG_FILTERS_POINTER, NULL); + int nfilters = (int) SDL_GetNumberProperty(props, SDL_PROP_FILE_DIALOG_NFILTERS_NUMBER, 0); + bool allow_many = SDL_GetBooleanProperty(props, SDL_PROP_FILE_DIALOG_MANY_BOOLEAN, false); + const char* default_location = SDL_GetStringProperty(props, SDL_PROP_FILE_DIALOG_LOCATION_STRING, NULL); + const char* title = SDL_GetStringProperty(props, SDL_PROP_FILE_DIALOG_TITLE_STRING, NULL); + const char* accept = SDL_GetStringProperty(props, SDL_PROP_FILE_DIALOG_ACCEPT_STRING, NULL); + const char* cancel = SDL_GetStringProperty(props, SDL_PROP_FILE_DIALOG_CANCEL_STRING, NULL); + bool is_save = false; + + switch (type) { + case SDL_FILEDIALOG_SAVEFILE: + is_save = true; + SDL_FALLTHROUGH; + case SDL_FILEDIALOG_OPENFILE: + ShowFileDialog(callback, userdata, window, filters, nfilters, default_location, allow_many, is_save, title, accept, cancel); + break; + + case SDL_FILEDIALOG_OPENFOLDER: + ShowFolderDialog(callback, userdata, window, default_location, allow_many, title, accept, cancel); + break; + }; +} diff --git a/libs/SDL3/src/dynapi/SDL_dynapi.c b/libs/SDL3/src/dynapi/SDL_dynapi.c index 21b9bb80..fa6872e3 100644 --- a/libs/SDL3/src/dynapi/SDL_dynapi.c +++ b/libs/SDL3/src/dynapi/SDL_dynapi.c @@ -86,13 +86,10 @@ static void SDL_InitDynamicAPI(void); result = jump_table.SDL_vsnprintf(buf, sizeof(buf), fmt, ap); \ va_end(ap); \ if (result >= 0 && (size_t)result >= sizeof(buf)) { \ - size_t len = (size_t)result + 1; \ - str = (char *)jump_table.SDL_malloc(len); \ - if (str) { \ - va_start(ap, fmt); \ - result = jump_table.SDL_vsnprintf(str, len, fmt, ap); \ - va_end(ap); \ - } \ + str = NULL; \ + va_start(ap, fmt); \ + result = jump_table.SDL_vasprintf(&str, fmt, ap); \ + va_end(ap); \ } \ if (result >= 0) { \ jump_table.SDL_SetError("%s", str); \ @@ -152,6 +149,30 @@ static void SDL_InitDynamicAPI(void); va_end(ap); \ return result; \ } \ + _static bool SDLCALL SDL_RenderDebugTextFormat##name(SDL_Renderer *renderer, float x, float y, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) \ + { \ + char buf[128], *str = buf; \ + int result; \ + va_list ap; \ + initcall; \ + va_start(ap, fmt); \ + result = jump_table.SDL_vsnprintf(buf, sizeof(buf), fmt, ap); \ + va_end(ap); \ + if (result >= 0 && (size_t)result >= sizeof(buf)) { \ + str = NULL; \ + va_start(ap, fmt); \ + result = jump_table.SDL_vasprintf(&str, fmt, ap); \ + va_end(ap); \ + } \ + bool retval = false; \ + if (result >= 0) { \ + retval = jump_table.SDL_RenderDebugTextFormat(renderer, x, y, "%s", str); \ + } \ + if (str != buf) { \ + jump_table.SDL_free(str); \ + } \ + return retval; \ + } \ _static void SDLCALL SDL_Log##name(SDL_PRINTF_FORMAT_STRING const char *fmt, ...) \ { \ va_list ap; \ diff --git a/libs/SDL3/src/dynapi/SDL_dynapi.h b/libs/SDL3/src/dynapi/SDL_dynapi.h index fd2e3b06..e03882e7 100644 --- a/libs/SDL3/src/dynapi/SDL_dynapi.h +++ b/libs/SDL3/src/dynapi/SDL_dynapi.h @@ -61,8 +61,6 @@ #define SDL_DYNAMIC_API 0 // Turn off for static analysis, so reports are more clear. #elif defined(SDL_PLATFORM_VITA) #define SDL_DYNAMIC_API 0 // vitasdk doesn't support dynamic linking -#elif defined(SDL_PLATFORM_NGAGE) -#define SDL_DYNAMIC_API 0 // The N-Gage doesn't support dynamic linking either #elif defined(SDL_PLATFORM_3DS) #define SDL_DYNAMIC_API 0 // devkitARM doesn't support dynamic linking #elif defined(DYNAPI_NEEDS_DLOPEN) && !defined(HAVE_DLOPEN) diff --git a/libs/SDL3/src/dynapi/SDL_dynapi.sym b/libs/SDL3/src/dynapi/SDL_dynapi.sym index fcdb93b0..47ddb603 100644 --- a/libs/SDL3/src/dynapi/SDL_dynapi.sym +++ b/libs/SDL3/src/dynapi/SDL_dynapi.sym @@ -974,6 +974,7 @@ SDL3_0.0.0 { SDL_WaitEventTimeout; SDL_WaitForGPUFences; SDL_WaitForGPUIdle; + SDL_WaitForGPUSwapchain; SDL_WaitProcess; SDL_WaitSemaphore; SDL_WaitSemaphoreTimeout; @@ -1184,6 +1185,51 @@ SDL3_0.0.0 { SDL_RenderDebugText; SDL_GetSandbox; SDL_CancelGPUCommandBuffer; + SDL_SaveFile_IO; + SDL_SaveFile; + SDL_GetCurrentDirectory; + SDL_IsAudioDevicePhysical; + SDL_IsAudioDevicePlayback; + SDL_AsyncIOFromFile; + SDL_GetAsyncIOSize; + SDL_ReadAsyncIO; + SDL_WriteAsyncIO; + SDL_CloseAsyncIO; + SDL_CreateAsyncIOQueue; + SDL_DestroyAsyncIOQueue; + SDL_GetAsyncIOResult; + SDL_WaitAsyncIOResult; + SDL_SignalAsyncIOQueue; + SDL_LoadFileAsync; + SDL_ShowFileDialogWithProperties; + SDL_IsMainThread; + SDL_RunOnMainThread; + SDL_SetGPUAllowedFramesInFlight; + SDL_RenderTextureAffine; + SDL_WaitAndAcquireGPUSwapchainTexture; + SDL_RenderDebugTextFormat; + SDL_CreateTray; + SDL_SetTrayIcon; + SDL_SetTrayTooltip; + SDL_CreateTrayMenu; + SDL_CreateTraySubmenu; + SDL_GetTrayMenu; + SDL_GetTraySubmenu; + SDL_GetTrayEntries; + SDL_RemoveTrayEntry; + SDL_InsertTrayEntryAt; + SDL_SetTrayEntryLabel; + SDL_GetTrayEntryLabel; + SDL_SetTrayEntryChecked; + SDL_GetTrayEntryChecked; + SDL_SetTrayEntryEnabled; + SDL_GetTrayEntryEnabled; + SDL_SetTrayEntryCallback; + SDL_DestroyTray; + SDL_GetTrayEntryParent; + SDL_GetTrayMenuParentEntry; + SDL_GetTrayMenuParentTray; + SDL_GetThreadState; # extra symbols go here (don't modify this line) local: *; }; diff --git a/libs/SDL3/src/dynapi/SDL_dynapi_overrides.h b/libs/SDL3/src/dynapi/SDL_dynapi_overrides.h index c33efb30..cf47d1c9 100644 --- a/libs/SDL3/src/dynapi/SDL_dynapi_overrides.h +++ b/libs/SDL3/src/dynapi/SDL_dynapi_overrides.h @@ -999,6 +999,7 @@ #define SDL_WaitEventTimeout SDL_WaitEventTimeout_REAL #define SDL_WaitForGPUFences SDL_WaitForGPUFences_REAL #define SDL_WaitForGPUIdle SDL_WaitForGPUIdle_REAL +#define SDL_WaitForGPUSwapchain SDL_WaitForGPUSwapchain_REAL #define SDL_WaitProcess SDL_WaitProcess_REAL #define SDL_WaitSemaphore SDL_WaitSemaphore_REAL #define SDL_WaitSemaphoreTimeout SDL_WaitSemaphoreTimeout_REAL @@ -1209,3 +1210,48 @@ #define SDL_RenderDebugText SDL_RenderDebugText_REAL #define SDL_GetSandbox SDL_GetSandbox_REAL #define SDL_CancelGPUCommandBuffer SDL_CancelGPUCommandBuffer_REAL +#define SDL_SaveFile_IO SDL_SaveFile_IO_REAL +#define SDL_SaveFile SDL_SaveFile_REAL +#define SDL_GetCurrentDirectory SDL_GetCurrentDirectory_REAL +#define SDL_IsAudioDevicePhysical SDL_IsAudioDevicePhysical_REAL +#define SDL_IsAudioDevicePlayback SDL_IsAudioDevicePlayback_REAL +#define SDL_AsyncIOFromFile SDL_AsyncIOFromFile_REAL +#define SDL_GetAsyncIOSize SDL_GetAsyncIOSize_REAL +#define SDL_ReadAsyncIO SDL_ReadAsyncIO_REAL +#define SDL_WriteAsyncIO SDL_WriteAsyncIO_REAL +#define SDL_CloseAsyncIO SDL_CloseAsyncIO_REAL +#define SDL_CreateAsyncIOQueue SDL_CreateAsyncIOQueue_REAL +#define SDL_DestroyAsyncIOQueue SDL_DestroyAsyncIOQueue_REAL +#define SDL_GetAsyncIOResult SDL_GetAsyncIOResult_REAL +#define SDL_WaitAsyncIOResult SDL_WaitAsyncIOResult_REAL +#define SDL_SignalAsyncIOQueue SDL_SignalAsyncIOQueue_REAL +#define SDL_LoadFileAsync SDL_LoadFileAsync_REAL +#define SDL_ShowFileDialogWithProperties SDL_ShowFileDialogWithProperties_REAL +#define SDL_IsMainThread SDL_IsMainThread_REAL +#define SDL_RunOnMainThread SDL_RunOnMainThread_REAL +#define SDL_SetGPUAllowedFramesInFlight SDL_SetGPUAllowedFramesInFlight_REAL +#define SDL_RenderTextureAffine SDL_RenderTextureAffine_REAL +#define SDL_WaitAndAcquireGPUSwapchainTexture SDL_WaitAndAcquireGPUSwapchainTexture_REAL +#define SDL_RenderDebugTextFormat SDL_RenderDebugTextFormat_REAL +#define SDL_CreateTray SDL_CreateTray_REAL +#define SDL_SetTrayIcon SDL_SetTrayIcon_REAL +#define SDL_SetTrayTooltip SDL_SetTrayTooltip_REAL +#define SDL_CreateTrayMenu SDL_CreateTrayMenu_REAL +#define SDL_CreateTraySubmenu SDL_CreateTraySubmenu_REAL +#define SDL_GetTrayMenu SDL_GetTrayMenu_REAL +#define SDL_GetTraySubmenu SDL_GetTraySubmenu_REAL +#define SDL_GetTrayEntries SDL_GetTrayEntries_REAL +#define SDL_RemoveTrayEntry SDL_RemoveTrayEntry_REAL +#define SDL_InsertTrayEntryAt SDL_InsertTrayEntryAt_REAL +#define SDL_SetTrayEntryLabel SDL_SetTrayEntryLabel_REAL +#define SDL_GetTrayEntryLabel SDL_GetTrayEntryLabel_REAL +#define SDL_SetTrayEntryChecked SDL_SetTrayEntryChecked_REAL +#define SDL_GetTrayEntryChecked SDL_GetTrayEntryChecked_REAL +#define SDL_SetTrayEntryEnabled SDL_SetTrayEntryEnabled_REAL +#define SDL_GetTrayEntryEnabled SDL_GetTrayEntryEnabled_REAL +#define SDL_SetTrayEntryCallback SDL_SetTrayEntryCallback_REAL +#define SDL_DestroyTray SDL_DestroyTray_REAL +#define SDL_GetTrayEntryParent SDL_GetTrayEntryParent_REAL +#define SDL_GetTrayMenuParentEntry SDL_GetTrayMenuParentEntry_REAL +#define SDL_GetTrayMenuParentTray SDL_GetTrayMenuParentTray_REAL +#define SDL_GetThreadState SDL_GetThreadState_REAL diff --git a/libs/SDL3/src/dynapi/SDL_dynapi_procs.h b/libs/SDL3/src/dynapi/SDL_dynapi_procs.h index cebdf020..01f6a4de 100644 --- a/libs/SDL3/src/dynapi/SDL_dynapi_procs.h +++ b/libs/SDL3/src/dynapi/SDL_dynapi_procs.h @@ -1009,6 +1009,7 @@ SDL_DYNAPI_PROC(bool,SDL_WaitEvent,(SDL_Event *a),(a),return) SDL_DYNAPI_PROC(bool,SDL_WaitEventTimeout,(SDL_Event *a, Sint32 b),(a,b),return) SDL_DYNAPI_PROC(bool,SDL_WaitForGPUFences,(SDL_GPUDevice *a, bool b, SDL_GPUFence *const *c, Uint32 d),(a,b,c,d),return) SDL_DYNAPI_PROC(bool,SDL_WaitForGPUIdle,(SDL_GPUDevice *a),(a),return) +SDL_DYNAPI_PROC(bool,SDL_WaitForGPUSwapchain,(SDL_GPUDevice *a, SDL_Window *b),(a,b),return) SDL_DYNAPI_PROC(bool,SDL_WaitProcess,(SDL_Process *a, bool b, int *c),(a,b,c),return) SDL_DYNAPI_PROC(void,SDL_WaitSemaphore,(SDL_Semaphore *a),(a),) SDL_DYNAPI_PROC(bool,SDL_WaitSemaphoreTimeout,(SDL_Semaphore *a, Sint32 b),(a,b),return) @@ -1215,3 +1216,50 @@ SDL_DYNAPI_PROC(SDL_LogOutputFunction,SDL_GetDefaultLogOutputFunction,(void),(), SDL_DYNAPI_PROC(bool,SDL_RenderDebugText,(SDL_Renderer *a,float b,float c,const char *d),(a,b,c,d),return) SDL_DYNAPI_PROC(SDL_Sandbox,SDL_GetSandbox,(void),(),return) SDL_DYNAPI_PROC(bool,SDL_CancelGPUCommandBuffer,(SDL_GPUCommandBuffer *a),(a),return) +SDL_DYNAPI_PROC(bool,SDL_SaveFile_IO,(SDL_IOStream *a,const void *b,size_t c,bool d),(a,b,c,d),return) +SDL_DYNAPI_PROC(bool,SDL_SaveFile,(const char *a,const void *b,size_t c),(a,b,c),return) +SDL_DYNAPI_PROC(char*,SDL_GetCurrentDirectory,(void),(),return) +SDL_DYNAPI_PROC(bool,SDL_IsAudioDevicePhysical,(SDL_AudioDeviceID a),(a),return) +SDL_DYNAPI_PROC(bool,SDL_IsAudioDevicePlayback,(SDL_AudioDeviceID a),(a),return) +SDL_DYNAPI_PROC(SDL_AsyncIO*,SDL_AsyncIOFromFile,(const char *a, const char *b),(a,b),return) +SDL_DYNAPI_PROC(Sint64,SDL_GetAsyncIOSize,(SDL_AsyncIO *a),(a),return) +SDL_DYNAPI_PROC(bool,SDL_ReadAsyncIO,(SDL_AsyncIO *a, void *b, Uint64 c, Uint64 d, SDL_AsyncIOQueue *e, void *f),(a,b,c,d,e,f),return) +SDL_DYNAPI_PROC(bool,SDL_WriteAsyncIO,(SDL_AsyncIO *a, void *b, Uint64 c, Uint64 d, SDL_AsyncIOQueue *e, void *f),(a,b,c,d,e,f),return) +SDL_DYNAPI_PROC(bool,SDL_CloseAsyncIO,(SDL_AsyncIO *a, bool b, SDL_AsyncIOQueue *c, void *d),(a,b,c,d),return) +SDL_DYNAPI_PROC(SDL_AsyncIOQueue*,SDL_CreateAsyncIOQueue,(void),(),return) +SDL_DYNAPI_PROC(void,SDL_DestroyAsyncIOQueue,(SDL_AsyncIOQueue *a),(a),) +SDL_DYNAPI_PROC(bool,SDL_GetAsyncIOResult,(SDL_AsyncIOQueue *a, SDL_AsyncIOOutcome *b),(a,b),return) +SDL_DYNAPI_PROC(bool,SDL_WaitAsyncIOResult,(SDL_AsyncIOQueue *a, SDL_AsyncIOOutcome *b, Sint32 c),(a,b,c),return) +SDL_DYNAPI_PROC(void,SDL_SignalAsyncIOQueue,(SDL_AsyncIOQueue *a),(a),) +SDL_DYNAPI_PROC(bool,SDL_LoadFileAsync,(const char *a, SDL_AsyncIOQueue *b, void *c),(a,b,c),return) +SDL_DYNAPI_PROC(void,SDL_ShowFileDialogWithProperties,(SDL_FileDialogType a, SDL_DialogFileCallback b, void *c, SDL_PropertiesID d),(a,b,c,d),) +SDL_DYNAPI_PROC(bool,SDL_IsMainThread,(void),(),return) +SDL_DYNAPI_PROC(bool,SDL_RunOnMainThread,(SDL_MainThreadCallback a,void *b,bool c),(a,b,c),return) +SDL_DYNAPI_PROC(bool,SDL_SetGPUAllowedFramesInFlight,(SDL_GPUDevice *a,Uint32 b),(a,b),return) +SDL_DYNAPI_PROC(bool,SDL_RenderTextureAffine,(SDL_Renderer *a,SDL_Texture *b,const SDL_FRect *c,const SDL_FPoint *d,const SDL_FPoint *e,const SDL_FPoint *f),(a,b,c,d,e,f),return) +SDL_DYNAPI_PROC(bool,SDL_WaitAndAcquireGPUSwapchainTexture,(SDL_GPUCommandBuffer *a,SDL_Window *b,SDL_GPUTexture **c,Uint32 *d,Uint32 *e),(a,b,c,d,e),return) +#ifndef SDL_DYNAPI_PROC_NO_VARARGS +SDL_DYNAPI_PROC(bool,SDL_RenderDebugTextFormat,(SDL_Renderer *a,float b,float c,SDL_PRINTF_FORMAT_STRING const char *d,...),(a,b,c,d),return) +#endif +SDL_DYNAPI_PROC(SDL_Tray*,SDL_CreateTray,(SDL_Surface *a,const char *b),(a,b),return) +SDL_DYNAPI_PROC(void,SDL_SetTrayIcon,(SDL_Tray *a,SDL_Surface *b),(a,b),) +SDL_DYNAPI_PROC(void,SDL_SetTrayTooltip,(SDL_Tray *a,const char *b),(a,b),) +SDL_DYNAPI_PROC(SDL_TrayMenu*,SDL_CreateTrayMenu,(SDL_Tray *a),(a),return) +SDL_DYNAPI_PROC(SDL_TrayMenu*,SDL_CreateTraySubmenu,(SDL_TrayEntry *a),(a),return) +SDL_DYNAPI_PROC(SDL_TrayMenu*,SDL_GetTrayMenu,(SDL_Tray *a),(a),return) +SDL_DYNAPI_PROC(SDL_TrayMenu*,SDL_GetTraySubmenu,(SDL_TrayEntry *a),(a),return) +SDL_DYNAPI_PROC(const SDL_TrayEntry**,SDL_GetTrayEntries,(SDL_TrayMenu *a,int *b),(a,b),return) +SDL_DYNAPI_PROC(void,SDL_RemoveTrayEntry,(SDL_TrayEntry *a),(a),) +SDL_DYNAPI_PROC(SDL_TrayEntry*,SDL_InsertTrayEntryAt,(SDL_TrayMenu *a,int b,const char *c,SDL_TrayEntryFlags d),(a,b,c,d),return) +SDL_DYNAPI_PROC(void,SDL_SetTrayEntryLabel,(SDL_TrayEntry *a,const char *b),(a,b),) +SDL_DYNAPI_PROC(const char*,SDL_GetTrayEntryLabel,(SDL_TrayEntry *a),(a),return) +SDL_DYNAPI_PROC(void,SDL_SetTrayEntryChecked,(SDL_TrayEntry *a,bool b),(a,b),) +SDL_DYNAPI_PROC(bool,SDL_GetTrayEntryChecked,(SDL_TrayEntry *a),(a),return) +SDL_DYNAPI_PROC(void,SDL_SetTrayEntryEnabled,(SDL_TrayEntry *a,bool b),(a,b),) +SDL_DYNAPI_PROC(bool,SDL_GetTrayEntryEnabled,(SDL_TrayEntry *a),(a),return) +SDL_DYNAPI_PROC(void,SDL_SetTrayEntryCallback,(SDL_TrayEntry *a,SDL_TrayCallback b,void *c),(a,b,c),) +SDL_DYNAPI_PROC(void,SDL_DestroyTray,(SDL_Tray *a),(a),) +SDL_DYNAPI_PROC(SDL_TrayMenu*,SDL_GetTrayEntryParent,(SDL_TrayEntry *a),(a),return) +SDL_DYNAPI_PROC(SDL_TrayEntry*,SDL_GetTrayMenuParentEntry,(SDL_TrayMenu *a),(a),return) +SDL_DYNAPI_PROC(SDL_Tray*,SDL_GetTrayMenuParentTray,(SDL_TrayMenu *a),(a),return) +SDL_DYNAPI_PROC(SDL_ThreadState,SDL_GetThreadState,(SDL_Thread *a),(a),return) diff --git a/libs/SDL3/src/dynapi/gendynapi.py b/libs/SDL3/src/dynapi/gendynapi.py index dae72d24..c780dac6 100644 --- a/libs/SDL3/src/dynapi/gendynapi.py +++ b/libs/SDL3/src/dynapi/gendynapi.py @@ -168,9 +168,11 @@ def parse_header(header_path: Path) -> list[SdlProcedure]: func = func.replace(" SDL_PRINTF_VARARG_FUNC(1)", "") func = func.replace(" SDL_PRINTF_VARARG_FUNC(2)", "") func = func.replace(" SDL_PRINTF_VARARG_FUNC(3)", "") + func = func.replace(" SDL_PRINTF_VARARG_FUNC(4)", "") func = func.replace(" SDL_PRINTF_VARARG_FUNCV(1)", "") func = func.replace(" SDL_PRINTF_VARARG_FUNCV(2)", "") func = func.replace(" SDL_PRINTF_VARARG_FUNCV(3)", "") + func = func.replace(" SDL_PRINTF_VARARG_FUNCV(4)", "") func = func.replace(" SDL_WPRINTF_VARARG_FUNC(3)", "") func = func.replace(" SDL_WPRINTF_VARARG_FUNCV(3)", "") func = func.replace(" SDL_SCANF_VARARG_FUNC(2)", "") @@ -444,6 +446,9 @@ def get_header_list() -> list[Path]: else: logger.debug("Skip %s", f) + # Order headers for reproducible behavior + ret.sort() + return ret # Write the new API in files: _procs.h _overrivides.h and .sym diff --git a/libs/SDL3/src/events/SDL_categories.c b/libs/SDL3/src/events/SDL_categories.c index 57fb8c49..5f085347 100644 --- a/libs/SDL3/src/events/SDL_categories.c +++ b/libs/SDL3/src/events/SDL_categories.c @@ -50,9 +50,12 @@ SDL_EventCategory SDL_GetEventCategory(Uint32 type) case SDL_EVENT_DID_ENTER_FOREGROUND: case SDL_EVENT_LOCALE_CHANGED: case SDL_EVENT_SYSTEM_THEME_CHANGED: + return SDL_EVENTCATEGORY_SYSTEM; + case SDL_EVENT_RENDER_TARGETS_RESET: case SDL_EVENT_RENDER_DEVICE_RESET: - return SDL_EVENTCATEGORY_SYSTEM; + case SDL_EVENT_RENDER_DEVICE_LOST: + return SDL_EVENTCATEGORY_RENDER; case SDL_EVENT_QUIT: return SDL_EVENTCATEGORY_QUIT; @@ -233,6 +236,9 @@ SDL_Window *SDL_GetWindowFromEvent(const SDL_Event *event) case SDL_EVENTCATEGORY_DROP: windowID = event->drop.windowID; break; + case SDL_EVENTCATEGORY_RENDER: + windowID = event->render.windowID; + break; default: // < 0 -> invalid event type (error is set by SDL_GetEventCategory) // else -> event has no associated window (not an error) diff --git a/libs/SDL3/src/events/SDL_categories_c.h b/libs/SDL3/src/events/SDL_categories_c.h index 5f82c00c..1f72c2aa 100644 --- a/libs/SDL3/src/events/SDL_categories_c.h +++ b/libs/SDL3/src/events/SDL_categories_c.h @@ -62,6 +62,7 @@ typedef enum SDL_EventCategory SDL_EVENTCATEGORY_PAXIS, SDL_EVENTCATEGORY_DROP, SDL_EVENTCATEGORY_CLIPBOARD, + SDL_EVENTCATEGORY_RENDER, } SDL_EventCategory; extern SDL_EventCategory SDL_GetEventCategory(Uint32 type); diff --git a/libs/SDL3/src/events/SDL_events.c b/libs/SDL3/src/events/SDL_events.c index 890a78ee..bc04ee91 100644 --- a/libs/SDL3/src/events/SDL_events.c +++ b/libs/SDL3/src/events/SDL_events.c @@ -450,10 +450,16 @@ static void SDL_LogEvent(const SDL_Event *event) break; SDL_EVENT_CASE(SDL_EVENT_CLIPBOARD_UPDATE) break; - SDL_EVENT_CASE(SDL_EVENT_RENDER_TARGETS_RESET) - break; - SDL_EVENT_CASE(SDL_EVENT_RENDER_DEVICE_RESET) - break; + +#define SDL_RENDEREVENT_CASE(x) \ + case x: \ + SDL_strlcpy(name, #x, sizeof(name)); \ + (void)SDL_snprintf(details, sizeof(details), " (timestamp=%u event=%s windowid=%u)", \ + (uint)event->display.timestamp, name, (uint)event->render.windowID); \ + break + SDL_RENDEREVENT_CASE(SDL_EVENT_RENDER_TARGETS_RESET); + SDL_RENDEREVENT_CASE(SDL_EVENT_RENDER_DEVICE_RESET); + SDL_RENDEREVENT_CASE(SDL_EVENT_RENDER_DEVICE_LOST); #define SDL_DISPLAYEVENT_CASE(x) \ case x: \ @@ -565,7 +571,7 @@ static void SDL_LogEvent(const SDL_Event *event) (void)SDL_snprintf(details, sizeof(details), " (timestamp=%u windowid=%u which=%u button=%u state=%s clicks=%u x=%g y=%g)", \ (uint)event->button.timestamp, (uint)event->button.windowID, \ (uint)event->button.which, (uint)event->button.button, \ - event->button.down ? "pressed" : "released", \ + event->button.down ? "pressed" : "released", \ (uint)event->button.clicks, event->button.x, event->button.y) SDL_EVENT_CASE(SDL_EVENT_MOUSE_BUTTON_DOWN) PRINT_MBUTTON_EVENT(event); @@ -730,7 +736,7 @@ static void SDL_LogEvent(const SDL_Event *event) SDL_EVENT_CASE(SDL_EVENT_PEN_AXIS) (void)SDL_snprintf(details, sizeof(details), " (timestamp=%u windowid=%u which=%u pen_state=%u x=%g y=%g axis=%s value=%g)", (uint)event->paxis.timestamp, (uint)event->paxis.windowID, (uint)event->paxis.which, (uint)event->paxis.pen_state, event->paxis.x, event->paxis.y, - ((event->paxis.axis >= 0) && (event->paxis.axis < SDL_arraysize(pen_axisnames))) ? pen_axisnames[event->paxis.axis] : "[UNKNOWN]", event->paxis.value); + ((((int) event->paxis.axis) >= 0) && (event->paxis.axis < SDL_arraysize(pen_axisnames))) ? pen_axisnames[event->paxis.axis] : "[UNKNOWN]", event->paxis.value); break; SDL_EVENT_CASE(SDL_EVENT_PEN_MOTION) @@ -879,12 +885,17 @@ void SDL_StopEventLoop(void) } SDL_zero(SDL_EventOK); - SDL_UnlockMutex(SDL_EventQ.lock); - + SDL_Mutex *lock = NULL; if (SDL_EventQ.lock) { - SDL_DestroyMutex(SDL_EventQ.lock); + lock = SDL_EventQ.lock; SDL_EventQ.lock = NULL; } + + SDL_UnlockMutex(lock); + + if (lock) { + SDL_DestroyMutex(lock); + } } // This function (and associated calls) may be called more than once @@ -1169,6 +1180,177 @@ void SDL_FlushEvents(Uint32 minType, Uint32 maxType) SDL_UnlockMutex(SDL_EventQ.lock); } +typedef enum +{ + SDL_MAIN_CALLBACK_WAITING, + SDL_MAIN_CALLBACK_COMPLETE, + SDL_MAIN_CALLBACK_CANCELED, +} SDL_MainThreadCallbackState; + +typedef struct SDL_MainThreadCallbackEntry +{ + SDL_MainThreadCallback callback; + void *userdata; + SDL_AtomicInt state; + SDL_Semaphore *semaphore; + struct SDL_MainThreadCallbackEntry *next; +} SDL_MainThreadCallbackEntry; + +static SDL_Mutex *SDL_main_callbacks_lock; +static SDL_MainThreadCallbackEntry *SDL_main_callbacks_head; +static SDL_MainThreadCallbackEntry *SDL_main_callbacks_tail; + +static SDL_MainThreadCallbackEntry *SDL_CreateMainThreadCallback(SDL_MainThreadCallback callback, void *userdata, bool wait_complete) +{ + SDL_MainThreadCallbackEntry *entry = (SDL_MainThreadCallbackEntry *)SDL_malloc(sizeof(*entry)); + if (!entry) { + return NULL; + } + + entry->callback = callback; + entry->userdata = userdata; + SDL_SetAtomicInt(&entry->state, SDL_MAIN_CALLBACK_WAITING); + if (wait_complete) { + entry->semaphore = SDL_CreateSemaphore(0); + if (!entry->semaphore) { + SDL_free(entry); + return NULL; + } + } else { + entry->semaphore = NULL; + } + entry->next = NULL; + + return entry; +} + +static void SDL_DestroyMainThreadCallback(SDL_MainThreadCallbackEntry *entry) +{ + if (entry->semaphore) { + SDL_DestroySemaphore(entry->semaphore); + } + SDL_free(entry); +} + +static void SDL_InitMainThreadCallbacks(void) +{ + SDL_main_callbacks_lock = SDL_CreateMutex(); + SDL_assert(SDL_main_callbacks_head == NULL && + SDL_main_callbacks_tail == NULL); +} + +static void SDL_QuitMainThreadCallbacks(void) +{ + SDL_MainThreadCallbackEntry *entry; + + SDL_LockMutex(SDL_main_callbacks_lock); + { + entry = SDL_main_callbacks_head; + SDL_main_callbacks_head = NULL; + SDL_main_callbacks_tail = NULL; + } + SDL_UnlockMutex(SDL_main_callbacks_lock); + + while (entry) { + SDL_MainThreadCallbackEntry *next = entry->next; + + if (entry->semaphore) { + // Let the waiting thread know this is canceled + SDL_SetAtomicInt(&entry->state, SDL_MAIN_CALLBACK_CANCELED); + SDL_SignalSemaphore(entry->semaphore); + } else { + // Nobody's waiting for this, clean it up + SDL_DestroyMainThreadCallback(entry); + } + entry = next; + } + + SDL_DestroyMutex(SDL_main_callbacks_lock); + SDL_main_callbacks_lock = NULL; +} + +static void SDL_RunMainThreadCallbacks(void) +{ + SDL_MainThreadCallbackEntry *entry; + + SDL_LockMutex(SDL_main_callbacks_lock); + { + entry = SDL_main_callbacks_head; + SDL_main_callbacks_head = NULL; + SDL_main_callbacks_tail = NULL; + } + SDL_UnlockMutex(SDL_main_callbacks_lock); + + while (entry) { + SDL_MainThreadCallbackEntry *next = entry->next; + + entry->callback(entry->userdata); + + if (entry->semaphore) { + // Let the waiting thread know this is done + SDL_SetAtomicInt(&entry->state, SDL_MAIN_CALLBACK_COMPLETE); + SDL_SignalSemaphore(entry->semaphore); + } else { + // Nobody's waiting for this, clean it up + SDL_DestroyMainThreadCallback(entry); + } + entry = next; + } +} + +bool SDL_RunOnMainThread(SDL_MainThreadCallback callback, void *userdata, bool wait_complete) +{ + if (SDL_IsMainThread() || !SDL_WasInit(SDL_INIT_EVENTS)) { + // No need to queue the callback + callback(userdata); + return true; + } + + SDL_MainThreadCallbackEntry *entry = SDL_CreateMainThreadCallback(callback, userdata, wait_complete); + if (!entry) { + return false; + } + + SDL_LockMutex(SDL_main_callbacks_lock); + { + if (SDL_main_callbacks_tail) { + SDL_main_callbacks_tail->next = entry; + SDL_main_callbacks_tail = entry; + } else { + SDL_main_callbacks_head = entry; + SDL_main_callbacks_tail = entry; + } + } + SDL_UnlockMutex(SDL_main_callbacks_lock); + + if (!wait_complete) { + // Queued for execution, wait not requested + return true; + } + + // Maximum wait of 30 seconds to prevent deadlocking forever + const Sint32 MAX_CALLBACK_WAIT = 30 * 1000; + SDL_WaitSemaphoreTimeout(entry->semaphore, MAX_CALLBACK_WAIT); + + switch (SDL_GetAtomicInt(&entry->state)) { + case SDL_MAIN_CALLBACK_COMPLETE: + // Execution complete! + SDL_DestroyMainThreadCallback(entry); + return true; + + case SDL_MAIN_CALLBACK_CANCELED: + // The callback was canceled on the main thread + SDL_DestroyMainThreadCallback(entry); + return SDL_SetError("Callback canceled"); + + default: + // Probably hit a deadlock in the callback + // We can't destroy the entry as the semaphore will be signaled + // if it ever comes back, just leak it here. + return SDL_SetError("Callback timed out"); + } +} + // Run the system dependent event loops static void SDL_PumpEventsInternal(bool push_sentinel) { @@ -1178,6 +1360,9 @@ static void SDL_PumpEventsInternal(bool push_sentinel) // Release any keys held down from last frame SDL_ReleaseAutoReleaseKeys(); + // Run any pending main thread callbacks + SDL_RunMainThreadCallbacks(); + #ifdef SDL_PLATFORM_ANDROID // Android event processing is independent of the video subsystem Android_PumpEvents(0); @@ -1786,6 +1971,7 @@ bool SDL_InitEvents(void) #endif SDL_AddHintCallback(SDL_HINT_EVENT_LOGGING, SDL_EventLoggingChanged, NULL); SDL_AddHintCallback(SDL_HINT_POLL_SENTINEL, SDL_PollSentinelChanged, NULL); + SDL_InitMainThreadCallbacks(); if (!SDL_StartEventLoop()) { SDL_RemoveHintCallback(SDL_HINT_EVENT_LOGGING, SDL_EventLoggingChanged, NULL); return false; @@ -1800,6 +1986,7 @@ void SDL_QuitEvents(void) { SDL_QuitQuit(); SDL_StopEventLoop(); + SDL_QuitMainThreadCallbacks(); SDL_RemoveHintCallback(SDL_HINT_POLL_SENTINEL, SDL_PollSentinelChanged, NULL); SDL_RemoveHintCallback(SDL_HINT_EVENT_LOGGING, SDL_EventLoggingChanged, NULL); #ifndef SDL_JOYSTICK_DISABLED diff --git a/libs/SDL3/src/events/SDL_keyboard.c b/libs/SDL3/src/events/SDL_keyboard.c index 3a96d5ab..a7f5c084 100644 --- a/libs/SDL3/src/events/SDL_keyboard.c +++ b/libs/SDL3/src/events/SDL_keyboard.c @@ -322,6 +322,7 @@ bool SDL_SetKeyboardFocus(SDL_Window *window) { SDL_VideoDevice *video = SDL_GetVideoDevice(); SDL_Keyboard *keyboard = &SDL_keyboard; + SDL_Mouse *mouse = SDL_GetMouse(); if (window) { if (!SDL_ObjectValid(window, SDL_OBJECT_TYPE_WINDOW) || window->is_destroying) { @@ -332,6 +333,19 @@ bool SDL_SetKeyboardFocus(SDL_Window *window) if (keyboard->focus && !window) { // We won't get anymore keyboard messages, so reset keyboard state SDL_ResetKeyboard(); + + // Also leave mouse relative mode + if (mouse->relative_mode) { + SDL_SetRelativeMouseMode(false); + + SDL_Window *focus = keyboard->focus; + if ((focus->flags & SDL_WINDOW_MINIMIZED) != 0) { + // We can't warp the mouse within minimized windows, so manually restore the position + float x = focus->x + mouse->x; + float y = focus->y + mouse->y; + SDL_WarpMouseGlobal(x, y); + } + } } // See if the current window has lost focus diff --git a/libs/SDL3/src/events/SDL_keymap.c b/libs/SDL3/src/events/SDL_keymap.c index 6bd69c50..d48b0378 100644 --- a/libs/SDL3/src/events/SDL_keymap.c +++ b/libs/SDL3/src/events/SDL_keymap.c @@ -39,8 +39,8 @@ SDL_Keymap *SDL_CreateKeymap(void) return NULL; } - keymap->scancode_to_keycode = SDL_CreateHashTable(NULL, 64, SDL_HashID, SDL_KeyMatchID, NULL, false); - keymap->keycode_to_scancode = SDL_CreateHashTable(NULL, 64, SDL_HashID, SDL_KeyMatchID, NULL, false); + keymap->scancode_to_keycode = SDL_CreateHashTable(NULL, 64, SDL_HashID, SDL_KeyMatchID, NULL, false, false); + keymap->keycode_to_scancode = SDL_CreateHashTable(NULL, 64, SDL_HashID, SDL_KeyMatchID, NULL, false, false); if (!keymap->scancode_to_keycode || !keymap->keycode_to_scancode) { SDL_DestroyKeymap(keymap); return NULL; diff --git a/libs/SDL3/src/events/SDL_mouse.c b/libs/SDL3/src/events/SDL_mouse.c index 1d9fc8a4..5cad582e 100644 --- a/libs/SDL3/src/events/SDL_mouse.c +++ b/libs/SDL3/src/events/SDL_mouse.c @@ -65,17 +65,6 @@ static void SDLCALL SDL_MouseDoubleClickTimeChanged(void *userdata, const char * } } -static void SDLCALL SDL_MouseRelativeClipIntervalChanged(void *userdata, const char *name, const char *oldValue, const char *hint) -{ - SDL_Mouse *mouse = (SDL_Mouse *)userdata; - - if (hint && *hint) { - mouse->relative_mode_clip_interval = SDL_atoi(hint); - } else { - mouse->relative_mode_clip_interval = 3000; - } -} - static void SDLCALL SDL_MouseDoubleClickRadiusChanged(void *userdata, const char *name, const char *oldValue, const char *hint) { SDL_Mouse *mouse = (SDL_Mouse *)userdata; @@ -113,6 +102,13 @@ static void SDLCALL SDL_MouseRelativeSpeedScaleChanged(void *userdata, const cha } } +static void SDLCALL SDL_MouseRelativeModeCenterChanged(void *userdata, const char *name, const char *oldValue, const char *hint) +{ + SDL_Mouse *mouse = (SDL_Mouse *)userdata; + + mouse->relative_mode_center = SDL_GetStringBoolean(hint, true); +} + static void SDLCALL SDL_MouseRelativeSystemScaleChanged(void *userdata, const char *name, const char *oldValue, const char *hint) { SDL_Mouse *mouse = (SDL_Mouse *)userdata; @@ -200,7 +196,7 @@ static void SDLCALL SDL_MouseRelativeCursorVisibleChanged(void *userdata, const SDL_Mouse *mouse = (SDL_Mouse *)userdata; mouse->relative_mode_cursor_visible = SDL_GetStringBoolean(hint, false); - + SDL_SetCursor(NULL); // Update cursor visibility } @@ -226,6 +222,9 @@ bool SDL_PreInitMouse(void) SDL_AddHintCallback(SDL_HINT_MOUSE_RELATIVE_SYSTEM_SCALE, SDL_MouseRelativeSystemScaleChanged, mouse); + SDL_AddHintCallback(SDL_HINT_MOUSE_RELATIVE_MODE_CENTER, + SDL_MouseRelativeModeCenterChanged, mouse); + SDL_AddHintCallback(SDL_HINT_MOUSE_EMULATE_WARP_WITH_RELATIVE, SDL_MouseWarpEmulationChanged, mouse); @@ -249,9 +248,6 @@ bool SDL_PreInitMouse(void) SDL_AddHintCallback(SDL_HINT_MOUSE_RELATIVE_CURSOR_VISIBLE, SDL_MouseRelativeCursorVisibleChanged, mouse); - SDL_AddHintCallback(SDL_HINT_MOUSE_RELATIVE_CLIP_INTERVAL, - SDL_MouseRelativeClipIntervalChanged, mouse); - mouse->was_touch_mouse_events = false; // no touch to mouse movement event pending mouse->cursor_shown = true; @@ -341,6 +337,7 @@ void SDL_RemoveMouse(SDL_MouseID mouseID, bool send_event) for (int i = 0; i < mouse->num_sources; ++i) { SDL_MouseInputSource *source = &mouse->sources[i]; if (source->mouseID == mouseID) { + SDL_free(source->clickstate); if (i != mouse->num_sources - 1) { SDL_memcpy(&mouse->sources[i], &mouse->sources[i + 1], (mouse->num_sources - i - 1) * sizeof(mouse->sources[i])); } @@ -440,6 +437,19 @@ void SDL_SetDefaultCursor(SDL_Cursor *cursor) } } +SDL_SystemCursor SDL_GetDefaultSystemCursor(void) +{ + SDL_SystemCursor id = SDL_SYSTEM_CURSOR_DEFAULT; + const char *value = SDL_GetHint(SDL_HINT_MOUSE_DEFAULT_SYSTEM_CURSOR); + if (value) { + int index = SDL_atoi(value); + if (0 <= index && index < (int)SDL_SYSTEM_CURSOR_COUNT) { + id = (SDL_SystemCursor)index; + } + } + return id; +} + SDL_Mouse *SDL_GetMouse(void) { return &SDL_mouse; @@ -588,111 +598,6 @@ void SDL_SendMouseMotion(Uint64 timestamp, SDL_Window *window, SDL_MouseID mouse SDL_PrivateSendMouseMotion(timestamp, window, mouseID, relative, x, y); } -static float CalculateSystemScale(SDL_Mouse *mouse, SDL_Window *window, const float *x, const float *y) -{ - int i; - int n = mouse->num_system_scale_values; - float *v = mouse->system_scale_values; - float speed, coef, scale; - - // If we're using a single scale value, return that - if (n == 1) { - scale = v[0]; - } else { - speed = SDL_sqrtf((*x * *x) + (*y * *y)); - for (i = 0; i < (n - 2); i += 2) { - if (speed < v[i + 2]) { - break; - } - } - if (i == (n - 2)) { - scale = v[n - 1]; - } else if (speed <= v[i]) { - scale = v[i + 1]; - } else { - coef = (speed - v[i]) / (v[i + 2] - v[i]); - scale = v[i + 1] + (coef * (v[i + 3] - v[i + 1])); - } - } -#ifdef SDL_PLATFORM_WIN32 - { - // On Windows the mouse speed is affected by the content scale - SDL_VideoDisplay *display; - - if (window) { - display = SDL_GetVideoDisplayForWindow(window); - } else { - display = SDL_GetVideoDisplay(SDL_GetPrimaryDisplay()); - } - if (display) { - scale *= display->content_scale; - } - } -#endif - return scale; -} - -// You can set either a single scale, or a set of {speed, scale} values in ascending order -bool SDL_SetMouseSystemScale(int num_values, const float *values) -{ - SDL_Mouse *mouse = SDL_GetMouse(); - float *v; - - if (num_values == mouse->num_system_scale_values && - SDL_memcmp(values, mouse->system_scale_values, num_values * sizeof(*values)) == 0) { - // Nothing has changed - return true; - } - - if (num_values < 1) { - return SDL_SetError("You must have at least one scale value"); - } - - if (num_values > 1) { - // Validate the values - int i; - - if (num_values < 4 || (num_values % 2) != 0) { - return SDL_SetError("You must pass a set of {speed, scale} values"); - } - - for (i = 0; i < (num_values - 2); i += 2) { - if (values[i] >= values[i + 2]) { - return SDL_SetError("Speed values must be in ascending order"); - } - } - } - - v = (float *)SDL_realloc(mouse->system_scale_values, num_values * sizeof(*values)); - if (!v) { - return false; - } - SDL_memcpy(v, values, num_values * sizeof(*values)); - - mouse->num_system_scale_values = num_values; - mouse->system_scale_values = v; - return true; -} - -static void GetScaledMouseDeltas(SDL_Mouse *mouse, SDL_Window *window, float *x, float *y) -{ - if (mouse->relative_mode) { - if (mouse->enable_relative_speed_scale) { - *x *= mouse->relative_speed_scale; - *y *= mouse->relative_speed_scale; - } else if (mouse->enable_relative_system_scale && mouse->num_system_scale_values > 0) { - float relative_system_scale = CalculateSystemScale(mouse, window, x, y); - *x *= relative_system_scale; - *y *= relative_system_scale; - } - } else { - if (mouse->enable_normal_speed_scale) { - *x *= mouse->normal_speed_scale; - *y *= mouse->normal_speed_scale; - } - } -} - static void ConstrainMousePosition(SDL_Mouse *mouse, SDL_Window *window, float *x, float *y) { /* make sure that the pointers find themselves inside the windows, @@ -739,6 +644,7 @@ static void SDL_PrivateSendMouseMotion(Uint64 timestamp, SDL_Window *window, SDL SDL_Mouse *mouse = SDL_GetMouse(); float xrel = 0.0f; float yrel = 0.0f; + bool window_is_relative = mouse->focus && (mouse->focus->flags & SDL_WINDOW_MOUSE_RELATIVE_MODE); if ((!mouse->relative_mode || mouse->warp_emulation_active) && mouseID != SDL_TOUCH_MOUSEID) { // We're not in relative mode, so all mouse events are global mouse events @@ -761,32 +667,22 @@ static void SDL_PrivateSendMouseMotion(Uint64 timestamp, SDL_Window *window, SDL return; } - if (mouseID != SDL_TOUCH_MOUSEID && mouse->relative_mode_warp) { - int w = 0, h = 0; - float center_x, center_y; - SDL_GetWindowSize(window, &w, &h); - center_x = (float)w / 2.0f; - center_y = (float)h / 2.0f; - if (x >= SDL_floorf(center_x) && x <= SDL_ceilf(center_x) && - y >= SDL_floorf(center_y) && y <= SDL_ceilf(center_y)) { - mouse->last_x = center_x; - mouse->last_y = center_y; - if (!mouse->relative_mode_warp_motion) { - return; - } - } else { - if (window && (window->flags & SDL_WINDOW_INPUT_FOCUS)) { - if (mouse->WarpMouse) { - mouse->WarpMouse(window, center_x, center_y); - } else { - SDL_PrivateSendMouseMotion(timestamp, window, mouseID, false, center_x, center_y); + if (relative) { + if (mouse->relative_mode) { + if (mouse->enable_relative_speed_scale) { + x *= mouse->relative_speed_scale; + y *= mouse->relative_speed_scale; + } else if (mouse->enable_relative_system_scale) { + if (mouse->ApplySystemScale) { + mouse->ApplySystemScale(mouse->system_scale_data, timestamp, window, mouseID, &x, &y); } } + } else { + if (mouse->enable_normal_speed_scale) { + x *= mouse->normal_speed_scale; + y *= mouse->normal_speed_scale; + } } - } - - if (relative) { - GetScaledMouseDeltas(mouse, window, &x, &y); xrel = x; yrel = y; x = (mouse->last_x + xrel); @@ -794,7 +690,6 @@ static void SDL_PrivateSendMouseMotion(Uint64 timestamp, SDL_Window *window, SDL ConstrainMousePosition(mouse, window, &x, &y); } else { ConstrainMousePosition(mouse, window, &x, &y); - if (mouse->has_position) { xrel = x - mouse->last_x; yrel = y - mouse->last_y; @@ -814,24 +709,30 @@ static void SDL_PrivateSendMouseMotion(Uint64 timestamp, SDL_Window *window, SDL yrel = 0.0f; } - if (mouse->has_position) { - // Update internal mouse coordinates - if (!mouse->relative_mode) { - mouse->x = x; - mouse->y = y; - } else { + // modify internal state + { + if (relative) { + mouse->x_accu += xrel; + mouse->y_accu += yrel; + } + + if (relative && mouse->has_position) { mouse->x += xrel; mouse->y += yrel; ConstrainMousePosition(mouse, window, &mouse->x, &mouse->y); + } else { + mouse->x = x; + mouse->y = y; } - } else { - mouse->x = x; - mouse->y = y; mouse->has_position = true; - } - mouse->xdelta += xrel; - mouse->ydelta += yrel; + // Use unclamped values if we're getting events outside the window + mouse->last_x = relative ? mouse->x : x; + mouse->last_y = relative ? mouse->y : y; + + mouse->click_motion_x += xrel; + mouse->click_motion_y += yrel; + } // Move the mouse cursor, if needed if (mouse->cursor_shown && !mouse->relative_mode && @@ -841,6 +742,13 @@ static void SDL_PrivateSendMouseMotion(Uint64 timestamp, SDL_Window *window, SDL // Post the event, if desired if (SDL_EventEnabled(SDL_EVENT_MOUSE_MOTION)) { + if (!relative && window_is_relative) { + if (!mouse->relative_mode_warp_motion) { + return; + } + xrel = 0.0f; + yrel = 0.0f; + } SDL_Event event; event.type = SDL_EVENT_MOUSE_MOTION; event.common.timestamp = timestamp; @@ -855,14 +763,6 @@ static void SDL_PrivateSendMouseMotion(Uint64 timestamp, SDL_Window *window, SDL event.motion.yrel = yrel; SDL_PushEvent(&event); } - if (relative) { - mouse->last_x = mouse->x; - mouse->last_y = mouse->y; - } else { - // Use unclamped values if we're getting events outside the window - mouse->last_x = x; - mouse->last_y = y; - } } static SDL_MouseInputSource *GetMouseInputSource(SDL_Mouse *mouse, SDL_MouseID mouseID, bool down, Uint8 button) @@ -899,29 +799,29 @@ static SDL_MouseInputSource *GetMouseInputSource(SDL_Mouse *mouse, SDL_MouseID m mouse->sources = sources; ++mouse->num_sources; source = &sources[mouse->num_sources - 1]; + SDL_zerop(source); source->mouseID = mouseID; - source->buttonstate = 0; return source; } return NULL; } -static SDL_MouseClickState *GetMouseClickState(SDL_Mouse *mouse, Uint8 button) +static SDL_MouseClickState *GetMouseClickState(SDL_MouseInputSource *source, Uint8 button) { - if (button >= mouse->num_clickstates) { + if (button >= source->num_clickstates) { int i, count = button + 1; - SDL_MouseClickState *clickstate = (SDL_MouseClickState *)SDL_realloc(mouse->clickstate, count * sizeof(*mouse->clickstate)); + SDL_MouseClickState *clickstate = (SDL_MouseClickState *)SDL_realloc(source->clickstate, count * sizeof(*source->clickstate)); if (!clickstate) { return NULL; } - mouse->clickstate = clickstate; + source->clickstate = clickstate; - for (i = mouse->num_clickstates; i < count; ++i) { - SDL_zero(mouse->clickstate[i]); + for (i = source->num_clickstates; i < count; ++i) { + SDL_zero(source->clickstate[i]); } - mouse->num_clickstates = count; + source->num_clickstates = count; } - return &mouse->clickstate[button]; + return &source->clickstate[button]; } static void SDL_PrivateSendMouseButton(Uint64 timestamp, SDL_Window *window, SDL_MouseID mouseID, Uint8 button, bool down, int clicks) @@ -986,19 +886,19 @@ static void SDL_PrivateSendMouseButton(Uint64 timestamp, SDL_Window *window, SDL source->buttonstate = buttonstate; if (clicks < 0) { - SDL_MouseClickState *clickstate = GetMouseClickState(mouse, button); + SDL_MouseClickState *clickstate = GetMouseClickState(source, button); if (clickstate) { if (down) { Uint64 now = SDL_GetTicks(); if (now >= (clickstate->last_timestamp + mouse->double_click_time) || - SDL_fabs((double)mouse->x - clickstate->last_x) > mouse->double_click_radius || - SDL_fabs((double)mouse->y - clickstate->last_y) > mouse->double_click_radius) { + SDL_fabs(mouse->click_motion_x - clickstate->click_motion_x) > mouse->double_click_radius || + SDL_fabs(mouse->click_motion_y - clickstate->click_motion_y) > mouse->double_click_radius) { clickstate->click_count = 0; } clickstate->last_timestamp = now; - clickstate->last_x = mouse->x; - clickstate->last_y = mouse->y; + clickstate->click_motion_x = mouse->click_motion_x; + clickstate->click_motion_y = mouse->click_motion_y; if (clickstate->click_count < 255) { ++clickstate->click_count; } @@ -1058,6 +958,11 @@ void SDL_SendMouseWheel(Uint64 timestamp, SDL_Window *window, SDL_MouseID mouseI return; } + if (!mouse->relative_mode || mouse->warp_emulation_active) { + // We're not in relative mode, so all mouse events are global mouse events + mouseID = SDL_GLOBAL_MOUSE_ID; + } + // Post the event, if desired if (SDL_EventEnabled(SDL_EVENT_MOUSE_WHEEL)) { SDL_Event event; @@ -1100,23 +1005,15 @@ void SDL_QuitMouse(void) mouse->cur_cursor = NULL; if (mouse->sources) { + for (int i = 0; i < mouse->num_sources; ++i) { + SDL_MouseInputSource *source = &mouse->sources[i]; + SDL_free(source->clickstate); + } SDL_free(mouse->sources); mouse->sources = NULL; } mouse->num_sources = 0; - if (mouse->clickstate) { - SDL_free(mouse->clickstate); - mouse->clickstate = NULL; - } - mouse->num_clickstates = 0; - - if (mouse->system_scale_values) { - SDL_free(mouse->system_scale_values); - mouse->system_scale_values = NULL; - } - mouse->num_system_scale_values = 0; - SDL_RemoveHintCallback(SDL_HINT_MOUSE_DOUBLE_CLICK_TIME, SDL_MouseDoubleClickTimeChanged, mouse); @@ -1132,6 +1029,9 @@ void SDL_QuitMouse(void) SDL_RemoveHintCallback(SDL_HINT_MOUSE_RELATIVE_SYSTEM_SCALE, SDL_MouseRelativeSystemScaleChanged, mouse); + SDL_RemoveHintCallback(SDL_HINT_MOUSE_RELATIVE_MODE_CENTER, + SDL_MouseRelativeModeCenterChanged, mouse); + SDL_RemoveHintCallback(SDL_HINT_MOUSE_EMULATE_WARP_WITH_RELATIVE, SDL_MouseWarpEmulationChanged, mouse); @@ -1150,9 +1050,6 @@ void SDL_QuitMouse(void) SDL_RemoveHintCallback(SDL_HINT_MOUSE_RELATIVE_CURSOR_VISIBLE, SDL_MouseRelativeCursorVisibleChanged, mouse); - SDL_RemoveHintCallback(SDL_HINT_MOUSE_RELATIVE_CLIP_INTERVAL, - SDL_MouseRelativeClipIntervalChanged, mouse); - for (int i = SDL_mouse_count; i--; ) { SDL_RemoveMouse(SDL_mice[i].instance_id, false); } @@ -1178,13 +1075,13 @@ SDL_MouseButtonFlags SDL_GetRelativeMouseState(float *x, float *y) SDL_Mouse *mouse = SDL_GetMouse(); if (x) { - *x = mouse->xdelta; + *x = mouse->x_accu; } if (y) { - *y = mouse->ydelta; + *y = mouse->y_accu; } - mouse->xdelta = 0.0f; - mouse->ydelta = 0.0f; + mouse->x_accu = 0.0f; + mouse->y_accu = 0.0f; return SDL_GetMouseButtonState(mouse, SDL_GLOBAL_MOUSE_ID, true); } @@ -1248,8 +1145,7 @@ void SDL_PerformWarpMouseInWindow(SDL_Window *window, float x, float y, bool ign } } - if (mouse->WarpMouse && - (!mouse->relative_mode || mouse->relative_mode_warp)) { + if (mouse->WarpMouse && !mouse->relative_mode) { mouse->WarpMouse(window, x, y); } else { SDL_PrivateSendMouseMotion(0, window, SDL_GLOBAL_MOUSE_ID, false, x, y); @@ -1318,16 +1214,6 @@ bool SDL_WarpMouseGlobal(float x, float y) return SDL_Unsupported(); } -static bool SDL_ShouldUseRelativeModeWarp(SDL_Mouse *mouse) -{ - if (!mouse->WarpMouse) { - // Need this functionality for relative mode warp implementation - return false; - } - - return SDL_GetHintBoolean(SDL_HINT_MOUSE_RELATIVE_MODE_WARP, false); -} - bool SDL_SetRelativeMouseMode(bool enabled) { SDL_Mouse *mouse = SDL_GetMouse(); @@ -1343,17 +1229,9 @@ bool SDL_SetRelativeMouseMode(bool enabled) } // Set the relative mode - if (!enabled && mouse->relative_mode_warp) { - mouse->relative_mode_warp = false; - } else if (enabled && SDL_ShouldUseRelativeModeWarp(mouse)) { - mouse->relative_mode_warp = true; - } else if (!mouse->SetRelativeMouseMode || !mouse->SetRelativeMouseMode(enabled)) { + if (!mouse->SetRelativeMouseMode || !mouse->SetRelativeMouseMode(enabled)) { if (enabled) { - // Fall back to warp mode if native relative mode failed - if (!mouse->WarpMouse) { - return SDL_SetError("No relative mode implementation available"); - } - mouse->relative_mode_warp = true; + return SDL_SetError("No relative mode implementation available"); } } mouse->relative_mode = enabled; @@ -1365,12 +1243,6 @@ bool SDL_SetRelativeMouseMode(bool enabled) if (enabled && focusWindow) { SDL_SetMouseFocus(focusWindow); - - if (mouse->relative_mode_warp) { - float center_x = (float)focusWindow->w / 2.0f; - float center_y = (float)focusWindow->h / 2.0f; - SDL_PerformWarpMouseInWindow(focusWindow, center_x, center_y, true); - } } if (focusWindow) { diff --git a/libs/SDL3/src/events/SDL_mouse_c.h b/libs/SDL3/src/events/SDL_mouse_c.h index 993e2ae1..074e6422 100644 --- a/libs/SDL3/src/events/SDL_mouse_c.h +++ b/libs/SDL3/src/events/SDL_mouse_c.h @@ -39,16 +39,21 @@ struct SDL_Cursor typedef struct { - SDL_MouseID mouseID; - Uint32 buttonstate; -} SDL_MouseInputSource; + Uint64 last_timestamp; + double click_motion_x; + double click_motion_y; + Uint8 click_count; +} SDL_MouseClickState; typedef struct { - float last_x, last_y; - Uint64 last_timestamp; - Uint8 click_count; -} SDL_MouseClickState; + SDL_MouseID mouseID; + Uint32 buttonstate; + + // Data for double-click tracking + int num_clickstates; + SDL_MouseClickState *clickstate; +} SDL_MouseInputSource; typedef struct { @@ -82,30 +87,33 @@ typedef struct // Get absolute mouse coordinates. (x) and (y) are never NULL and set to zero before call. SDL_MouseButtonFlags (*GetGlobalMouseState)(float *x, float *y); + // Platform-specific system mouse transform + void (*ApplySystemScale)(void *internal, Uint64 timestamp, SDL_Window *window, SDL_MouseID mouseID, float *x, float *y); + void *system_scale_data; + // Data common to all mice SDL_Window *focus; float x; float y; - float xdelta; - float ydelta; + float x_accu; + float y_accu; float last_x, last_y; // the last reported x and y coordinates + double click_motion_x; + double click_motion_y; bool has_position; bool relative_mode; - bool relative_mode_warp; bool relative_mode_warp_motion; bool relative_mode_cursor_visible; + bool relative_mode_center; bool warp_emulation_hint; bool warp_emulation_active; bool warp_emulation_prohibited; Uint64 last_center_warp_time_ns; - int relative_mode_clip_interval; bool enable_normal_speed_scale; float normal_speed_scale; bool enable_relative_speed_scale; float relative_speed_scale; bool enable_relative_system_scale; - int num_system_scale_values; - float *system_scale_values; Uint32 double_click_time; int double_click_radius; bool touch_mouse_events; @@ -122,10 +130,6 @@ typedef struct int num_sources; SDL_MouseInputSource *sources; - // Data for double-click tracking - int num_clickstates; - SDL_MouseClickState *clickstate; - SDL_Cursor *cursors; SDL_Cursor *def_cursor; SDL_Cursor *cur_cursor; @@ -156,15 +160,15 @@ extern SDL_Mouse *SDL_GetMouse(void); // Set the default mouse cursor extern void SDL_SetDefaultCursor(SDL_Cursor *cursor); +// Get the preferred default system cursor +extern SDL_SystemCursor SDL_GetDefaultSystemCursor(void); + // Set the mouse focus window extern void SDL_SetMouseFocus(SDL_Window *window); // Update the mouse capture window extern bool SDL_UpdateMouseCapture(bool force_release); -// You can set either a single scale, or a set of {speed, scale} values in sorted order -extern bool SDL_SetMouseSystemScale(int num_values, const float *values); - // Send a mouse motion event extern void SDL_SendMouseMotion(Uint64 timestamp, SDL_Window *window, SDL_MouseID mouseID, bool relative, float x, float y); diff --git a/libs/SDL3/src/events/SDL_windowevents.c b/libs/SDL3/src/events/SDL_windowevents.c index 01a09ab7..5535ec50 100644 --- a/libs/SDL3/src/events/SDL_windowevents.c +++ b/libs/SDL3/src/events/SDL_windowevents.c @@ -69,6 +69,7 @@ bool SDL_SendWindowEvent(SDL_Window *window, SDL_EventType windowevent, int data case SDL_EVENT_WINDOW_MOVED: window->undefined_x = false; window->undefined_y = false; + window->last_position_pending = false; if (!(window->flags & SDL_WINDOW_FULLSCREEN)) { window->windowed.x = data1; window->windowed.y = data2; @@ -85,6 +86,7 @@ bool SDL_SendWindowEvent(SDL_Window *window, SDL_EventType windowevent, int data window->y = data2; break; case SDL_EVENT_WINDOW_RESIZED: + window->last_size_pending = false; if (!(window->flags & SDL_WINDOW_FULLSCREEN)) { window->windowed.w = data1; window->windowed.h = data2; diff --git a/libs/SDL3/src/file/SDL_asyncio.c b/libs/SDL3/src/file/SDL_asyncio.c new file mode 100644 index 00000000..a18f7b95 --- /dev/null +++ b/libs/SDL3/src/file/SDL_asyncio.c @@ -0,0 +1,327 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2024 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#include "SDL_internal.h" +#include "SDL_sysasyncio.h" +#include "SDL_asyncio_c.h" + +static const char *AsyncFileModeValid(const char *mode) +{ + static const struct { const char *valid; const char *with_binary; } mode_map[] = { + { "r", "rb" }, + { "w", "wb" }, + { "r+","r+b" }, + { "w+", "w+b" } + }; + + for (int i = 0; i < SDL_arraysize(mode_map); i++) { + if (SDL_strcmp(mode, mode_map[i].valid) == 0) { + return mode_map[i].with_binary; + } + } + return NULL; +} + +SDL_AsyncIO *SDL_AsyncIOFromFile(const char *file, const char *mode) +{ + if (!file) { + SDL_InvalidParamError("file"); + return NULL; + } else if (!mode) { + SDL_InvalidParamError("mode"); + return NULL; + } + + const char *binary_mode = AsyncFileModeValid(mode); + if (!binary_mode) { + SDL_SetError("Unsupported file mode"); + return NULL; + } + + SDL_AsyncIO *asyncio = (SDL_AsyncIO *)SDL_calloc(1, sizeof(*asyncio)); + if (asyncio) { + asyncio->lock = SDL_CreateMutex(); + if (!asyncio->lock) { + SDL_free(asyncio); + return NULL; + } + } + + if (!SDL_SYS_AsyncIOFromFile(file, binary_mode, asyncio)) { + SDL_DestroyMutex(asyncio->lock); + SDL_free(asyncio); + return NULL; + } + + return asyncio; +} + +Sint64 SDL_GetAsyncIOSize(SDL_AsyncIO *asyncio) +{ + if (!asyncio) { + SDL_InvalidParamError("asyncio"); + return -1; + } + return asyncio->iface.size(asyncio->userdata); +} + +static bool RequestAsyncIO(bool reading, SDL_AsyncIO *asyncio, void *ptr, Uint64 offset, Uint64 size, SDL_AsyncIOQueue *queue, void *userdata) +{ + if (!asyncio) { + return SDL_InvalidParamError("asyncio"); + } else if (!ptr) { + return SDL_InvalidParamError("ptr"); + } else if (!queue) { + return SDL_InvalidParamError("queue"); + } + + SDL_AsyncIOTask *task = (SDL_AsyncIOTask *) SDL_calloc(1, sizeof (*task)); + if (!task) { + return false; + } + + task->asyncio = asyncio; + task->type = reading ? SDL_ASYNCIO_TASK_READ : SDL_ASYNCIO_TASK_WRITE; + task->offset = offset; + task->buffer = ptr; + task->requested_size = size; + task->app_userdata = userdata; + task->queue = queue; + + SDL_LockMutex(asyncio->lock); + if (asyncio->closing) { + SDL_free(task); + SDL_UnlockMutex(asyncio->lock); + return SDL_SetError("SDL_AsyncIO is closing, can't start new tasks"); + } + LINKED_LIST_PREPEND(task, asyncio->tasks, asyncio); + SDL_AddAtomicInt(&queue->tasks_inflight, 1); + SDL_UnlockMutex(asyncio->lock); + + const bool queued = reading ? asyncio->iface.read(asyncio->userdata, task) : asyncio->iface.write(asyncio->userdata, task); + if (!queued) { + SDL_AddAtomicInt(&queue->tasks_inflight, -1); + SDL_LockMutex(asyncio->lock); + LINKED_LIST_UNLINK(task, asyncio); + SDL_UnlockMutex(asyncio->lock); + SDL_free(task); + task = NULL; + } + + return (task != NULL); +} + +bool SDL_ReadAsyncIO(SDL_AsyncIO *asyncio, void *ptr, Uint64 offset, Uint64 size, SDL_AsyncIOQueue *queue, void *userdata) +{ + return RequestAsyncIO(true, asyncio, ptr, offset, size, queue, userdata); +} + +bool SDL_WriteAsyncIO(SDL_AsyncIO *asyncio, void *ptr, Uint64 offset, Uint64 size, SDL_AsyncIOQueue *queue, void *userdata) +{ + return RequestAsyncIO(false, asyncio, ptr, offset, size, queue, userdata); +} + +bool SDL_CloseAsyncIO(SDL_AsyncIO *asyncio, bool flush, SDL_AsyncIOQueue *queue, void *userdata) +{ + if (!asyncio) { + return SDL_InvalidParamError("asyncio"); + } else if (!queue) { + return SDL_InvalidParamError("queue"); + } + + SDL_LockMutex(asyncio->lock); + if (asyncio->closing) { + SDL_UnlockMutex(asyncio->lock); + return SDL_SetError("Already closing"); + } + + SDL_AsyncIOTask *task = (SDL_AsyncIOTask *) SDL_calloc(1, sizeof (*task)); + if (task) { + task->asyncio = asyncio; + task->type = SDL_ASYNCIO_TASK_CLOSE; + task->app_userdata = userdata; + task->queue = queue; + task->flush = flush; + + asyncio->closing = task; + + if (LINKED_LIST_START(asyncio->tasks, asyncio) == NULL) { // no tasks? Queue the close task now. + LINKED_LIST_PREPEND(task, asyncio->tasks, asyncio); + SDL_AddAtomicInt(&queue->tasks_inflight, 1); + if (!asyncio->iface.close(asyncio->userdata, task)) { + // uhoh, maybe they can try again later...? + SDL_AddAtomicInt(&queue->tasks_inflight, -1); + LINKED_LIST_UNLINK(task, asyncio); + SDL_free(task); + task = asyncio->closing = NULL; + } + } + } + + SDL_UnlockMutex(asyncio->lock); + + return (task != NULL); +} + +SDL_AsyncIOQueue *SDL_CreateAsyncIOQueue(void) +{ + SDL_AsyncIOQueue *queue = SDL_calloc(1, sizeof (*queue)); + if (queue) { + SDL_SetAtomicInt(&queue->tasks_inflight, 0); + if (!SDL_SYS_CreateAsyncIOQueue(queue)) { + SDL_free(queue); + return NULL; + } + } + return queue; +} + +static bool GetAsyncIOTaskOutcome(SDL_AsyncIOTask *task, SDL_AsyncIOOutcome *outcome) +{ + if (!task || !outcome) { + return false; + } + + SDL_AsyncIO *asyncio = task->asyncio; + + SDL_zerop(outcome); + outcome->asyncio = asyncio->oneshot ? NULL : asyncio; + outcome->result = task->result; + outcome->buffer = task->buffer; + outcome->offset = task->offset; + outcome->bytes_requested = task->requested_size; + outcome->bytes_transferred = task->result_size; + outcome->userdata = task->app_userdata; + + // Take the completed task out of the SDL_AsyncIO that created it. + SDL_LockMutex(asyncio->lock); + LINKED_LIST_UNLINK(task, asyncio); + // see if it's time to queue a pending close request (close requested and no other pending tasks) + SDL_AsyncIOTask *closing = asyncio->closing; + if (closing && (task != closing) && (LINKED_LIST_START(asyncio->tasks, asyncio) == NULL)) { + LINKED_LIST_PREPEND(closing, asyncio->tasks, asyncio); + SDL_AddAtomicInt(&closing->queue->tasks_inflight, 1); + const bool async_close_task_was_queued = asyncio->iface.close(asyncio->userdata, closing); + SDL_assert(async_close_task_was_queued); // !!! FIXME: if this fails to queue the task, we're leaking resources! + if (!async_close_task_was_queued) { + SDL_AddAtomicInt(&closing->queue->tasks_inflight, -1); + } + } + SDL_UnlockMutex(task->asyncio->lock); + + // was this the result of a closing task? Finally destroy the asyncio. + bool retval = true; + if (closing && (task == closing)) { + if (asyncio->oneshot) { + retval = false; // don't send the close task results on to the app, just the read task for these. + } + asyncio->iface.destroy(asyncio->userdata); + SDL_DestroyMutex(asyncio->lock); + SDL_free(asyncio); + } + + SDL_AddAtomicInt(&task->queue->tasks_inflight, -1); + SDL_free(task); + + return retval; +} + +bool SDL_GetAsyncIOResult(SDL_AsyncIOQueue *queue, SDL_AsyncIOOutcome *outcome) +{ + if (!queue || !outcome) { + return false; + } + return GetAsyncIOTaskOutcome(queue->iface.get_results(queue->userdata), outcome); +} + +bool SDL_WaitAsyncIOResult(SDL_AsyncIOQueue *queue, SDL_AsyncIOOutcome *outcome, Sint32 timeoutMS) +{ + if (!queue || !outcome) { + return false; + } + return GetAsyncIOTaskOutcome(queue->iface.wait_results(queue->userdata, timeoutMS), outcome); +} + +void SDL_SignalAsyncIOQueue(SDL_AsyncIOQueue *queue) +{ + if (queue) { + queue->iface.signal(queue->userdata); + } +} + +void SDL_DestroyAsyncIOQueue(SDL_AsyncIOQueue *queue) +{ + if (queue) { + // block until any pending tasks complete. + while (SDL_GetAtomicInt(&queue->tasks_inflight) > 0) { + SDL_AsyncIOTask *task = queue->iface.wait_results(queue->userdata, -1); + if (task) { + if (task->asyncio->oneshot) { + SDL_free(task->buffer); // throw away the buffer from SDL_LoadFileAsync that will never be consumed/freed by app. + task->buffer = NULL; + } + SDL_AsyncIOOutcome outcome; + GetAsyncIOTaskOutcome(task, &outcome); // this frees the task, and does other upkeep. + } + } + + queue->iface.destroy(queue->userdata); + SDL_free(queue); + } +} + +void SDL_QuitAsyncIO(void) +{ + SDL_SYS_QuitAsyncIO(); +} + +bool SDL_LoadFileAsync(const char *file, SDL_AsyncIOQueue *queue, void *userdata) +{ + if (!file) { + return SDL_InvalidParamError("file"); + } else if (!queue) { + return SDL_InvalidParamError("queue"); + } + + bool retval = false; + SDL_AsyncIO *asyncio = SDL_AsyncIOFromFile(file, "r"); + if (asyncio) { + asyncio->oneshot = true; + + void *ptr = NULL; + const Sint64 flen = SDL_GetAsyncIOSize(asyncio); + if (flen >= 0) { + // !!! FIXME: check if flen > address space, since it'll truncate and we'll just end up with an incomplete buffer or a crash. + ptr = SDL_malloc((size_t) (flen + 1)); // over-allocate by one so we can add a null-terminator. + if (ptr) { + retval = SDL_ReadAsyncIO(asyncio, ptr, 0, (Uint64) flen, queue, userdata); + if (!retval) { + SDL_free(ptr); + } + } + } + + SDL_CloseAsyncIO(asyncio, false, queue, userdata); // if this fails, we'll have a resource leak, but this would already be a dramatic system failure. + } + + return retval; +} + diff --git a/libs/SDL3/src/video/ngage/SDL_ngagewindow.h b/libs/SDL3/src/file/SDL_asyncio_c.h similarity index 71% rename from libs/SDL3/src/video/ngage/SDL_ngagewindow.h rename to libs/SDL3/src/file/SDL_asyncio_c.h index 317e03b2..85b443a9 100644 --- a/libs/SDL3/src/video/ngage/SDL_ngagewindow.h +++ b/libs/SDL3/src/file/SDL_asyncio_c.h @@ -18,21 +18,13 @@ misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ +#include "../SDL_internal.h" -#ifndef SDL_ngagewindow_h -#define SDL_ngagewindow_h +#ifndef SDL_asyncio_c_h_ +#define SDL_asyncio_c_h_ -#include "../SDL_sysvideo.h" +// Shutdown any still-existing Async I/O. Note that there is no Init function, as it inits on-demand! +extern void SDL_QuitAsyncIO(void); -#include "SDL_ngagevideo.h" +#endif // SDL_asyncio_c_h_ -typedef struct -{ - SDL_Window *sdl_window; - -} NGAGE_Window; - -extern int NGAGE_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID create_props); -extern void NGAGE_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window); - -#endif // SDL_ngagewindow diff --git a/libs/SDL3/src/file/SDL_iostream.c b/libs/SDL3/src/file/SDL_iostream.c index 1fa652f8..7e70f5b9 100644 --- a/libs/SDL3/src/file/SDL_iostream.c +++ b/libs/SDL3/src/file/SDL_iostream.c @@ -1218,6 +1218,58 @@ void *SDL_LoadFile(const char *file, size_t *datasize) return SDL_LoadFile_IO(stream, datasize, true); } +bool SDL_SaveFile_IO(SDL_IOStream *src, const void *data, size_t datasize, bool closeio) +{ + size_t size_written = 0; + size_t size_total = 0; + bool success = true; + + if (!src) { + SDL_InvalidParamError("src"); + goto done; + } + + if (!data && datasize > 0) { + SDL_InvalidParamError("data"); + goto done; + } + + if (datasize > 0) { + while (size_total < datasize) { + size_written = SDL_WriteIO(src, ((const char *) data) + size_written, datasize - size_written); + + if (size_written <= 0) { + if (SDL_GetIOStatus(src) == SDL_IO_STATUS_NOT_READY) { + // Wait for the stream to be ready + SDL_Delay(1); + continue; + } else { + success = false; + goto done; + } + } + + size_total += size_written; + } + } + +done: + if (closeio && src) { + SDL_CloseIO(src); + } + + return success; +} + +bool SDL_SaveFile(const char *file, const void *data, size_t datasize) +{ + SDL_IOStream *stream = SDL_IOFromFile(file, "wb"); + if (!stream) { + return false; + } + return SDL_SaveFile_IO(stream, data, datasize, true); +} + SDL_PropertiesID SDL_GetIOProperties(SDL_IOStream *context) { if (!context) { diff --git a/libs/SDL3/src/file/SDL_sysasyncio.h b/libs/SDL3/src/file/SDL_sysasyncio.h new file mode 100644 index 00000000..6955139c --- /dev/null +++ b/libs/SDL3/src/file/SDL_sysasyncio.h @@ -0,0 +1,144 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2024 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#include "SDL_internal.h" + +#ifndef SDL_sysasyncio_h_ +#define SDL_sysasyncio_h_ + +#if defined(SDL_PLATFORM_WINDOWS) && defined(NTDDI_WIN10_NI) +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) && NTDDI_VERSION >= NTDDI_WIN10_NI +#define HAVE_IORINGAPI_H +#endif +#endif + +// If your platform has an option other than the "generic" code, make sure this +// is #defined to 0 instead and implement the SDL_SYS_* functions below in your +// backend (having them maybe call into the SDL_SYS_*_Generic versions as a +// fallback if the platform has functionality that isn't always available). +#if defined(HAVE_LIBURING_H) || defined(HAVE_IORINGAPI_H) +#define SDL_ASYNCIO_ONLY_HAVE_GENERIC 0 +#else +#define SDL_ASYNCIO_ONLY_HAVE_GENERIC 1 +#endif + +// this entire thing is just juggling doubly-linked lists, so make some helper macros. +#define LINKED_LIST_DECLARE_FIELDS(type, prefix) \ + type *prefix##prev; \ + type *prefix##next + +#define LINKED_LIST_PREPEND(item, list, prefix) do { \ + item->prefix##prev = &list; \ + item->prefix##next = list.prefix##next; \ + if (item->prefix##next) { \ + item->prefix##next->prefix##prev = item; \ + } \ + list.prefix##next = item; \ +} while (false) + +#define LINKED_LIST_UNLINK(item, prefix) do { \ + if (item->prefix##next) { \ + item->prefix##next->prefix##prev = item->prefix##prev; \ + } \ + item->prefix##prev->prefix##next = task->prefix##next; \ + item->prefix##prev = item->prefix##next = NULL; \ +} while (false) + +#define LINKED_LIST_START(list, prefix) (list.prefix##next) +#define LINKED_LIST_NEXT(item, prefix) (item->prefix##next) +#define LINKED_LIST_PREV(item, prefix) (item->prefix##prev) + +typedef struct SDL_AsyncIOTask SDL_AsyncIOTask; + +struct SDL_AsyncIOTask +{ + SDL_AsyncIO *asyncio; + SDL_AsyncIOTaskType type; + SDL_AsyncIOQueue *queue; + Uint64 offset; + bool flush; + void *buffer; + char *error; + SDL_AsyncIOResult result; + Uint64 requested_size; + Uint64 result_size; + void *app_userdata; + LINKED_LIST_DECLARE_FIELDS(struct SDL_AsyncIOTask, asyncio); + LINKED_LIST_DECLARE_FIELDS(struct SDL_AsyncIOTask, queue); // the generic backend uses this, so I've added it here to avoid the extra allocation. + LINKED_LIST_DECLARE_FIELDS(struct SDL_AsyncIOTask, threadpool); // the generic backend uses this, so I've added it here to avoid the extra allocation. +}; + +typedef struct SDL_AsyncIOQueueInterface +{ + bool (*queue_task)(void *userdata, SDL_AsyncIOTask *task); + void (*cancel_task)(void *userdata, SDL_AsyncIOTask *task); + SDL_AsyncIOTask * (*get_results)(void *userdata); + SDL_AsyncIOTask * (*wait_results)(void *userdata, Sint32 timeoutMS); + void (*signal)(void *userdata); + void (*destroy)(void *userdata); +} SDL_AsyncIOQueueInterface; + +struct SDL_AsyncIOQueue +{ + SDL_AsyncIOQueueInterface iface; + void *userdata; + SDL_AtomicInt tasks_inflight; +}; + +// this interface is kept per-object, even though generally it's going to decide +// on a single interface that is the same for the entire process, but I've kept +// the abstraction in case we start exposing more types of async i/o, like +// sockets, in the future. +typedef struct SDL_AsyncIOInterface +{ + Sint64 (*size)(void *userdata); + bool (*read)(void *userdata, SDL_AsyncIOTask *task); + bool (*write)(void *userdata, SDL_AsyncIOTask *task); + bool (*close)(void *userdata, SDL_AsyncIOTask *task); + void (*destroy)(void *userdata); +} SDL_AsyncIOInterface; + +struct SDL_AsyncIO +{ + SDL_AsyncIOInterface iface; + void *userdata; + SDL_Mutex *lock; + SDL_AsyncIOTask tasks; + SDL_AsyncIOTask *closing; // The close task, which isn't queued until all pending work for this file is done. + bool oneshot; // true if this is a SDL_LoadFileAsync open. +}; + +// This is implemented for various platforms; param validation is done before calling this. Open file, fill in iface and userdata. +extern bool SDL_SYS_AsyncIOFromFile(const char *file, const char *mode, SDL_AsyncIO *asyncio); + +// This is implemented for various platforms. Call SDL_OpenAsyncIOQueue from in here. +extern bool SDL_SYS_CreateAsyncIOQueue(SDL_AsyncIOQueue *queue); + +// This is called during SDL_QuitAsyncIO, after all tasks have completed and all files are closed, to let the platform clean up global backend details. +extern void SDL_SYS_QuitAsyncIO(void); + +// the "generic" version is always available, since it is almost always needed as a fallback even on platforms that might offer something better. +extern bool SDL_SYS_AsyncIOFromFile_Generic(const char *file, const char *mode, SDL_AsyncIO *asyncio); +extern bool SDL_SYS_CreateAsyncIOQueue_Generic(SDL_AsyncIOQueue *queue); +extern void SDL_SYS_QuitAsyncIO_Generic(void); + +#endif + diff --git a/libs/SDL3/src/file/generic/SDL_asyncio_generic.c b/libs/SDL3/src/file/generic/SDL_asyncio_generic.c new file mode 100644 index 00000000..9c1e442d --- /dev/null +++ b/libs/SDL3/src/file/generic/SDL_asyncio_generic.c @@ -0,0 +1,465 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2024 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +// The generic backend uses a threadpool to block on synchronous i/o. +// This is not ideal, it's meant to be used if there isn't a platform-specific +// backend that can do something more efficient! + +#include "SDL_internal.h" +#include "../SDL_sysasyncio.h" + +// on Emscripten without threads, async i/o is synchronous. Sorry. Almost +// everything is MEMFS, so it's just a memcpy anyhow, and the Emscripten +// filesystem APIs don't offer async. In theory, directly accessing +// persistent storage _does_ offer async APIs at the browser level, but +// that's not exposed in Emscripten's filesystem abstraction. +#if defined(SDL_PLATFORM_EMSCRIPTEN) && !defined(__EMSCRIPTEN_PTHREADS__) +#define SDL_ASYNCIO_USE_THREADPOOL 0 +#else +#define SDL_ASYNCIO_USE_THREADPOOL 1 +#endif + +typedef struct GenericAsyncIOQueueData +{ + SDL_Mutex *lock; + SDL_Condition *condition; + SDL_AsyncIOTask completed_tasks; +} GenericAsyncIOQueueData; + +typedef struct GenericAsyncIOData +{ + SDL_Mutex *lock; // !!! FIXME: we can skip this lock if we have an equivalent of pread/pwrite + SDL_IOStream *io; +} GenericAsyncIOData; + +static void AsyncIOTaskComplete(SDL_AsyncIOTask *task) +{ + SDL_assert(task->queue); + GenericAsyncIOQueueData *data = (GenericAsyncIOQueueData *) task->queue->userdata; + SDL_LockMutex(data->lock); + LINKED_LIST_PREPEND(task, data->completed_tasks, queue); + SDL_SignalCondition(data->condition); // wake a thread waiting on the queue. + SDL_UnlockMutex(data->lock); +} + +// synchronous i/o is offloaded onto the threadpool. This function does the threaded work. +// This is called directly, without a threadpool, if !SDL_ASYNCIO_USE_THREADPOOL. +static void SynchronousIO(SDL_AsyncIOTask *task) +{ + SDL_assert(task->result != SDL_ASYNCIO_CANCELLED); // shouldn't have gotten in here if cancelled! + + GenericAsyncIOData *data = (GenericAsyncIOData *) task->asyncio->userdata; + SDL_IOStream *io = data->io; + const size_t size = (size_t) task->requested_size; + void *ptr = task->buffer; + + // this seek won't work if two tasks are reading from the same file at the same time, + // so we lock here. This makes multiple reads from a single file serialize, but different + // files will still run in parallel. An app can also open the same file twice to avoid this. + SDL_LockMutex(data->lock); + if (task->type == SDL_ASYNCIO_TASK_CLOSE) { + bool okay = true; + if (task->flush) { + okay = SDL_FlushIO(data->io); + } + okay = SDL_CloseIO(data->io) && okay; + task->result = okay ? SDL_ASYNCIO_COMPLETE : SDL_ASYNCIO_FAILURE; + } else if (SDL_SeekIO(io, (Sint64) task->offset, SDL_IO_SEEK_SET) < 0) { + task->result = SDL_ASYNCIO_FAILURE; + } else { + const bool writing = (task->type == SDL_ASYNCIO_TASK_WRITE); + task->result_size = (Uint64) (writing ? SDL_WriteIO(io, ptr, size) : SDL_ReadIO(io, ptr, size)); + if (task->result_size == task->requested_size) { + task->result = SDL_ASYNCIO_COMPLETE; + } else { + if (writing) { + task->result = SDL_ASYNCIO_FAILURE; // it's always a failure on short writes. + } else { + const SDL_IOStatus status = SDL_GetIOStatus(io); + SDL_assert(status != SDL_IO_STATUS_READY); // this should have either failed or been EOF. + SDL_assert(status != SDL_IO_STATUS_NOT_READY); // these should not be non-blocking reads! + task->result = (status == SDL_IO_STATUS_EOF) ? SDL_ASYNCIO_COMPLETE : SDL_ASYNCIO_FAILURE; + } + } + } + SDL_UnlockMutex(data->lock); + + AsyncIOTaskComplete(task); +} + +#if SDL_ASYNCIO_USE_THREADPOOL +static SDL_InitState threadpool_init; +static SDL_Mutex *threadpool_lock = NULL; +static bool stop_threadpool = false; +static SDL_AsyncIOTask threadpool_tasks; +static SDL_Condition *threadpool_condition = NULL; +static int max_threadpool_threads = 0; +static int running_threadpool_threads = 0; +static int idle_threadpool_threads = 0; +static int threadpool_threads_spun = 0; + +static int SDLCALL AsyncIOThreadpoolWorker(void *data) +{ + SDL_LockMutex(threadpool_lock); + + while (!stop_threadpool) { + SDL_AsyncIOTask *task = LINKED_LIST_START(threadpool_tasks, threadpool); + if (!task) { + // if we go 30 seconds without a new task, terminate unless we're the only thread left. + idle_threadpool_threads++; + const bool rc = SDL_WaitConditionTimeout(threadpool_condition, threadpool_lock, 30000); + idle_threadpool_threads--; + + if (!rc) { + // decide if we have too many idle threads, and if so, quit to let thread pool shrink when not busy. + if (idle_threadpool_threads) { + break; + } + } + + continue; + } + + LINKED_LIST_UNLINK(task, threadpool); + + SDL_UnlockMutex(threadpool_lock); + + // bookkeeping is done, so we drop the mutex and fire the work. + SynchronousIO(task); + + SDL_LockMutex(threadpool_lock); // take the lock again and see if there's another task (if not, we'll wait on the Condition). + } + + running_threadpool_threads--; + + // this is kind of a hack, but this lets us reuse threadpool_condition to block on shutdown until all threads have exited. + if (stop_threadpool) { + SDL_BroadcastCondition(threadpool_condition); + } + + SDL_UnlockMutex(threadpool_lock); + + return 0; +} + +static bool MaybeSpinNewWorkerThread(void) +{ + // if all existing threads are busy and the pool of threads isn't maxed out, make a new one. + if ((idle_threadpool_threads == 0) && (running_threadpool_threads < max_threadpool_threads)) { + char threadname[32]; + SDL_snprintf(threadname, sizeof (threadname), "SDLasyncio%d", threadpool_threads_spun); + SDL_Thread *thread = SDL_CreateThread(AsyncIOThreadpoolWorker, threadname, NULL); + if (thread == NULL) { + return false; + } + SDL_DetachThread(thread); // these terminate themselves when idle too long, so we never WaitThread. + running_threadpool_threads++; + threadpool_threads_spun++; + } + return true; +} + +static void QueueAsyncIOTask(SDL_AsyncIOTask *task) +{ + SDL_assert(task != NULL); + + SDL_LockMutex(threadpool_lock); + + if (stop_threadpool) { // just in case. + task->result = SDL_ASYNCIO_CANCELLED; + AsyncIOTaskComplete(task); + } else { + LINKED_LIST_PREPEND(task, threadpool_tasks, threadpool); + MaybeSpinNewWorkerThread(); // okay if this fails or the thread pool is maxed out. Something will get there eventually. + + // tell idle threads to get to work. + // This is a broadcast because we want someone from the thread pool to wake up, but + // also shutdown might also be blocking on this. One of the threads will grab + // it, the others will go back to sleep. + SDL_BroadcastCondition(threadpool_condition); + } + + SDL_UnlockMutex(threadpool_lock); +} + +// We don't initialize async i/o at all until it's used, so +// JUST IN CASE two things try to start at the same time, +// this will make sure everything gets the same mutex. +static bool PrepareThreadpool(void) +{ + bool okay = true; + if (SDL_ShouldInit(&threadpool_init)) { + max_threadpool_threads = (SDL_GetNumLogicalCPUCores() * 2) + 1; // !!! FIXME: this should probably have a hint to override. + max_threadpool_threads = SDL_clamp(max_threadpool_threads, 1, 8); // 8 is probably more than enough. + + okay = (okay && ((threadpool_lock = SDL_CreateMutex()) != NULL)); + okay = (okay && ((threadpool_condition = SDL_CreateCondition()) != NULL)); + okay = (okay && MaybeSpinNewWorkerThread()); // make sure at least one thread is going, since we'll need it. + + if (!okay) { + if (threadpool_condition) { + SDL_DestroyCondition(threadpool_condition); + threadpool_condition = NULL; + } + if (threadpool_lock) { + SDL_DestroyMutex(threadpool_lock); + threadpool_lock = NULL; + } + } + + SDL_SetInitialized(&threadpool_init, okay); + } + return okay; +} + +static void ShutdownThreadpool(void) +{ + if (SDL_ShouldQuit(&threadpool_init)) { + SDL_LockMutex(threadpool_lock); + + // cancel anything that's still pending. + SDL_AsyncIOTask *task; + while ((task = LINKED_LIST_START(threadpool_tasks, threadpool)) != NULL) { + LINKED_LIST_UNLINK(task, threadpool); + task->result = SDL_ASYNCIO_CANCELLED; + AsyncIOTaskComplete(task); + } + + stop_threadpool = true; + SDL_BroadcastCondition(threadpool_condition); // tell the whole threadpool to wake up and quit. + + while (running_threadpool_threads > 0) { + // each threadpool thread will broadcast this condition before it terminates if stop_threadpool is set. + // we can't just join the threads because they are detached, so the thread pool can automatically shrink as necessary. + SDL_WaitCondition(threadpool_condition, threadpool_lock); + } + + SDL_UnlockMutex(threadpool_lock); + + SDL_DestroyMutex(threadpool_lock); + threadpool_lock = NULL; + SDL_DestroyCondition(threadpool_condition); + threadpool_condition = NULL; + + max_threadpool_threads = running_threadpool_threads = idle_threadpool_threads = threadpool_threads_spun = 0; + + stop_threadpool = false; + SDL_SetInitialized(&threadpool_init, false); + } +} +#endif + + +static Sint64 generic_asyncio_size(void *userdata) +{ + GenericAsyncIOData *data = (GenericAsyncIOData *) userdata; + return SDL_GetIOSize(data->io); +} + +static bool generic_asyncio_io(void *userdata, SDL_AsyncIOTask *task) +{ + return task->queue->iface.queue_task(task->queue->userdata, task); +} + +static void generic_asyncio_destroy(void *userdata) +{ + GenericAsyncIOData *data = (GenericAsyncIOData *) userdata; + SDL_DestroyMutex(data->lock); + SDL_free(data); +} + + +static bool generic_asyncioqueue_queue_task(void *userdata, SDL_AsyncIOTask *task) +{ + #if SDL_ASYNCIO_USE_THREADPOOL + QueueAsyncIOTask(task); + #else + SynchronousIO(task); // oh well. Get a better platform. + #endif + return true; +} + +static void generic_asyncioqueue_cancel_task(void *userdata, SDL_AsyncIOTask *task) +{ + #if !SDL_ASYNCIO_USE_THREADPOOL // in theory, this was all synchronous and should never call this, but just in case. + task->result = SDL_ASYNCIO_CANCELLED; + AsyncIOTaskComplete(task); + #else + // we can't stop i/o that's in-flight, but we _can_ just refuse to start it if the threadpool hadn't picked it up yet. + SDL_LockMutex(threadpool_lock); + if (LINKED_LIST_PREV(task, threadpool) != NULL) { // still in the queue waiting to be run? Take it out. + LINKED_LIST_UNLINK(task, threadpool); + task->result = SDL_ASYNCIO_CANCELLED; + AsyncIOTaskComplete(task); + } + SDL_UnlockMutex(threadpool_lock); + #endif +} + +static SDL_AsyncIOTask *generic_asyncioqueue_get_results(void *userdata) +{ + GenericAsyncIOQueueData *data = (GenericAsyncIOQueueData *) userdata; + SDL_LockMutex(data->lock); + SDL_AsyncIOTask *task = LINKED_LIST_START(data->completed_tasks, queue); + if (task) { + LINKED_LIST_UNLINK(task, queue); + } + SDL_UnlockMutex(data->lock); + return task; +} + +static SDL_AsyncIOTask *generic_asyncioqueue_wait_results(void *userdata, Sint32 timeoutMS) +{ + GenericAsyncIOQueueData *data = (GenericAsyncIOQueueData *) userdata; + SDL_LockMutex(data->lock); + SDL_AsyncIOTask *task = LINKED_LIST_START(data->completed_tasks, queue); + if (!task) { + SDL_WaitConditionTimeout(data->condition, data->lock, timeoutMS); + task = LINKED_LIST_START(data->completed_tasks, queue); + } + if (task) { + LINKED_LIST_UNLINK(task, queue); + } + SDL_UnlockMutex(data->lock); + return task; +} + +static void generic_asyncioqueue_signal(void *userdata) +{ + GenericAsyncIOQueueData *data = (GenericAsyncIOQueueData *) userdata; + SDL_LockMutex(data->lock); + SDL_BroadcastCondition(data->condition); + SDL_UnlockMutex(data->lock); +} + +static void generic_asyncioqueue_destroy(void *userdata) +{ + GenericAsyncIOQueueData *data = (GenericAsyncIOQueueData *) userdata; + SDL_DestroyMutex(data->lock); + SDL_DestroyCondition(data->condition); + SDL_free(data); +} + +bool SDL_SYS_CreateAsyncIOQueue_Generic(SDL_AsyncIOQueue *queue) +{ + #if SDL_ASYNCIO_USE_THREADPOOL + if (!PrepareThreadpool()) { + return false; + } + #endif + + GenericAsyncIOQueueData *data = (GenericAsyncIOQueueData *) SDL_calloc(1, sizeof (*data)); + if (!data) { + return false; + } + + data->lock = SDL_CreateMutex(); + if (!data->lock) { + SDL_free(data); + return false; + } + + data->condition = SDL_CreateCondition(); + if (!data->condition) { + SDL_DestroyMutex(data->lock); + SDL_free(data); + return false; + } + + static const SDL_AsyncIOQueueInterface SDL_AsyncIOQueue_Generic = { + generic_asyncioqueue_queue_task, + generic_asyncioqueue_cancel_task, + generic_asyncioqueue_get_results, + generic_asyncioqueue_wait_results, + generic_asyncioqueue_signal, + generic_asyncioqueue_destroy + }; + + SDL_copyp(&queue->iface, &SDL_AsyncIOQueue_Generic); + queue->userdata = data; + return true; +} + + +bool SDL_SYS_AsyncIOFromFile_Generic(const char *file, const char *mode, SDL_AsyncIO *asyncio) +{ + #if SDL_ASYNCIO_USE_THREADPOOL + if (!PrepareThreadpool()) { + return false; + } + #endif + + GenericAsyncIOData *data = (GenericAsyncIOData *) SDL_calloc(1, sizeof (*data)); + if (!data) { + return false; + } + + data->lock = SDL_CreateMutex(); + if (!data->lock) { + SDL_free(data); + return false; + } + + data->io = SDL_IOFromFile(file, mode); + if (!data->io) { + SDL_DestroyMutex(data->lock); + SDL_free(data); + return false; + } + + static const SDL_AsyncIOInterface SDL_AsyncIOFile_Generic = { + generic_asyncio_size, + generic_asyncio_io, + generic_asyncio_io, + generic_asyncio_io, + generic_asyncio_destroy + }; + + SDL_copyp(&asyncio->iface, &SDL_AsyncIOFile_Generic); + asyncio->userdata = data; + return true; +} + +void SDL_SYS_QuitAsyncIO_Generic(void) +{ + #if SDL_ASYNCIO_USE_THREADPOOL + ShutdownThreadpool(); + #endif +} + + +#if SDL_ASYNCIO_ONLY_HAVE_GENERIC +bool SDL_SYS_AsyncIOFromFile(const char *file, const char *mode, SDL_AsyncIO *asyncio) +{ + return SDL_SYS_AsyncIOFromFile_Generic(file, mode, asyncio); +} + +bool SDL_SYS_CreateAsyncIOQueue(SDL_AsyncIOQueue *queue) +{ + return SDL_SYS_CreateAsyncIOQueue_Generic(queue); +} + +void SDL_SYS_QuitAsyncIO(void) +{ + SDL_SYS_QuitAsyncIO_Generic(); +} +#endif + diff --git a/libs/SDL3/src/file/io_uring/SDL_asyncio_liburing.c b/libs/SDL3/src/file/io_uring/SDL_asyncio_liburing.c new file mode 100644 index 00000000..b4544fd0 --- /dev/null +++ b/libs/SDL3/src/file/io_uring/SDL_asyncio_liburing.c @@ -0,0 +1,551 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2024 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +// The Linux backend uses io_uring for asynchronous i/o, and falls back to +// the "generic" threadpool implementation if liburing isn't available or +// fails for some other reason. + +#include "SDL_internal.h" + +#ifdef HAVE_LIBURING_H + +#include "../SDL_sysasyncio.h" + +#include +#include +#include +#include // for strerror() + +static SDL_InitState liburing_init; + +// We could add a whole bootstrap thing like the audio/video/etc subsystems use, but let's keep this simple for now. +static bool (*CreateAsyncIOQueue)(SDL_AsyncIOQueue *queue); +static void (*QuitAsyncIO)(void); +static bool (*AsyncIOFromFile)(const char *file, const char *mode, SDL_AsyncIO *asyncio); + +// we never link directly to liburing. +// (this says "-ffi" which sounds like a scripting language binding thing, but the non-ffi version +// is static-inline code we can't lookup with dlsym. This is by design.) +static const char *liburing_library = "liburing-ffi.so.2"; +static void *liburing_handle = NULL; + +#define SDL_LIBURING_FUNCS \ + SDL_LIBURING_FUNC(int, io_uring_queue_init, (unsigned entries, struct io_uring *ring, unsigned flags)) \ + SDL_LIBURING_FUNC(struct io_uring_probe *,io_uring_get_probe,(void)) \ + SDL_LIBURING_FUNC(void, io_uring_free_probe, (struct io_uring_probe *probe)) \ + SDL_LIBURING_FUNC(int, io_uring_opcode_supported, (const struct io_uring_probe *p, int op)) \ + SDL_LIBURING_FUNC(struct io_uring_sqe *, io_uring_get_sqe, (struct io_uring *ring)) \ + SDL_LIBURING_FUNC(void, io_uring_prep_read,(struct io_uring_sqe *sqe, int fd, void *buf, unsigned nbytes, __u64 offset)) \ + SDL_LIBURING_FUNC(void, io_uring_prep_write,(struct io_uring_sqe *sqe, int fd, const void *buf, unsigned nbytes, __u64 offset)) \ + SDL_LIBURING_FUNC(void, io_uring_prep_close, (struct io_uring_sqe *sqe, int fd)) \ + SDL_LIBURING_FUNC(void, io_uring_prep_fsync, (struct io_uring_sqe *sqe, int fd, unsigned fsync_flags)) \ + SDL_LIBURING_FUNC(void, io_uring_prep_cancel, (struct io_uring_sqe *sqe, void *user_data, int flags)) \ + SDL_LIBURING_FUNC(void, io_uring_prep_timeout, (struct io_uring_sqe *sqe, struct __kernel_timespec *ts, unsigned count, unsigned flags)) \ + SDL_LIBURING_FUNC(void, io_uring_prep_nop, (struct io_uring_sqe *sqe)) \ + SDL_LIBURING_FUNC(void, io_uring_sqe_set_data, (struct io_uring_sqe *sqe, void *data)) \ + SDL_LIBURING_FUNC(void, io_uring_sqe_set_flags, (struct io_uring_sqe *sqe, unsigned flags)) \ + SDL_LIBURING_FUNC(int, io_uring_submit, (struct io_uring *ring)) \ + SDL_LIBURING_FUNC(int, io_uring_peek_cqe, (struct io_uring *ring, struct io_uring_cqe **cqe_ptr)) \ + SDL_LIBURING_FUNC(int, io_uring_wait_cqe, (struct io_uring *ring, struct io_uring_cqe **cqe_ptr)) \ + SDL_LIBURING_FUNC(int, io_uring_wait_cqe_timeout, (struct io_uring *ring, struct io_uring_cqe **cqe_ptr, struct __kernel_timespec *ts)) \ + SDL_LIBURING_FUNC(void, io_uring_cqe_seen, (struct io_uring *ring, struct io_uring_cqe *cqe)) \ + SDL_LIBURING_FUNC(void, io_uring_queue_exit, (struct io_uring *ring)) \ + + +#define SDL_LIBURING_FUNC(ret, fn, args) typedef ret (*SDL_fntype_##fn) args; +SDL_LIBURING_FUNCS +#undef SDL_LIBURING_FUNC + +typedef struct SDL_LibUringFunctions +{ + #define SDL_LIBURING_FUNC(ret, fn, args) SDL_fntype_##fn fn; + SDL_LIBURING_FUNCS + #undef SDL_LIBURING_FUNC +} SDL_LibUringFunctions; + +static SDL_LibUringFunctions liburing; + + +typedef struct LibUringAsyncIOQueueData +{ + SDL_Mutex *sqe_lock; + SDL_Mutex *cqe_lock; + struct io_uring ring; + SDL_AtomicInt num_waiting; +} LibUringAsyncIOQueueData; + + +static void UnloadLibUringLibrary(void) +{ + if (liburing_library) { + SDL_UnloadObject(liburing_handle); + liburing_library = NULL; + } + SDL_zero(liburing); +} + +static bool LoadLibUringSyms(void) +{ + #define SDL_LIBURING_FUNC(ret, fn, args) { \ + liburing.fn = (SDL_fntype_##fn) SDL_LoadFunction(liburing_handle, #fn); \ + if (!liburing.fn) { \ + return false; \ + } \ + } + SDL_LIBURING_FUNCS + #undef SDL_LIBURING_FUNC + return true; +} + +// we rely on the presence of liburing to handle io_uring for us. The alternative is making +// direct syscalls into the kernel, which is undesirable. liburing both shields us from this, +// but also smooths over some kernel version differences, etc. +static bool LoadLibUring(void) +{ + bool result = true; + + if (!liburing_handle) { + liburing_handle = SDL_LoadObject(liburing_library); + if (!liburing_handle) { + result = false; + // Don't call SDL_SetError(): SDL_LoadObject already did. + } else { + result = LoadLibUringSyms(); + if (result) { + static const int needed_ops[] = { + IORING_OP_NOP, + IORING_OP_FSYNC, + IORING_OP_TIMEOUT, + IORING_OP_CLOSE, + IORING_OP_READ, + IORING_OP_WRITE, + IORING_OP_ASYNC_CANCEL + }; + + struct io_uring_probe *probe = liburing.io_uring_get_probe(); + if (!probe) { + result = false; + } else { + for (int i = 0; i < SDL_arraysize(needed_ops); i++) { + if (!io_uring_opcode_supported(probe, needed_ops[i])) { + result = false; + break; + } + } + liburing.io_uring_free_probe(probe); + } + } + + if (!result) { + UnloadLibUringLibrary(); + } + } + } + return result; +} + +static bool liburing_SetError(const char *what, int err) +{ + SDL_assert(err <= 0); + return SDL_SetError("%s failed: %s", what, strerror(-err)); +} + +static Sint64 liburing_asyncio_size(void *userdata) +{ + const int fd = (int) (intptr_t) userdata; + struct stat statbuf; + if (fstat(fd, &statbuf) < 0) { + SDL_SetError("fstat failed: %s", strerror(errno)); + return -1; + } + return ((Sint64) statbuf.st_size); +} + +// you must hold sqe_lock when calling this! +static bool liburing_asyncioqueue_queue_task(void *userdata, SDL_AsyncIOTask *task) +{ + LibUringAsyncIOQueueData *queuedata = (LibUringAsyncIOQueueData *) userdata; + const int rc = liburing.io_uring_submit(&queuedata->ring); + return (rc < 0) ? liburing_SetError("io_uring_submit", rc) : true; +} + +static void liburing_asyncioqueue_cancel_task(void *userdata, SDL_AsyncIOTask *task) +{ + SDL_AsyncIOTask *cancel_task = (SDL_AsyncIOTask *) SDL_calloc(1, sizeof (*cancel_task)); + if (!cancel_task) { + return; // oh well, the task can just finish on its own. + } + + LibUringAsyncIOQueueData *queuedata = (LibUringAsyncIOQueueData *) userdata; + + // have to hold a lock because otherwise two threads could get_sqe and submit while one request isn't fully set up. + SDL_LockMutex(queuedata->sqe_lock); + struct io_uring_sqe *sqe = liburing.io_uring_get_sqe(&queuedata->ring); + if (!sqe) { + SDL_UnlockMutex(queuedata->sqe_lock); + SDL_free(cancel_task); // oh well, the task can just finish on its own. + return; + } + + cancel_task->app_userdata = task; + liburing.io_uring_prep_cancel(sqe, task, 0); + liburing.io_uring_sqe_set_data(sqe, cancel_task); + liburing_asyncioqueue_queue_task(userdata, task); + SDL_UnlockMutex(queuedata->sqe_lock); +} + +static SDL_AsyncIOTask *ProcessCQE(LibUringAsyncIOQueueData *queuedata, struct io_uring_cqe *cqe) +{ + if (!cqe) { + return NULL; + } + + SDL_AsyncIOTask *task = (SDL_AsyncIOTask *) io_uring_cqe_get_data(cqe); + if (task) { // can be NULL if this was just a wakeup message, a NOP, etc. + if (!task->queue) { // We leave `queue` blank to signify this was a task cancellation. + SDL_AsyncIOTask *cancel_task = task; + task = (SDL_AsyncIOTask *) cancel_task->app_userdata; + SDL_free(cancel_task); + if (cqe->res >= 0) { // cancel was successful? + task->result = SDL_ASYNCIO_CANCELLED; + } else { + task = NULL; // it already finished or was too far along to cancel, so we'll pick up the actual results later. + } + } else if (cqe->res < 0) { + task->result = SDL_ASYNCIO_FAILURE; + // !!! FIXME: fill in task->error. + } else { + if ((task->type == SDL_ASYNCIO_TASK_WRITE) && (((Uint64) cqe->res) < task->requested_size)) { + task->result = SDL_ASYNCIO_FAILURE; // it's always a failure on short writes. + } + + // don't explicitly mark it as COMPLETE; that's the default value and a linked task might have failed in an earlier operation and this would overwrite it. + + if ((task->type == SDL_ASYNCIO_TASK_READ) || (task->type == SDL_ASYNCIO_TASK_WRITE)) { + task->result_size = (Uint64) cqe->res; + } + } + + if ((task->type == SDL_ASYNCIO_TASK_CLOSE) && task->flush) { + task->flush = false; + task = NULL; // don't return this one, it's a linked task, so it'll arrive in a later CQE. + } + } + + return task; +} + +static SDL_AsyncIOTask *liburing_asyncioqueue_get_results(void *userdata) +{ + LibUringAsyncIOQueueData *queuedata = (LibUringAsyncIOQueueData *) userdata; + + // have to hold a lock because otherwise two threads will get the same cqe until we mark it "seen". Copy and mark it right away, then process further. + SDL_LockMutex(queuedata->cqe_lock); + struct io_uring_cqe *cqe = NULL; + const int rc = liburing.io_uring_peek_cqe(&queuedata->ring, &cqe); + if (rc != 0) { + SDL_assert(rc == -EAGAIN); // should only fail because nothing is available at the moment. + SDL_UnlockMutex(queuedata->cqe_lock); + return NULL; + } + + struct io_uring_cqe cqe_copy; + SDL_copyp(&cqe_copy, cqe); // this is only a few bytes. + liburing.io_uring_cqe_seen(&queuedata->ring, cqe); // let io_uring use this slot again. + SDL_UnlockMutex(queuedata->cqe_lock); + + return ProcessCQE(queuedata, &cqe_copy); +} + +static SDL_AsyncIOTask *liburing_asyncioqueue_wait_results(void *userdata, Sint32 timeoutMS) +{ + LibUringAsyncIOQueueData *queuedata = (LibUringAsyncIOQueueData *) userdata; + struct io_uring_cqe *cqe = NULL; + + SDL_AddAtomicInt(&queuedata->num_waiting, 1); + if (timeoutMS < 0) { + liburing.io_uring_wait_cqe(&queuedata->ring, &cqe); + } else { + struct __kernel_timespec ts = { (Sint64) timeoutMS / SDL_MS_PER_SECOND, (Sint64) SDL_MS_TO_NS(timeoutMS % SDL_MS_PER_SECOND) }; + liburing.io_uring_wait_cqe_timeout(&queuedata->ring, &cqe, &ts); + } + SDL_AddAtomicInt(&queuedata->num_waiting, -1); + + // (we don't care if the wait failed for any reason, as the upcoming peek_cqe will report valid information. We just wanted the wait operation to block.) + + // each thing that peeks or waits for a completion _gets the same cqe_ until we mark it as seen. So when we wake up from the wait, lock the mutex and + // then use peek to make sure we have a unique cqe, and other competing threads either get their own or nothing. + return liburing_asyncioqueue_get_results(userdata); // this just happens to do all those things. +} + +static void liburing_asyncioqueue_signal(void *userdata) +{ + LibUringAsyncIOQueueData *queuedata = (LibUringAsyncIOQueueData *) userdata; + const int num_waiting = SDL_GetAtomicInt(&queuedata->num_waiting); + + SDL_LockMutex(queuedata->sqe_lock); + for (int i = 0; i < num_waiting; i++) { // !!! FIXME: is there a better way to do this than pushing a zero-timeout request for everything waiting? + struct io_uring_sqe *sqe = liburing.io_uring_get_sqe(&queuedata->ring); + if (sqe) { + static struct __kernel_timespec ts; // no wait, just wake a thread as fast as this can land in the completion queue. + liburing.io_uring_prep_timeout(sqe, &ts, 0, 0); + liburing.io_uring_sqe_set_data(sqe, NULL); + } + } + liburing.io_uring_submit(&queuedata->ring); + + SDL_UnlockMutex(queuedata->sqe_lock); +} + +static void liburing_asyncioqueue_destroy(void *userdata) +{ + LibUringAsyncIOQueueData *queuedata = (LibUringAsyncIOQueueData *) userdata; + liburing.io_uring_queue_exit(&queuedata->ring); + SDL_DestroyMutex(queuedata->sqe_lock); + SDL_DestroyMutex(queuedata->cqe_lock); + SDL_free(queuedata); +} + +static bool SDL_SYS_CreateAsyncIOQueue_liburing(SDL_AsyncIOQueue *queue) +{ + LibUringAsyncIOQueueData *queuedata = (LibUringAsyncIOQueueData *) SDL_calloc(1, sizeof (*queuedata)); + if (!queuedata) { + return false; + } + + SDL_SetAtomicInt(&queuedata->num_waiting, 0); + + queuedata->sqe_lock = SDL_CreateMutex(); + if (!queuedata->sqe_lock) { + SDL_free(queuedata); + return false; + } + + queuedata->cqe_lock = SDL_CreateMutex(); + if (!queuedata->cqe_lock) { + SDL_DestroyMutex(queuedata->sqe_lock); + SDL_free(queuedata); + return false; + } + + // !!! FIXME: no idea how large the queue should be. Is 128 overkill or too small? + const int rc = liburing.io_uring_queue_init(128, &queuedata->ring, 0); + if (rc != 0) { + SDL_DestroyMutex(queuedata->sqe_lock); + SDL_DestroyMutex(queuedata->cqe_lock); + SDL_free(queuedata); + return liburing_SetError("io_uring_queue_init", rc); + } + + static const SDL_AsyncIOQueueInterface SDL_AsyncIOQueue_liburing = { + liburing_asyncioqueue_queue_task, + liburing_asyncioqueue_cancel_task, + liburing_asyncioqueue_get_results, + liburing_asyncioqueue_wait_results, + liburing_asyncioqueue_signal, + liburing_asyncioqueue_destroy + }; + + SDL_copyp(&queue->iface, &SDL_AsyncIOQueue_liburing); + queue->userdata = queuedata; + return true; +} + + +static bool liburing_asyncio_read(void *userdata, SDL_AsyncIOTask *task) +{ + LibUringAsyncIOQueueData *queuedata = (LibUringAsyncIOQueueData *) task->queue->userdata; + const int fd = (int) (intptr_t) userdata; + + // !!! FIXME: `unsigned` is likely smaller than requested_size's Uint64. If we overflow it, we could try submitting multiple SQEs + // !!! FIXME: and make a note in the task that there are several in sequence. + if (task->requested_size > ((Uint64) ~((unsigned) 0))) { + return SDL_SetError("io_uring: i/o task is too large"); + } + + // have to hold a lock because otherwise two threads could get_sqe and submit while one request isn't fully set up. + SDL_LockMutex(queuedata->sqe_lock); + bool retval; + struct io_uring_sqe *sqe = liburing.io_uring_get_sqe(&queuedata->ring); + if (!sqe) { + retval = SDL_SetError("io_uring: submission queue is full"); + } else { + liburing.io_uring_prep_read(sqe, fd, task->buffer, (unsigned) task->requested_size, task->offset); + liburing.io_uring_sqe_set_data(sqe, task); + retval = task->queue->iface.queue_task(task->queue->userdata, task); + } + SDL_UnlockMutex(queuedata->sqe_lock); + return retval; +} + +static bool liburing_asyncio_write(void *userdata, SDL_AsyncIOTask *task) +{ + LibUringAsyncIOQueueData *queuedata = (LibUringAsyncIOQueueData *) task->queue->userdata; + const int fd = (int) (intptr_t) userdata; + + // !!! FIXME: `unsigned` is likely smaller than requested_size's Uint64. If we overflow it, we could try submitting multiple SQEs + // !!! FIXME: and make a note in the task that there are several in sequence. + if (task->requested_size > ((Uint64) ~((unsigned) 0))) { + return SDL_SetError("io_uring: i/o task is too large"); + } + + // have to hold a lock because otherwise two threads could get_sqe and submit while one request isn't fully set up. + SDL_LockMutex(queuedata->sqe_lock); + bool retval; + struct io_uring_sqe *sqe = liburing.io_uring_get_sqe(&queuedata->ring); + if (!sqe) { + retval = SDL_SetError("io_uring: submission queue is full"); + } else { + liburing.io_uring_prep_write(sqe, fd, task->buffer, (unsigned) task->requested_size, task->offset); + liburing.io_uring_sqe_set_data(sqe, task); + retval = task->queue->iface.queue_task(task->queue->userdata, task); + } + SDL_UnlockMutex(queuedata->sqe_lock); + return retval; +} + +static bool liburing_asyncio_close(void *userdata, SDL_AsyncIOTask *task) +{ + LibUringAsyncIOQueueData *queuedata = (LibUringAsyncIOQueueData *) task->queue->userdata; + const int fd = (int) (intptr_t) userdata; + + // have to hold a lock because otherwise two threads could get_sqe and submit while one request isn't fully set up. + SDL_LockMutex(queuedata->sqe_lock); + bool retval; + struct io_uring_sqe *sqe = liburing.io_uring_get_sqe(&queuedata->ring); + if (!sqe) { + retval = SDL_SetError("io_uring: submission queue is full"); + } else { + if (task->flush) { + struct io_uring_sqe *flush_sqe = sqe; + sqe = liburing.io_uring_get_sqe(&queuedata->ring); // this will be our actual close task. + if (!sqe) { + liburing.io_uring_prep_nop(flush_sqe); // we already have the first sqe, just make it a NOP. + liburing.io_uring_sqe_set_data(flush_sqe, NULL); + task->queue->iface.queue_task(task->queue->userdata, task); + SDL_UnlockMutex(queuedata->sqe_lock); + return SDL_SetError("io_uring: submission queue is full"); + } + liburing.io_uring_prep_fsync(flush_sqe, fd, IORING_FSYNC_DATASYNC); + liburing.io_uring_sqe_set_data(flush_sqe, task); + liburing.io_uring_sqe_set_flags(flush_sqe, IOSQE_IO_HARDLINK); // must complete before next sqe starts, and next sqe should run even if this fails. + } + + liburing.io_uring_prep_close(sqe, fd); + liburing.io_uring_sqe_set_data(sqe, task); + + retval = task->queue->iface.queue_task(task->queue->userdata, task); + } + SDL_UnlockMutex(queuedata->sqe_lock); + return retval; +} + +static void liburing_asyncio_destroy(void *userdata) +{ + // this is only a Unix file descriptor, should have been closed elsewhere. +} + +static int PosixOpenModeFromString(const char *mode) +{ + // this is exactly the set of strings that SDL_AsyncIOFromFile promises will work. + static const struct { const char *str; int flags; } mappings[] = { + { "rb", O_RDONLY }, + { "wb", O_WRONLY | O_CREAT | O_TRUNC }, + { "r+b", O_RDWR }, + { "w+b", O_RDWR | O_CREAT | O_TRUNC } + }; + + for (int i = 0; i < SDL_arraysize(mappings); i++) { + if (SDL_strcmp(mappings[i].str, mode) == 0) { + return mappings[i].flags; + } + } + + SDL_assert(!"Shouldn't have reached this code"); + return 0; +} + +static bool SDL_SYS_AsyncIOFromFile_liburing(const char *file, const char *mode, SDL_AsyncIO *asyncio) +{ + const int fd = open(file, PosixOpenModeFromString(mode), 0644); + if (fd == -1) { + return SDL_SetError("open failed: %s", strerror(errno)); + } + + static const SDL_AsyncIOInterface SDL_AsyncIOFile_liburing = { + liburing_asyncio_size, + liburing_asyncio_read, + liburing_asyncio_write, + liburing_asyncio_close, + liburing_asyncio_destroy + }; + + SDL_copyp(&asyncio->iface, &SDL_AsyncIOFile_liburing); + asyncio->userdata = (void *) (intptr_t) fd; + return true; +} + +static void SDL_SYS_QuitAsyncIO_liburing(void) +{ + UnloadLibUringLibrary(); +} + +static void MaybeInitializeLibUring(void) +{ + if (SDL_ShouldInit(&liburing_init)) { + if (LoadLibUring()) { + CreateAsyncIOQueue = SDL_SYS_CreateAsyncIOQueue_liburing; + QuitAsyncIO = SDL_SYS_QuitAsyncIO_liburing; + AsyncIOFromFile = SDL_SYS_AsyncIOFromFile_liburing; + } else { // can't use liburing? Use the "generic" threadpool implementation instead. + CreateAsyncIOQueue = SDL_SYS_CreateAsyncIOQueue_Generic; + QuitAsyncIO = SDL_SYS_QuitAsyncIO_Generic; + AsyncIOFromFile = SDL_SYS_AsyncIOFromFile_Generic; + } + SDL_SetInitialized(&liburing_init, true); + } +} + +bool SDL_SYS_CreateAsyncIOQueue(SDL_AsyncIOQueue *queue) +{ + MaybeInitializeLibUring(); + return CreateAsyncIOQueue(queue); +} + +bool SDL_SYS_AsyncIOFromFile(const char *file, const char *mode, SDL_AsyncIO *asyncio) +{ + MaybeInitializeLibUring(); + return AsyncIOFromFile(file, mode, asyncio); +} + +void SDL_SYS_QuitAsyncIO(void) +{ + if (SDL_ShouldQuit(&liburing_init)) { + QuitAsyncIO(); + CreateAsyncIOQueue = NULL; + QuitAsyncIO = NULL; + AsyncIOFromFile = NULL; + SDL_SetInitialized(&liburing_init, false); + } +} + +#endif // defined HAVE_LIBURING_H + diff --git a/libs/SDL3/src/file/windows/SDL_asyncio_windows_ioring.c b/libs/SDL3/src/file/windows/SDL_asyncio_windows_ioring.c new file mode 100644 index 00000000..48ff25f1 --- /dev/null +++ b/libs/SDL3/src/file/windows/SDL_asyncio_windows_ioring.c @@ -0,0 +1,550 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2024 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +// The Windows backend uses IoRing for asynchronous i/o, and falls back to +// the "generic" threadpool implementation if it isn't available or +// fails for some other reason. IoRing was introduced in Windows 11. + +#include "SDL_internal.h" +#include "../SDL_sysasyncio.h" + +#ifdef HAVE_IORINGAPI_H + +#include "../../core/windows/SDL_windows.h" +#include + +// Don't know what the lowest usable version is, but this seems safe. +#define SDL_REQUIRED_IORING_VERSION IORING_VERSION_3 + +static SDL_InitState ioring_init; + +// We could add a whole bootstrap thing like the audio/video/etc subsystems use, but let's keep this simple for now. +static bool (*CreateAsyncIOQueue)(SDL_AsyncIOQueue *queue); +static void (*QuitAsyncIO)(void); +static bool (*AsyncIOFromFile)(const char *file, const char *mode, SDL_AsyncIO *asyncio); + +// we never link directly to ioring. +static const char *ioring_library = "KernelBase.dll"; +static void *ioring_handle = NULL; + +#define SDL_IORING_FUNCS \ + SDL_IORING_FUNC(HRESULT, QueryIoRingCapabilities, (IORING_CAPABILITIES *capabilities)) \ + SDL_IORING_FUNC(BOOL, IsIoRingOpSupported, (HIORING ioRing, IORING_OP_CODE op)) \ + SDL_IORING_FUNC(HRESULT, CreateIoRing, (IORING_VERSION ioringVersion, IORING_CREATE_FLAGS flags, UINT32 submissionQueueSize, UINT32 completionQueueSize, HIORING* h)) \ + SDL_IORING_FUNC(HRESULT, GetIoRingInfo, (HIORING ioRing, IORING_INFO* info)) \ + SDL_IORING_FUNC(HRESULT, SubmitIoRing, (HIORING ioRing, UINT32 waitOperations, UINT32 milliseconds, UINT32* submittedEntries)) \ + SDL_IORING_FUNC(HRESULT, CloseIoRing, (HIORING ioRing)) \ + SDL_IORING_FUNC(HRESULT, PopIoRingCompletion, (HIORING ioRing, IORING_CQE* cqe)) \ + SDL_IORING_FUNC(HRESULT, SetIoRingCompletionEvent, (HIORING ioRing, HANDLE hEvent)) \ + SDL_IORING_FUNC(HRESULT, BuildIoRingCancelRequest, (HIORING ioRing, IORING_HANDLE_REF file, UINT_PTR opToCancel, UINT_PTR userData)) \ + SDL_IORING_FUNC(HRESULT, BuildIoRingReadFile, (HIORING ioRing, IORING_HANDLE_REF fileRef, IORING_BUFFER_REF dataRef, UINT32 numberOfBytesToRead, UINT64 fileOffset, UINT_PTR userData, IORING_SQE_FLAGS sqeFlags)) \ + SDL_IORING_FUNC(HRESULT, BuildIoRingWriteFile, (HIORING ioRing, IORING_HANDLE_REF fileRef, IORING_BUFFER_REF bufferRef, UINT32 numberOfBytesToWrite, UINT64 fileOffset, FILE_WRITE_FLAGS writeFlags, UINT_PTR userData, IORING_SQE_FLAGS sqeFlags)) \ + SDL_IORING_FUNC(HRESULT, BuildIoRingFlushFile, (HIORING ioRing, IORING_HANDLE_REF fileRef, FILE_FLUSH_MODE flushMode, UINT_PTR userData, IORING_SQE_FLAGS sqeFlags)) \ + +#define SDL_IORING_FUNC(ret, fn, args) typedef ret (WINAPI *SDL_fntype_##fn) args; +SDL_IORING_FUNCS +#undef SDL_IORING_FUNC + +typedef struct SDL_WinIoRingFunctions +{ + #define SDL_IORING_FUNC(ret, fn, args) SDL_fntype_##fn fn; + SDL_IORING_FUNCS + #undef SDL_IORING_FUNC +} SDL_WinIoRingFunctions; + +static SDL_WinIoRingFunctions ioring; + + +typedef struct WinIoRingAsyncIOQueueData +{ + SDL_Mutex *sqe_lock; + SDL_Mutex *cqe_lock; + HANDLE event; + HIORING ring; + SDL_AtomicInt num_waiting; +} WinIoRingAsyncIOQueueData; + + +static void UnloadWinIoRingLibrary(void) +{ + if (ioring_library) { + SDL_UnloadObject(ioring_handle); + ioring_library = NULL; + } + SDL_zero(ioring); +} + +static bool LoadWinIoRingSyms(void) +{ + #define SDL_IORING_FUNC(ret, fn, args) { \ + ioring.fn = (SDL_fntype_##fn) SDL_LoadFunction(ioring_handle, #fn); \ + if (!ioring.fn) { \ + return false; \ + } \ + } + SDL_IORING_FUNCS + #undef SDL_IORING_FUNC + return true; +} + +static bool LoadWinIoRing(void) +{ + bool result = true; + + if (!ioring_handle) { + ioring_handle = SDL_LoadObject(ioring_library); + if (!ioring_handle) { + result = false; + // Don't call SDL_SetError(): SDL_LoadObject already did. + } else { + result = LoadWinIoRingSyms(); + if (result) { + IORING_CAPABILITIES caps; + HRESULT hr = ioring.QueryIoRingCapabilities(&caps); + if (FAILED(hr)) { + result = false; + } else if (caps.MaxVersion < SDL_REQUIRED_IORING_VERSION) { + result = false; + } + } + + if (!result) { + UnloadWinIoRingLibrary(); + } + } + } + return result; +} + +static Sint64 ioring_asyncio_size(void *userdata) +{ + HANDLE handle = (HANDLE) userdata; + LARGE_INTEGER size; + if (!GetFileSizeEx(handle, &size)) { + WIN_SetError("GetFileSizeEx"); + return -1; + } + return (Sint64) size.QuadPart; +} + +// you must hold sqe_lock when calling this! +static bool ioring_asyncioqueue_queue_task(void *userdata, SDL_AsyncIOTask *task) +{ + WinIoRingAsyncIOQueueData *queuedata = (WinIoRingAsyncIOQueueData *) userdata; + const HRESULT hr = ioring.SubmitIoRing(queuedata->ring, 0, 0, NULL); + return (FAILED(hr) ? WIN_SetErrorFromHRESULT("SubmitIoRing", hr) : true); +} + +static void ioring_asyncioqueue_cancel_task(void *userdata, SDL_AsyncIOTask *task) +{ + if (!task->asyncio || !task->asyncio->userdata) { + return; // Windows IoRing needs the file handle in question, so we'll just have to let it complete if unknown. + } + + SDL_AsyncIOTask *cancel_task = (SDL_AsyncIOTask *) SDL_calloc(1, sizeof (*cancel_task)); + if (!cancel_task) { + return; // oh well, the task can just finish on its own. + } + + WinIoRingAsyncIOQueueData *queuedata = (WinIoRingAsyncIOQueueData *) userdata; + HANDLE handle = (HANDLE) task->asyncio->userdata; + IORING_HANDLE_REF href = IoRingHandleRefFromHandle(handle); + + // have to hold a lock because otherwise two threads could get_sqe and submit while one request isn't fully set up. + SDL_LockMutex(queuedata->sqe_lock); + const HRESULT hr = ioring.BuildIoRingCancelRequest(queuedata->ring, href, (UINT_PTR) task, (UINT_PTR) cancel_task); + if (FAILED(hr)) { + SDL_UnlockMutex(queuedata->sqe_lock); + SDL_free(cancel_task); // oh well, the task can just finish on its own. + return; + } + + cancel_task->app_userdata = task; + ioring_asyncioqueue_queue_task(userdata, task); + SDL_UnlockMutex(queuedata->sqe_lock); +} + +static SDL_AsyncIOTask *ProcessCQE(WinIoRingAsyncIOQueueData *queuedata, IORING_CQE *cqe) +{ + if (!cqe) { + return NULL; + } + + SDL_AsyncIOTask *task = (SDL_AsyncIOTask *) cqe->UserData; + if (task) { // can be NULL if this was just a wakeup message, a NOP, etc. + if (!task->queue) { // We leave `queue` blank to signify this was a task cancellation. + SDL_AsyncIOTask *cancel_task = task; + task = (SDL_AsyncIOTask *) cancel_task->app_userdata; + SDL_free(cancel_task); + if (SUCCEEDED(cqe->ResultCode)) { // cancel was successful? + task->result = SDL_ASYNCIO_CANCELLED; + } else { + task = NULL; // it already finished or was too far along to cancel, so we'll pick up the actual results later. + } + } else if (FAILED(cqe->ResultCode)) { + task->result = SDL_ASYNCIO_FAILURE; + // !!! FIXME: fill in task->error. + } else { + if ((task->type == SDL_ASYNCIO_TASK_WRITE) && (((Uint64) cqe->Information) < task->requested_size)) { + task->result = SDL_ASYNCIO_FAILURE; // it's always a failure on short writes. + } + + // don't explicitly mark it as COMPLETE; that's the default value and a linked task might have failed in an earlier operation and this would overwrite it. + + if ((task->type == SDL_ASYNCIO_TASK_READ) || (task->type == SDL_ASYNCIO_TASK_WRITE)) { + task->result_size = (Uint64) cqe->Information; + } + } + + // we currently send all close operations through as flushes, requested or not, so the actually closing is (in theory) fast. We do that here. + // if a later IoRing interface version offers an asynchronous close operation, revisit this to only flush if requested, like we do in the Linux io_uring code. + if (task->type == SDL_ASYNCIO_TASK_CLOSE) { + SDL_assert(task->asyncio != NULL); + SDL_assert(task->asyncio->userdata != NULL); + HANDLE handle = (HANDLE) task->asyncio->userdata; + if (!CloseHandle(handle)) { + task->result = SDL_ASYNCIO_FAILURE; // shrug. + } + } + } + + return task; +} + +static SDL_AsyncIOTask *ioring_asyncioqueue_get_results(void *userdata) +{ + WinIoRingAsyncIOQueueData *queuedata = (WinIoRingAsyncIOQueueData *) userdata; + + // unlike liburing's io_uring_peek_cqe(), it's possible PopIoRingCompletion() is thread safe, but for now we wrap it in a mutex just in case. + SDL_LockMutex(queuedata->cqe_lock); + IORING_CQE cqe; + const HRESULT hr = ioring.PopIoRingCompletion(queuedata->ring, &cqe); + SDL_UnlockMutex(queuedata->cqe_lock); + + if ((hr == S_FALSE) || FAILED(hr)) { + return NULL; // nothing available at the moment. + } + + return ProcessCQE(queuedata, &cqe); +} + +static SDL_AsyncIOTask *ioring_asyncioqueue_wait_results(void *userdata, Sint32 timeoutMS) +{ + WinIoRingAsyncIOQueueData *queuedata = (WinIoRingAsyncIOQueueData *) userdata; + + // the event only signals when the IoRing moves from empty to non-empty, so you have to try a (non-blocking) get_results first or risk eternal hangs. + SDL_AsyncIOTask *task = ioring_asyncioqueue_get_results(userdata); + if (!task) { + SDL_AddAtomicInt(&queuedata->num_waiting, 1); + WaitForSingleObject(queuedata->event, (timeoutMS < 0) ? INFINITE : (DWORD) timeoutMS); + SDL_AddAtomicInt(&queuedata->num_waiting, -1); + + // (we don't care if the wait failed for any reason, as the upcoming get_results will report valid information. We just wanted the wait operation to block.) + task = ioring_asyncioqueue_get_results(userdata); + } + + return task; +} + +static void ioring_asyncioqueue_signal(void *userdata) +{ + WinIoRingAsyncIOQueueData *queuedata = (WinIoRingAsyncIOQueueData *) userdata; + const int num_waiting = SDL_GetAtomicInt(&queuedata->num_waiting); + for (int i = 0; i < num_waiting; i++) { + SetEvent(queuedata->event); + } +} + +static void ioring_asyncioqueue_destroy(void *userdata) +{ + WinIoRingAsyncIOQueueData *queuedata = (WinIoRingAsyncIOQueueData *) userdata; + ioring.CloseIoRing(queuedata->ring); + CloseHandle(queuedata->event); + SDL_DestroyMutex(queuedata->sqe_lock); + SDL_DestroyMutex(queuedata->cqe_lock); + SDL_free(queuedata); +} + +static bool SDL_SYS_CreateAsyncIOQueue_ioring(SDL_AsyncIOQueue *queue) +{ + WinIoRingAsyncIOQueueData *queuedata = (WinIoRingAsyncIOQueueData *) SDL_calloc(1, sizeof (*queuedata)); + if (!queuedata) { + return false; + } + + HRESULT hr; + IORING_CREATE_FLAGS flags; + + SDL_SetAtomicInt(&queuedata->num_waiting, 0); + + queuedata->sqe_lock = SDL_CreateMutex(); + if (!queuedata->sqe_lock) { + goto failed; + } + + queuedata->cqe_lock = SDL_CreateMutex(); + if (!queuedata->cqe_lock) { + goto failed; + } + + queuedata->event = CreateEventW(NULL, FALSE, FALSE, NULL); + if (!queuedata->event) { + WIN_SetError("CreateEventW"); + goto failed; + } + + // !!! FIXME: no idea how large the queue should be. Is 128 overkill or too small? + flags.Required = IORING_CREATE_REQUIRED_FLAGS_NONE; + flags.Advisory = IORING_CREATE_ADVISORY_FLAGS_NONE; + hr = ioring.CreateIoRing(SDL_REQUIRED_IORING_VERSION, flags, 128, 128, &queuedata->ring); + if (FAILED(hr)) { + WIN_SetErrorFromHRESULT("CreateIoRing", hr); + goto failed; + } + + hr = ioring.SetIoRingCompletionEvent(queuedata->ring, queuedata->event); + if (FAILED(hr)) { + WIN_SetErrorFromHRESULT("SetIoRingCompletionEvent", hr); + goto failed; + } + + static const IORING_OP_CODE needed_ops[] = { + IORING_OP_NOP, + IORING_OP_FLUSH, + IORING_OP_READ, + IORING_OP_WRITE, + IORING_OP_CANCEL + }; + + for (int i = 0; i < SDL_arraysize(needed_ops); i++) { + if (!ioring.IsIoRingOpSupported(queuedata->ring, needed_ops[i])) { + SDL_SetError("Created IoRing doesn't support op %u", (unsigned int) needed_ops[i]); + goto failed; + } + } + + static const SDL_AsyncIOQueueInterface SDL_AsyncIOQueue_ioring = { + ioring_asyncioqueue_queue_task, + ioring_asyncioqueue_cancel_task, + ioring_asyncioqueue_get_results, + ioring_asyncioqueue_wait_results, + ioring_asyncioqueue_signal, + ioring_asyncioqueue_destroy + }; + + SDL_copyp(&queue->iface, &SDL_AsyncIOQueue_ioring); + queue->userdata = queuedata; + return true; + +failed: + if (queuedata->ring) { + ioring.CloseIoRing(queuedata->ring); + } + if (queuedata->event) { + CloseHandle(queuedata->event); + } + if (queuedata->sqe_lock) { + SDL_DestroyMutex(queuedata->sqe_lock); + } + if (queuedata->cqe_lock) { + SDL_DestroyMutex(queuedata->cqe_lock); + } + SDL_free(queuedata); + return false; +} + +static bool ioring_asyncio_read(void *userdata, SDL_AsyncIOTask *task) +{ + // !!! FIXME: UINT32 smaller than requested_size's Uint64. If we overflow it, we could try submitting multiple SQEs + // !!! FIXME: and make a note in the task that there are several in sequence. + if (task->requested_size > 0xFFFFFFFF) { + return SDL_SetError("ioring: i/o task is too large"); + } + + HANDLE handle = (HANDLE) userdata; + WinIoRingAsyncIOQueueData *queuedata = (WinIoRingAsyncIOQueueData *) task->queue->userdata; + IORING_HANDLE_REF href = IoRingHandleRefFromHandle(handle); + IORING_BUFFER_REF bref = IoRingBufferRefFromPointer(task->buffer); + + // have to hold a lock because otherwise two threads could get_sqe and submit while one request isn't fully set up. + SDL_LockMutex(queuedata->sqe_lock); + bool retval; + const HRESULT hr = ioring.BuildIoRingReadFile(queuedata->ring, href, bref, (UINT32) task->requested_size, task->offset, (UINT_PTR) task, IOSQE_FLAGS_NONE); + if (FAILED(hr)) { + retval = WIN_SetErrorFromHRESULT("BuildIoRingReadFile", hr); + } else { + retval = task->queue->iface.queue_task(task->queue->userdata, task); + } + SDL_UnlockMutex(queuedata->sqe_lock); + return retval; +} + +static bool ioring_asyncio_write(void *userdata, SDL_AsyncIOTask *task) +{ + // !!! FIXME: UINT32 smaller than requested_size's Uint64. If we overflow it, we could try submitting multiple SQEs + // !!! FIXME: and make a note in the task that there are several in sequence. + if (task->requested_size > 0xFFFFFFFF) { + return SDL_SetError("ioring: i/o task is too large"); + } + + HANDLE handle = (HANDLE) userdata; + WinIoRingAsyncIOQueueData *queuedata = (WinIoRingAsyncIOQueueData *) task->queue->userdata; + IORING_HANDLE_REF href = IoRingHandleRefFromHandle(handle); + IORING_BUFFER_REF bref = IoRingBufferRefFromPointer(task->buffer); + + // have to hold a lock because otherwise two threads could get_sqe and submit while one request isn't fully set up. + SDL_LockMutex(queuedata->sqe_lock); + bool retval; + const HRESULT hr = ioring.BuildIoRingWriteFile(queuedata->ring, href, bref, (UINT32) task->requested_size, task->offset, 0 /*FILE_WRITE_FLAGS_NONE*/, (UINT_PTR) task, IOSQE_FLAGS_NONE); + if (FAILED(hr)) { + retval = WIN_SetErrorFromHRESULT("BuildIoRingWriteFile", hr); + } else { + retval = task->queue->iface.queue_task(task->queue->userdata, task); + } + SDL_UnlockMutex(queuedata->sqe_lock); + return retval; +} + +static bool ioring_asyncio_close(void *userdata, SDL_AsyncIOTask *task) +{ + // current IoRing operations don't offer asynchronous closing, but let's assume most of the potential work is flushing to disk, so just do it for everything, explicit flush or not. We'll close when it finishes. + HANDLE handle = (HANDLE) userdata; + WinIoRingAsyncIOQueueData *queuedata = (WinIoRingAsyncIOQueueData *)task->queue->userdata; + IORING_HANDLE_REF href = IoRingHandleRefFromHandle(handle); + + // have to hold a lock because otherwise two threads could get_sqe and submit while one request isn't fully set up. + SDL_LockMutex(queuedata->sqe_lock); + bool retval; + const HRESULT hr = ioring.BuildIoRingFlushFile(queuedata->ring, href, FILE_FLUSH_DEFAULT, (UINT_PTR) task, IOSQE_FLAGS_NONE); + if (FAILED(hr)) { + retval = WIN_SetErrorFromHRESULT("BuildIoRingFlushFile", hr); + } else { + retval = task->queue->iface.queue_task(task->queue->userdata, task); + } + SDL_UnlockMutex(queuedata->sqe_lock); + return retval; +} + +static void ioring_asyncio_destroy(void *userdata) +{ + // this is only a Win32 file HANDLE, should have been closed elsewhere. +} + +static bool Win32OpenModeFromString(const char *mode, DWORD *access_mode, DWORD *create_mode) +{ + // this is exactly the set of strings that SDL_AsyncIOFromFile promises will work. + static const struct { const char *str; DWORD amode; WORD cmode; } mappings[] = { + { "rb", GENERIC_READ, OPEN_EXISTING }, + { "wb", GENERIC_WRITE, CREATE_ALWAYS }, + { "r+b", GENERIC_READ | GENERIC_WRITE, OPEN_EXISTING }, + { "w+b", GENERIC_READ | GENERIC_WRITE, CREATE_ALWAYS } + }; + + for (int i = 0; i < SDL_arraysize(mappings); i++) { + if (SDL_strcmp(mappings[i].str, mode) == 0) { + *access_mode = mappings[i].amode; + *create_mode = mappings[i].cmode; + return true; + } + } + + SDL_assert(!"Shouldn't have reached this code"); + return SDL_SetError("Invalid file open mode"); +} + +static bool SDL_SYS_AsyncIOFromFile_ioring(const char *file, const char *mode, SDL_AsyncIO *asyncio) +{ + DWORD access_mode, create_mode; + if (!Win32OpenModeFromString(mode, &access_mode, &create_mode)) { + return false; + } + + LPWSTR wstr = WIN_UTF8ToStringW(file); + if (!wstr) { + return false; + } + + HANDLE handle = CreateFileW(wstr, access_mode, FILE_SHARE_READ, NULL, create_mode, FILE_ATTRIBUTE_NORMAL, NULL); + SDL_free(wstr); + if (!handle) { + return WIN_SetError("CreateFileW"); + } + + static const SDL_AsyncIOInterface SDL_AsyncIOFile_ioring = { + ioring_asyncio_size, + ioring_asyncio_read, + ioring_asyncio_write, + ioring_asyncio_close, + ioring_asyncio_destroy + }; + + SDL_copyp(&asyncio->iface, &SDL_AsyncIOFile_ioring); + + asyncio->userdata = (void *) handle; + return true; +} + +static void SDL_SYS_QuitAsyncIO_ioring(void) +{ + UnloadWinIoRingLibrary(); +} + +static void MaybeInitializeWinIoRing(void) +{ + if (SDL_ShouldInit(&ioring_init)) { + if (LoadWinIoRing()) { + CreateAsyncIOQueue = SDL_SYS_CreateAsyncIOQueue_ioring; + QuitAsyncIO = SDL_SYS_QuitAsyncIO_ioring; + AsyncIOFromFile = SDL_SYS_AsyncIOFromFile_ioring; + } else { // can't use ioring? Use the "generic" threadpool implementation instead. + CreateAsyncIOQueue = SDL_SYS_CreateAsyncIOQueue_Generic; + QuitAsyncIO = SDL_SYS_QuitAsyncIO_Generic; + AsyncIOFromFile = SDL_SYS_AsyncIOFromFile_Generic; + } + SDL_SetInitialized(&ioring_init, true); + } +} + +bool SDL_SYS_CreateAsyncIOQueue(SDL_AsyncIOQueue *queue) +{ + MaybeInitializeWinIoRing(); + return CreateAsyncIOQueue(queue); +} + +bool SDL_SYS_AsyncIOFromFile(const char *file, const char *mode, SDL_AsyncIO *asyncio) +{ + MaybeInitializeWinIoRing(); + return AsyncIOFromFile(file, mode, asyncio); +} + +void SDL_SYS_QuitAsyncIO(void) +{ + if (SDL_ShouldQuit(&ioring_init)) { + QuitAsyncIO(); + CreateAsyncIOQueue = NULL; + QuitAsyncIO = NULL; + AsyncIOFromFile = NULL; + SDL_SetInitialized(&ioring_init, false); + } +} + +#endif // defined HAVE_IORINGAPI_H + diff --git a/libs/SDL3/src/filesystem/SDL_filesystem.c b/libs/SDL3/src/filesystem/SDL_filesystem.c index d13b6ea8..46cb74b7 100644 --- a/libs/SDL3/src/filesystem/SDL_filesystem.c +++ b/libs/SDL3/src/filesystem/SDL_filesystem.c @@ -495,10 +495,13 @@ const char *SDL_GetUserFolder(SDL_Folder folder) char *SDL_GetPrefPath(const char *org, const char *app) { - char *path = SDL_SYS_GetPrefPath(org, app); - return path; + return SDL_SYS_GetPrefPath(org, app); } +char *SDL_GetCurrentDirectory(void) +{ + return SDL_SYS_GetCurrentDirectory(); +} void SDL_InitFilesystem(void) { diff --git a/libs/SDL3/src/filesystem/SDL_sysfilesystem.h b/libs/SDL3/src/filesystem/SDL_sysfilesystem.h index e0fe9c60..cec031f4 100644 --- a/libs/SDL3/src/filesystem/SDL_sysfilesystem.h +++ b/libs/SDL3/src/filesystem/SDL_sysfilesystem.h @@ -26,6 +26,7 @@ extern char *SDL_SYS_GetBasePath(void); extern char *SDL_SYS_GetPrefPath(const char *org, const char *app); extern char *SDL_SYS_GetUserFolder(SDL_Folder folder); +extern char *SDL_SYS_GetCurrentDirectory(void); extern bool SDL_SYS_EnumerateDirectory(const char *path, const char *dirname, SDL_EnumerateDirectoryCallback cb, void *userdata); extern bool SDL_SYS_RemovePath(const char *path); diff --git a/libs/SDL3/src/filesystem/dummy/SDL_sysfilesystem.c b/libs/SDL3/src/filesystem/dummy/SDL_sysfilesystem.c index 0c74b598..4aebe958 100644 --- a/libs/SDL3/src/filesystem/dummy/SDL_sysfilesystem.c +++ b/libs/SDL3/src/filesystem/dummy/SDL_sysfilesystem.c @@ -45,4 +45,10 @@ char *SDL_SYS_GetUserFolder(SDL_Folder folder) return NULL; } +char *SDL_SYS_GetCurrentDirectory(void) +{ + SDL_Unsupported(); + return NULL; +} + #endif // SDL_FILESYSTEM_DUMMY || SDL_FILESYSTEM_DISABLED diff --git a/libs/SDL3/src/filesystem/gdk/SDL_sysfilesystem.cpp b/libs/SDL3/src/filesystem/gdk/SDL_sysfilesystem.cpp index d2811007..edfee0d4 100644 --- a/libs/SDL3/src/filesystem/gdk/SDL_sysfilesystem.cpp +++ b/libs/SDL3/src/filesystem/gdk/SDL_sysfilesystem.cpp @@ -141,3 +141,10 @@ char *SDL_SYS_GetUserFolder(SDL_Folder folder) SDL_Unsupported(); return NULL; } + +// TODO +char *SDL_SYS_GetCurrentDirectory(void) +{ + SDL_Unsupported(); + return NULL; +} diff --git a/libs/SDL3/src/filesystem/posix/SDL_sysfsops.c b/libs/SDL3/src/filesystem/posix/SDL_sysfsops.c index db197ed0..ec2cc15c 100644 --- a/libs/SDL3/src/filesystem/posix/SDL_sysfsops.c +++ b/libs/SDL3/src/filesystem/posix/SDL_sysfsops.c @@ -33,6 +33,7 @@ #include #include #include +#include bool SDL_SYS_EnumerateDirectory(const char *path, const char *dirname, SDL_EnumerateDirectoryCallback cb, void *userdata) { @@ -185,5 +186,36 @@ bool SDL_SYS_GetPathInfo(const char *path, SDL_PathInfo *info) return true; } +// Note that this isn't actually part of filesystem, not fsops, but everything that uses posix fsops uses this implementation, even with separate filesystem code. +char *SDL_SYS_GetCurrentDirectory(void) +{ + size_t buflen = 64; + char *buf = NULL; + + while (true) { + void *ptr = SDL_realloc(buf, buflen); + if (!ptr) { + SDL_free(buf); + return NULL; + } + buf = (char *) ptr; + + if (getcwd(buf, buflen) != NULL) { + break; // we got it! + } + + if (errno == ERANGE) { + buflen *= 2; // try again with a bigger buffer. + continue; + } + + SDL_free(buf); + SDL_SetError("getcwd failed: %s", strerror(errno)); + return NULL; + } + + return buf; +} + #endif // SDL_FSOPS_POSIX diff --git a/libs/SDL3/src/filesystem/windows/SDL_sysfilesystem.c b/libs/SDL3/src/filesystem/windows/SDL_sysfilesystem.c index 6db77d45..3b627ada 100644 --- a/libs/SDL3/src/filesystem/windows/SDL_sysfilesystem.c +++ b/libs/SDL3/src/filesystem/windows/SDL_sysfilesystem.c @@ -345,4 +345,32 @@ done: } return result; } + +char *SDL_SYS_GetCurrentDirectory(void) +{ + WCHAR *wstr = NULL; + DWORD buflen = 0; + while (true) { + const DWORD bw = GetCurrentDirectoryW(buflen, wstr); + if (bw == 0) { + WIN_SetError("GetCurrentDirectoryW failed"); + return NULL; + } else if (bw < buflen) { + break; // we got it! + } + + void *ptr = SDL_realloc(wstr, bw * sizeof (WCHAR)); + if (!ptr) { + SDL_free(wstr); + return NULL; + } + wstr = (WCHAR *) ptr; + buflen = bw; + } + + char *retval = WIN_StringToUTF8W(wstr); + SDL_free(wstr); + return retval; +} + #endif // SDL_FILESYSTEM_WINDOWS diff --git a/libs/SDL3/src/gpu/SDL_gpu.c b/libs/SDL3/src/gpu/SDL_gpu.c index 6f1e8975..bc1cb1a2 100644 --- a/libs/SDL3/src/gpu/SDL_gpu.c +++ b/libs/SDL3/src/gpu/SDL_gpu.c @@ -166,9 +166,6 @@ static const SDL_GPUBootstrap *backends[] = { #endif #ifdef SDL_GPU_D3D12 &D3D12Driver, -#endif -#ifdef SDL_GPU_D3D11 - &D3D11Driver, #endif NULL }; @@ -250,7 +247,7 @@ SDL_GPUGraphicsPipeline *SDL_GPU_FetchBlitPipeline( BlitPipelineCacheEntry, *blit_pipeline_count + 1, *blit_pipeline_capacity, - *blit_pipeline_capacity * 2) + *blit_pipeline_capacity * 2); (*blit_pipelines)[*blit_pipeline_count].pipeline = pipeline; (*blit_pipelines)[*blit_pipeline_count].type = source_texture_type; @@ -786,6 +783,14 @@ SDL_GPUGraphicsPipeline *SDL_CreateGPUGraphicsPipeline( } if (device->debug_mode) { + if (graphicsPipelineCreateInfo->vertex_shader == NULL) { + SDL_assert_release(!"Vertex shader cannot be NULL!"); + return NULL; + } + if (graphicsPipelineCreateInfo->fragment_shader == NULL) { + SDL_assert_release(!"Fragment shader cannot be NULL!"); + return NULL; + } if (graphicsPipelineCreateInfo->target_info.num_color_targets > 0 && graphicsPipelineCreateInfo->target_info.color_target_descriptions == NULL) { SDL_assert_release(!"Color target descriptions array pointer cannot be NULL if num_color_targets is greater than zero!"); return NULL; @@ -2645,6 +2650,25 @@ bool SDL_SetGPUSwapchainParameters( present_mode); } +bool SDL_SetGPUAllowedFramesInFlight( + SDL_GPUDevice *device, + Uint32 allowed_frames_in_flight) +{ + CHECK_DEVICE_MAGIC(device, false); + + if (device->debug_mode) { + if (allowed_frames_in_flight < 1 || allowed_frames_in_flight > 3) + { + SDL_assert_release(!"allowed_frames_in_flight value must be between 1 and 3!"); + } + } + + allowed_frames_in_flight = SDL_clamp(allowed_frames_in_flight, 1, 3); + return device->SetAllowedFramesInFlight( + device->driverData, + allowed_frames_in_flight); +} + SDL_GPUTextureFormat SDL_GetGPUSwapchainTextureFormat( SDL_GPUDevice *device, SDL_Window *window) @@ -2670,16 +2694,13 @@ bool SDL_AcquireGPUSwapchainTexture( CommandBufferCommonHeader *commandBufferHeader = (CommandBufferCommonHeader *)command_buffer; if (command_buffer == NULL) { - SDL_InvalidParamError("command_buffer"); - return false; + return SDL_InvalidParamError("command_buffer"); } if (window == NULL) { - SDL_InvalidParamError("window"); - return false; + return SDL_InvalidParamError("window"); } if (swapchain_texture == NULL) { - SDL_InvalidParamError("swapchain_texture"); - return false; + return SDL_InvalidParamError("swapchain_texture"); } if (COMMAND_BUFFER_DEVICE->debug_mode) { @@ -2701,6 +2722,59 @@ bool SDL_AcquireGPUSwapchainTexture( return result; } +bool SDL_WaitForGPUSwapchain( + SDL_GPUDevice *device, + SDL_Window *window) +{ + CHECK_DEVICE_MAGIC(device, false); + + if (window == NULL) { + return SDL_InvalidParamError("window"); + } + + return device->WaitForSwapchain( + device->driverData, + window); +} + +bool SDL_WaitAndAcquireGPUSwapchainTexture( + SDL_GPUCommandBuffer *command_buffer, + SDL_Window *window, + SDL_GPUTexture **swapchain_texture, + Uint32 *swapchain_texture_width, + Uint32 *swapchain_texture_height) +{ + CommandBufferCommonHeader *commandBufferHeader = (CommandBufferCommonHeader *)command_buffer; + + if (command_buffer == NULL) { + return SDL_InvalidParamError("command_buffer"); + } + if (window == NULL) { + return SDL_InvalidParamError("window"); + } + if (swapchain_texture == NULL) { + return SDL_InvalidParamError("swapchain_texture"); + } + + if (COMMAND_BUFFER_DEVICE->debug_mode) { + CHECK_COMMAND_BUFFER_RETURN_FALSE + CHECK_ANY_PASS_IN_PROGRESS("Cannot acquire a swapchain texture during a pass!", false) + } + + bool result = COMMAND_BUFFER_DEVICE->WaitAndAcquireSwapchainTexture( + command_buffer, + window, + swapchain_texture, + swapchain_texture_width, + swapchain_texture_height); + + if (*swapchain_texture != NULL){ + commandBufferHeader->swapchain_texture_acquired = true; + } + + return result; +} + bool SDL_SubmitGPUCommandBuffer( SDL_GPUCommandBuffer *command_buffer) { diff --git a/libs/SDL3/src/gpu/SDL_sysgpu.h b/libs/SDL3/src/gpu/SDL_sysgpu.h index c20531f9..901fd9ab 100644 --- a/libs/SDL3/src/gpu/SDL_sysgpu.h +++ b/libs/SDL3/src/gpu/SDL_sysgpu.h @@ -391,12 +391,14 @@ static inline Uint32 BytesPerRow( // Internal Macros #define EXPAND_ARRAY_IF_NEEDED(arr, elementType, newCount, capacity, newCapacity) \ - if (newCount >= capacity) { \ - capacity = newCapacity; \ - arr = (elementType *)SDL_realloc( \ - arr, \ - sizeof(elementType) * capacity); \ - } + do { \ + if ((newCount) >= (capacity)) { \ + (capacity) = (newCapacity); \ + (arr) = (elementType *)SDL_realloc( \ + (arr), \ + sizeof(elementType) * (capacity)); \ + } \ + } while (0) // Internal Declarations @@ -791,6 +793,10 @@ struct SDL_GPUDevice SDL_GPUSwapchainComposition swapchainComposition, SDL_GPUPresentMode presentMode); + bool (*SetAllowedFramesInFlight)( + SDL_GPURenderer *driverData, + Uint32 allowedFramesInFlight); + SDL_GPUTextureFormat (*GetSwapchainTextureFormat)( SDL_GPURenderer *driverData, SDL_Window *window); @@ -805,6 +811,17 @@ struct SDL_GPUDevice Uint32 *swapchainTextureWidth, Uint32 *swapchainTextureHeight); + bool (*WaitForSwapchain)( + SDL_GPURenderer *driverData, + SDL_Window *window); + + bool (*WaitAndAcquireSwapchainTexture)( + SDL_GPUCommandBuffer *commandBuffer, + SDL_Window *window, + SDL_GPUTexture **swapchainTexture, + Uint32 *swapchainTextureWidth, + Uint32 *swapchainTextureHeight); + bool (*Submit)( SDL_GPUCommandBuffer *commandBuffer); @@ -927,9 +944,12 @@ struct SDL_GPUDevice ASSIGN_DRIVER_FUNC(ClaimWindow, name) \ ASSIGN_DRIVER_FUNC(ReleaseWindow, name) \ ASSIGN_DRIVER_FUNC(SetSwapchainParameters, name) \ + ASSIGN_DRIVER_FUNC(SetAllowedFramesInFlight, name) \ ASSIGN_DRIVER_FUNC(GetSwapchainTextureFormat, name) \ ASSIGN_DRIVER_FUNC(AcquireCommandBuffer, name) \ ASSIGN_DRIVER_FUNC(AcquireSwapchainTexture, name) \ + ASSIGN_DRIVER_FUNC(WaitForSwapchain, name) \ + ASSIGN_DRIVER_FUNC(WaitAndAcquireSwapchainTexture, name)\ ASSIGN_DRIVER_FUNC(Submit, name) \ ASSIGN_DRIVER_FUNC(SubmitAndAcquireFence, name) \ ASSIGN_DRIVER_FUNC(Cancel, name) \ @@ -953,7 +973,6 @@ extern "C" { #endif extern SDL_GPUBootstrap VulkanDriver; -extern SDL_GPUBootstrap D3D11Driver; extern SDL_GPUBootstrap D3D12Driver; extern SDL_GPUBootstrap MetalDriver; extern SDL_GPUBootstrap PS5Driver; diff --git a/libs/SDL3/src/gpu/d3d11/D3D11_Blit.h b/libs/SDL3/src/gpu/d3d11/D3D11_Blit.h deleted file mode 100644 index 58800af6..00000000 --- a/libs/SDL3/src/gpu/d3d11/D3D11_Blit.h +++ /dev/null @@ -1,1707 +0,0 @@ -#if 0 -// -// Generated by Microsoft (R) HLSL Shader Compiler 10.1 -// -// -// -// Input signature: -// -// Name Index Mask Register SysValue Format Used -// -------------------- ----- ------ -------- -------- ------- ------ -// SV_VERTEXID 0 x 0 VERTID uint x -// -// -// Output signature: -// -// Name Index Mask Register SysValue Format Used -// -------------------- ----- ------ -------- -------- ------- ------ -// TEXCOORD 0 xy 0 NONE float xy -// SV_POSITION 0 xyzw 1 POS float xyzw -// -vs_5_0 -dcl_globalFlags refactoringAllowed -dcl_input_sgv v0.x, vertex_id -dcl_output o0.xy -dcl_output_siv o1.xyzw, position -dcl_temps 1 -bfi r0.x, l(1), l(1), v0.x, l(0) -and r0.z, v0.x, l(2) -utof r0.xy, r0.xzxx -mov o0.xy, r0.xyxx -mad o1.xy, r0.xyxx, l(2.000000, -2.000000, 0.000000, 0.000000), l(-1.000000, 1.000000, 0.000000, 0.000000) -mov o1.zw, l(0,0,0,1.000000) -ret -// Approximately 7 instruction slots used -#endif - -const BYTE g_FullscreenVert[] = -{ - 68, 88, 66, 67, 35, 105, - 237, 237, 146, 210, 147, 25, - 112, 64, 239, 9, 223, 111, - 229, 225, 1, 0, 0, 0, - 224, 2, 0, 0, 5, 0, - 0, 0, 52, 0, 0, 0, - 160, 0, 0, 0, 212, 0, - 0, 0, 44, 1, 0, 0, - 68, 2, 0, 0, 82, 68, - 69, 70, 100, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 60, 0, 0, 0, 0, 5, - 254, 255, 0, 1, 0, 0, - 60, 0, 0, 0, 82, 68, - 49, 49, 60, 0, 0, 0, - 24, 0, 0, 0, 32, 0, - 0, 0, 40, 0, 0, 0, - 36, 0, 0, 0, 12, 0, - 0, 0, 0, 0, 0, 0, - 77, 105, 99, 114, 111, 115, - 111, 102, 116, 32, 40, 82, - 41, 32, 72, 76, 83, 76, - 32, 83, 104, 97, 100, 101, - 114, 32, 67, 111, 109, 112, - 105, 108, 101, 114, 32, 49, - 48, 46, 49, 0, 73, 83, - 71, 78, 44, 0, 0, 0, - 1, 0, 0, 0, 8, 0, - 0, 0, 32, 0, 0, 0, - 0, 0, 0, 0, 6, 0, - 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 1, - 0, 0, 83, 86, 95, 86, - 69, 82, 84, 69, 88, 73, - 68, 0, 79, 83, 71, 78, - 80, 0, 0, 0, 2, 0, - 0, 0, 8, 0, 0, 0, - 56, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 3, 0, 0, 0, 0, 0, - 0, 0, 3, 12, 0, 0, - 65, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, - 3, 0, 0, 0, 1, 0, - 0, 0, 15, 0, 0, 0, - 84, 69, 88, 67, 79, 79, - 82, 68, 0, 83, 86, 95, - 80, 79, 83, 73, 84, 73, - 79, 78, 0, 171, 171, 171, - 83, 72, 69, 88, 16, 1, - 0, 0, 80, 0, 1, 0, - 68, 0, 0, 0, 106, 8, - 0, 1, 96, 0, 0, 4, - 18, 16, 16, 0, 0, 0, - 0, 0, 6, 0, 0, 0, - 101, 0, 0, 3, 50, 32, - 16, 0, 0, 0, 0, 0, - 103, 0, 0, 4, 242, 32, - 16, 0, 1, 0, 0, 0, - 1, 0, 0, 0, 104, 0, - 0, 2, 1, 0, 0, 0, - 140, 0, 0, 11, 18, 0, - 16, 0, 0, 0, 0, 0, - 1, 64, 0, 0, 1, 0, - 0, 0, 1, 64, 0, 0, - 1, 0, 0, 0, 10, 16, - 16, 0, 0, 0, 0, 0, - 1, 64, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 7, - 66, 0, 16, 0, 0, 0, - 0, 0, 10, 16, 16, 0, - 0, 0, 0, 0, 1, 64, - 0, 0, 2, 0, 0, 0, - 86, 0, 0, 5, 50, 0, - 16, 0, 0, 0, 0, 0, - 134, 0, 16, 0, 0, 0, - 0, 0, 54, 0, 0, 5, - 50, 32, 16, 0, 0, 0, - 0, 0, 70, 0, 16, 0, - 0, 0, 0, 0, 50, 0, - 0, 15, 50, 32, 16, 0, - 1, 0, 0, 0, 70, 0, - 16, 0, 0, 0, 0, 0, - 2, 64, 0, 0, 0, 0, - 0, 64, 0, 0, 0, 192, - 0, 0, 0, 0, 0, 0, - 0, 0, 2, 64, 0, 0, - 0, 0, 128, 191, 0, 0, - 128, 63, 0, 0, 0, 0, - 0, 0, 0, 0, 54, 0, - 0, 8, 194, 32, 16, 0, - 1, 0, 0, 0, 2, 64, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 128, 63, - 62, 0, 0, 1, 83, 84, - 65, 84, 148, 0, 0, 0, - 7, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, - 3, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 2, 0, - 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0 -}; -#if 0 -// -// Generated by Microsoft (R) HLSL Shader Compiler 10.1 -// -// -// Buffer Definitions: -// -// cbuffer SourceRegionBuffer -// { -// -// float2 UVLeftTop; // Offset: 0 Size: 8 -// float2 UVDimensions; // Offset: 8 Size: 8 -// uint MipLevel; // Offset: 16 Size: 4 -// float LayerOrDepth; // Offset: 20 Size: 4 [unused] -// -// } -// -// -// Resource Bindings: -// -// Name Type Format Dim HLSL Bind Count -// ------------------------------ ---------- ------- ----------- -------------- ------ -// SourceSampler sampler NA NA s0 1 -// SourceTexture2D texture float4 2d t0 1 -// SourceRegionBuffer cbuffer NA NA cb0 1 -// -// -// -// Input signature: -// -// Name Index Mask Register SysValue Format Used -// -------------------- ----- ------ -------- -------- ------- ------ -// TEXCOORD 0 xy 0 NONE float xy -// SV_POSITION 0 xyzw 1 POS float -// -// -// Output signature: -// -// Name Index Mask Register SysValue Format Used -// -------------------- ----- ------ -------- -------- ------- ------ -// SV_Target 0 xyzw 0 TARGET float xyzw -// -ps_5_0 -dcl_globalFlags refactoringAllowed -dcl_constantbuffer CB0[2], immediateIndexed -dcl_sampler s0, mode_default -dcl_resource_texture2d (float,float,float,float) t0 -dcl_input_ps linear v0.xy -dcl_output o0.xyzw -dcl_temps 1 -mad r0.xy, cb0[0].zwzz, v0.xyxx, cb0[0].xyxx -utof r0.z, cb0[1].x -sample_l_indexable(texture2d)(float,float,float,float) o0.xyzw, r0.xyxx, t0.xyzw, s0, r0.z -ret -// Approximately 4 instruction slots used -#endif - -const BYTE g_BlitFrom2D[] = -{ - 68, 88, 66, 67, 202, 121, - 86, 209, 195, 28, 22, 216, - 35, 118, 37, 69, 114, 188, - 248, 164, 1, 0, 0, 0, - 160, 4, 0, 0, 5, 0, - 0, 0, 52, 0, 0, 0, - 156, 2, 0, 0, 244, 2, - 0, 0, 40, 3, 0, 0, - 4, 4, 0, 0, 82, 68, - 69, 70, 96, 2, 0, 0, - 1, 0, 0, 0, 208, 0, - 0, 0, 3, 0, 0, 0, - 60, 0, 0, 0, 0, 5, - 255, 255, 0, 1, 0, 0, - 56, 2, 0, 0, 82, 68, - 49, 49, 60, 0, 0, 0, - 24, 0, 0, 0, 32, 0, - 0, 0, 40, 0, 0, 0, - 36, 0, 0, 0, 12, 0, - 0, 0, 0, 0, 0, 0, - 156, 0, 0, 0, 3, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 1, 0, - 0, 0, 170, 0, 0, 0, - 2, 0, 0, 0, 5, 0, - 0, 0, 4, 0, 0, 0, - 255, 255, 255, 255, 0, 0, - 0, 0, 1, 0, 0, 0, - 13, 0, 0, 0, 186, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 0, - 0, 0, 1, 0, 0, 0, - 83, 111, 117, 114, 99, 101, - 83, 97, 109, 112, 108, 101, - 114, 0, 83, 111, 117, 114, - 99, 101, 84, 101, 120, 116, - 117, 114, 101, 50, 68, 0, - 83, 111, 117, 114, 99, 101, - 82, 101, 103, 105, 111, 110, - 66, 117, 102, 102, 101, 114, - 0, 171, 171, 171, 186, 0, - 0, 0, 4, 0, 0, 0, - 232, 0, 0, 0, 32, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 136, 1, - 0, 0, 0, 0, 0, 0, - 8, 0, 0, 0, 2, 0, - 0, 0, 156, 1, 0, 0, - 0, 0, 0, 0, 255, 255, - 255, 255, 0, 0, 0, 0, - 255, 255, 255, 255, 0, 0, - 0, 0, 192, 1, 0, 0, - 8, 0, 0, 0, 8, 0, - 0, 0, 2, 0, 0, 0, - 156, 1, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 255, - 0, 0, 0, 0, 255, 255, - 255, 255, 0, 0, 0, 0, - 205, 1, 0, 0, 16, 0, - 0, 0, 4, 0, 0, 0, - 2, 0, 0, 0, 220, 1, - 0, 0, 0, 0, 0, 0, - 255, 255, 255, 255, 0, 0, - 0, 0, 255, 255, 255, 255, - 0, 0, 0, 0, 0, 2, - 0, 0, 20, 0, 0, 0, - 4, 0, 0, 0, 0, 0, - 0, 0, 20, 2, 0, 0, - 0, 0, 0, 0, 255, 255, - 255, 255, 0, 0, 0, 0, - 255, 255, 255, 255, 0, 0, - 0, 0, 85, 86, 76, 101, - 102, 116, 84, 111, 112, 0, - 102, 108, 111, 97, 116, 50, - 0, 171, 171, 171, 1, 0, - 3, 0, 1, 0, 2, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 146, 1, 0, 0, 85, 86, - 68, 105, 109, 101, 110, 115, - 105, 111, 110, 115, 0, 77, - 105, 112, 76, 101, 118, 101, - 108, 0, 100, 119, 111, 114, - 100, 0, 0, 0, 19, 0, - 1, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 214, 1, - 0, 0, 76, 97, 121, 101, - 114, 79, 114, 68, 101, 112, - 116, 104, 0, 102, 108, 111, - 97, 116, 0, 171, 0, 0, - 3, 0, 1, 0, 1, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 13, 2, 0, 0, 77, 105, - 99, 114, 111, 115, 111, 102, - 116, 32, 40, 82, 41, 32, - 72, 76, 83, 76, 32, 83, - 104, 97, 100, 101, 114, 32, - 67, 111, 109, 112, 105, 108, - 101, 114, 32, 49, 48, 46, - 49, 0, 73, 83, 71, 78, - 80, 0, 0, 0, 2, 0, - 0, 0, 8, 0, 0, 0, - 56, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 3, 0, 0, 0, 0, 0, - 0, 0, 3, 3, 0, 0, - 65, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, - 3, 0, 0, 0, 1, 0, - 0, 0, 15, 0, 0, 0, - 84, 69, 88, 67, 79, 79, - 82, 68, 0, 83, 86, 95, - 80, 79, 83, 73, 84, 73, - 79, 78, 0, 171, 171, 171, - 79, 83, 71, 78, 44, 0, - 0, 0, 1, 0, 0, 0, - 8, 0, 0, 0, 32, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 3, 0, - 0, 0, 0, 0, 0, 0, - 15, 0, 0, 0, 83, 86, - 95, 84, 97, 114, 103, 101, - 116, 0, 171, 171, 83, 72, - 69, 88, 212, 0, 0, 0, - 80, 0, 0, 0, 53, 0, - 0, 0, 106, 8, 0, 1, - 89, 0, 0, 4, 70, 142, - 32, 0, 0, 0, 0, 0, - 2, 0, 0, 0, 90, 0, - 0, 3, 0, 96, 16, 0, - 0, 0, 0, 0, 88, 24, - 0, 4, 0, 112, 16, 0, - 0, 0, 0, 0, 85, 85, - 0, 0, 98, 16, 0, 3, - 50, 16, 16, 0, 0, 0, - 0, 0, 101, 0, 0, 3, - 242, 32, 16, 0, 0, 0, - 0, 0, 104, 0, 0, 2, - 1, 0, 0, 0, 50, 0, - 0, 11, 50, 0, 16, 0, - 0, 0, 0, 0, 230, 138, - 32, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 70, 16, - 16, 0, 0, 0, 0, 0, - 70, 128, 32, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 86, 0, 0, 6, 66, 0, - 16, 0, 0, 0, 0, 0, - 10, 128, 32, 0, 0, 0, - 0, 0, 1, 0, 0, 0, - 72, 0, 0, 141, 194, 0, - 0, 128, 67, 85, 21, 0, - 242, 32, 16, 0, 0, 0, - 0, 0, 70, 0, 16, 0, - 0, 0, 0, 0, 70, 126, - 16, 0, 0, 0, 0, 0, - 0, 96, 16, 0, 0, 0, - 0, 0, 42, 0, 16, 0, - 0, 0, 0, 0, 62, 0, - 0, 1, 83, 84, 65, 84, - 148, 0, 0, 0, 4, 0, - 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 2, 0, - 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0 -}; -#if 0 -// -// Generated by Microsoft (R) HLSL Shader Compiler 10.1 -// -// -// Buffer Definitions: -// -// cbuffer SourceRegionBuffer -// { -// -// float2 UVLeftTop; // Offset: 0 Size: 8 -// float2 UVDimensions; // Offset: 8 Size: 8 -// uint MipLevel; // Offset: 16 Size: 4 -// float LayerOrDepth; // Offset: 20 Size: 4 -// -// } -// -// -// Resource Bindings: -// -// Name Type Format Dim HLSL Bind Count -// ------------------------------ ---------- ------- ----------- -------------- ------ -// SourceSampler sampler NA NA s0 1 -// SourceTexture2DArray texture float4 2darray t0 1 -// SourceRegionBuffer cbuffer NA NA cb0 1 -// -// -// -// Input signature: -// -// Name Index Mask Register SysValue Format Used -// -------------------- ----- ------ -------- -------- ------- ------ -// TEXCOORD 0 xy 0 NONE float xy -// SV_POSITION 0 xyzw 1 POS float -// -// -// Output signature: -// -// Name Index Mask Register SysValue Format Used -// -------------------- ----- ------ -------- -------- ------- ------ -// SV_Target 0 xyzw 0 TARGET float xyzw -// -ps_5_0 -dcl_globalFlags refactoringAllowed -dcl_constantbuffer CB0[2], immediateIndexed -dcl_sampler s0, mode_default -dcl_resource_texture2darray (float,float,float,float) t0 -dcl_input_ps linear v0.xy -dcl_output o0.xyzw -dcl_temps 1 -ftou r0.x, cb0[1].y -utof r0.z, r0.x -mad r0.xy, cb0[0].zwzz, v0.xyxx, cb0[0].xyxx -utof r0.w, cb0[1].x -sample_l_indexable(texture2darray)(float,float,float,float) o0.xyzw, r0.xyzx, t0.xyzw, s0, r0.w -ret -// Approximately 6 instruction slots used -#endif - -const BYTE g_BlitFrom2DArray[] = -{ - 68, 88, 66, 67, 228, 235, - 27, 223, 254, 144, 184, 34, - 12, 203, 116, 115, 17, 233, - 159, 157, 1, 0, 0, 0, - 208, 4, 0, 0, 5, 0, - 0, 0, 52, 0, 0, 0, - 160, 2, 0, 0, 248, 2, - 0, 0, 44, 3, 0, 0, - 52, 4, 0, 0, 82, 68, - 69, 70, 100, 2, 0, 0, - 1, 0, 0, 0, 212, 0, - 0, 0, 3, 0, 0, 0, - 60, 0, 0, 0, 0, 5, - 255, 255, 0, 1, 0, 0, - 60, 2, 0, 0, 82, 68, - 49, 49, 60, 0, 0, 0, - 24, 0, 0, 0, 32, 0, - 0, 0, 40, 0, 0, 0, - 36, 0, 0, 0, 12, 0, - 0, 0, 0, 0, 0, 0, - 156, 0, 0, 0, 3, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 1, 0, - 0, 0, 170, 0, 0, 0, - 2, 0, 0, 0, 5, 0, - 0, 0, 5, 0, 0, 0, - 255, 255, 255, 255, 0, 0, - 0, 0, 1, 0, 0, 0, - 13, 0, 0, 0, 191, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 0, - 0, 0, 1, 0, 0, 0, - 83, 111, 117, 114, 99, 101, - 83, 97, 109, 112, 108, 101, - 114, 0, 83, 111, 117, 114, - 99, 101, 84, 101, 120, 116, - 117, 114, 101, 50, 68, 65, - 114, 114, 97, 121, 0, 83, - 111, 117, 114, 99, 101, 82, - 101, 103, 105, 111, 110, 66, - 117, 102, 102, 101, 114, 0, - 171, 171, 191, 0, 0, 0, - 4, 0, 0, 0, 236, 0, - 0, 0, 32, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 140, 1, 0, 0, - 0, 0, 0, 0, 8, 0, - 0, 0, 2, 0, 0, 0, - 160, 1, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 255, - 0, 0, 0, 0, 255, 255, - 255, 255, 0, 0, 0, 0, - 196, 1, 0, 0, 8, 0, - 0, 0, 8, 0, 0, 0, - 2, 0, 0, 0, 160, 1, - 0, 0, 0, 0, 0, 0, - 255, 255, 255, 255, 0, 0, - 0, 0, 255, 255, 255, 255, - 0, 0, 0, 0, 209, 1, - 0, 0, 16, 0, 0, 0, - 4, 0, 0, 0, 2, 0, - 0, 0, 224, 1, 0, 0, - 0, 0, 0, 0, 255, 255, - 255, 255, 0, 0, 0, 0, - 255, 255, 255, 255, 0, 0, - 0, 0, 4, 2, 0, 0, - 20, 0, 0, 0, 4, 0, - 0, 0, 2, 0, 0, 0, - 24, 2, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 255, - 0, 0, 0, 0, 255, 255, - 255, 255, 0, 0, 0, 0, - 85, 86, 76, 101, 102, 116, - 84, 111, 112, 0, 102, 108, - 111, 97, 116, 50, 0, 171, - 171, 171, 1, 0, 3, 0, - 1, 0, 2, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 150, 1, - 0, 0, 85, 86, 68, 105, - 109, 101, 110, 115, 105, 111, - 110, 115, 0, 77, 105, 112, - 76, 101, 118, 101, 108, 0, - 100, 119, 111, 114, 100, 0, - 0, 0, 19, 0, 1, 0, - 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 218, 1, 0, 0, - 76, 97, 121, 101, 114, 79, - 114, 68, 101, 112, 116, 104, - 0, 102, 108, 111, 97, 116, - 0, 171, 0, 0, 3, 0, - 1, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 17, 2, - 0, 0, 77, 105, 99, 114, - 111, 115, 111, 102, 116, 32, - 40, 82, 41, 32, 72, 76, - 83, 76, 32, 83, 104, 97, - 100, 101, 114, 32, 67, 111, - 109, 112, 105, 108, 101, 114, - 32, 49, 48, 46, 49, 0, - 73, 83, 71, 78, 80, 0, - 0, 0, 2, 0, 0, 0, - 8, 0, 0, 0, 56, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 3, 0, - 0, 0, 0, 0, 0, 0, - 3, 3, 0, 0, 65, 0, - 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 3, 0, - 0, 0, 1, 0, 0, 0, - 15, 0, 0, 0, 84, 69, - 88, 67, 79, 79, 82, 68, - 0, 83, 86, 95, 80, 79, - 83, 73, 84, 73, 79, 78, - 0, 171, 171, 171, 79, 83, - 71, 78, 44, 0, 0, 0, - 1, 0, 0, 0, 8, 0, - 0, 0, 32, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 3, 0, 0, 0, - 0, 0, 0, 0, 15, 0, - 0, 0, 83, 86, 95, 84, - 97, 114, 103, 101, 116, 0, - 171, 171, 83, 72, 69, 88, - 0, 1, 0, 0, 80, 0, - 0, 0, 64, 0, 0, 0, - 106, 8, 0, 1, 89, 0, - 0, 4, 70, 142, 32, 0, - 0, 0, 0, 0, 2, 0, - 0, 0, 90, 0, 0, 3, - 0, 96, 16, 0, 0, 0, - 0, 0, 88, 64, 0, 4, - 0, 112, 16, 0, 0, 0, - 0, 0, 85, 85, 0, 0, - 98, 16, 0, 3, 50, 16, - 16, 0, 0, 0, 0, 0, - 101, 0, 0, 3, 242, 32, - 16, 0, 0, 0, 0, 0, - 104, 0, 0, 2, 1, 0, - 0, 0, 28, 0, 0, 6, - 18, 0, 16, 0, 0, 0, - 0, 0, 26, 128, 32, 0, - 0, 0, 0, 0, 1, 0, - 0, 0, 86, 0, 0, 5, - 66, 0, 16, 0, 0, 0, - 0, 0, 10, 0, 16, 0, - 0, 0, 0, 0, 50, 0, - 0, 11, 50, 0, 16, 0, - 0, 0, 0, 0, 230, 138, - 32, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 70, 16, - 16, 0, 0, 0, 0, 0, - 70, 128, 32, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 86, 0, 0, 6, 130, 0, - 16, 0, 0, 0, 0, 0, - 10, 128, 32, 0, 0, 0, - 0, 0, 1, 0, 0, 0, - 72, 0, 0, 141, 2, 2, - 0, 128, 67, 85, 21, 0, - 242, 32, 16, 0, 0, 0, - 0, 0, 70, 2, 16, 0, - 0, 0, 0, 0, 70, 126, - 16, 0, 0, 0, 0, 0, - 0, 96, 16, 0, 0, 0, - 0, 0, 58, 0, 16, 0, - 0, 0, 0, 0, 62, 0, - 0, 1, 83, 84, 65, 84, - 148, 0, 0, 0, 6, 0, - 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 2, 0, - 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 3, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0 -}; -#if 0 -// -// Generated by Microsoft (R) HLSL Shader Compiler 10.1 -// -// -// Buffer Definitions: -// -// cbuffer SourceRegionBuffer -// { -// -// float2 UVLeftTop; // Offset: 0 Size: 8 -// float2 UVDimensions; // Offset: 8 Size: 8 -// uint MipLevel; // Offset: 16 Size: 4 -// float LayerOrDepth; // Offset: 20 Size: 4 -// -// } -// -// -// Resource Bindings: -// -// Name Type Format Dim HLSL Bind Count -// ------------------------------ ---------- ------- ----------- -------------- ------ -// SourceSampler sampler NA NA s0 1 -// SourceTexture3D texture float4 3d t0 1 -// SourceRegionBuffer cbuffer NA NA cb0 1 -// -// -// -// Input signature: -// -// Name Index Mask Register SysValue Format Used -// -------------------- ----- ------ -------- -------- ------- ------ -// TEXCOORD 0 xy 0 NONE float xy -// SV_POSITION 0 xyzw 1 POS float -// -// -// Output signature: -// -// Name Index Mask Register SysValue Format Used -// -------------------- ----- ------ -------- -------- ------- ------ -// SV_Target 0 xyzw 0 TARGET float xyzw -// -ps_5_0 -dcl_globalFlags refactoringAllowed -dcl_constantbuffer CB0[2], immediateIndexed -dcl_sampler s0, mode_default -dcl_resource_texture3d (float,float,float,float) t0 -dcl_input_ps linear v0.xy -dcl_output o0.xyzw -dcl_temps 1 -mad r0.xy, cb0[0].zwzz, v0.xyxx, cb0[0].xyxx -utof r0.w, cb0[1].x -mov r0.z, cb0[1].y -sample_l_indexable(texture3d)(float,float,float,float) o0.xyzw, r0.xyzx, t0.xyzw, s0, r0.w -ret -// Approximately 5 instruction slots used -#endif - -const BYTE g_BlitFrom3D[] = -{ - 68, 88, 66, 67, 147, 168, - 4, 231, 209, 46, 121, 4, - 107, 179, 12, 172, 196, 130, - 224, 85, 1, 0, 0, 0, - 184, 4, 0, 0, 5, 0, - 0, 0, 52, 0, 0, 0, - 156, 2, 0, 0, 244, 2, - 0, 0, 40, 3, 0, 0, - 28, 4, 0, 0, 82, 68, - 69, 70, 96, 2, 0, 0, - 1, 0, 0, 0, 208, 0, - 0, 0, 3, 0, 0, 0, - 60, 0, 0, 0, 0, 5, - 255, 255, 0, 1, 0, 0, - 56, 2, 0, 0, 82, 68, - 49, 49, 60, 0, 0, 0, - 24, 0, 0, 0, 32, 0, - 0, 0, 40, 0, 0, 0, - 36, 0, 0, 0, 12, 0, - 0, 0, 0, 0, 0, 0, - 156, 0, 0, 0, 3, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 1, 0, - 0, 0, 170, 0, 0, 0, - 2, 0, 0, 0, 5, 0, - 0, 0, 8, 0, 0, 0, - 255, 255, 255, 255, 0, 0, - 0, 0, 1, 0, 0, 0, - 13, 0, 0, 0, 186, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 0, - 0, 0, 1, 0, 0, 0, - 83, 111, 117, 114, 99, 101, - 83, 97, 109, 112, 108, 101, - 114, 0, 83, 111, 117, 114, - 99, 101, 84, 101, 120, 116, - 117, 114, 101, 51, 68, 0, - 83, 111, 117, 114, 99, 101, - 82, 101, 103, 105, 111, 110, - 66, 117, 102, 102, 101, 114, - 0, 171, 171, 171, 186, 0, - 0, 0, 4, 0, 0, 0, - 232, 0, 0, 0, 32, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 136, 1, - 0, 0, 0, 0, 0, 0, - 8, 0, 0, 0, 2, 0, - 0, 0, 156, 1, 0, 0, - 0, 0, 0, 0, 255, 255, - 255, 255, 0, 0, 0, 0, - 255, 255, 255, 255, 0, 0, - 0, 0, 192, 1, 0, 0, - 8, 0, 0, 0, 8, 0, - 0, 0, 2, 0, 0, 0, - 156, 1, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 255, - 0, 0, 0, 0, 255, 255, - 255, 255, 0, 0, 0, 0, - 205, 1, 0, 0, 16, 0, - 0, 0, 4, 0, 0, 0, - 2, 0, 0, 0, 220, 1, - 0, 0, 0, 0, 0, 0, - 255, 255, 255, 255, 0, 0, - 0, 0, 255, 255, 255, 255, - 0, 0, 0, 0, 0, 2, - 0, 0, 20, 0, 0, 0, - 4, 0, 0, 0, 2, 0, - 0, 0, 20, 2, 0, 0, - 0, 0, 0, 0, 255, 255, - 255, 255, 0, 0, 0, 0, - 255, 255, 255, 255, 0, 0, - 0, 0, 85, 86, 76, 101, - 102, 116, 84, 111, 112, 0, - 102, 108, 111, 97, 116, 50, - 0, 171, 171, 171, 1, 0, - 3, 0, 1, 0, 2, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 146, 1, 0, 0, 85, 86, - 68, 105, 109, 101, 110, 115, - 105, 111, 110, 115, 0, 77, - 105, 112, 76, 101, 118, 101, - 108, 0, 100, 119, 111, 114, - 100, 0, 0, 0, 19, 0, - 1, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 214, 1, - 0, 0, 76, 97, 121, 101, - 114, 79, 114, 68, 101, 112, - 116, 104, 0, 102, 108, 111, - 97, 116, 0, 171, 0, 0, - 3, 0, 1, 0, 1, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 13, 2, 0, 0, 77, 105, - 99, 114, 111, 115, 111, 102, - 116, 32, 40, 82, 41, 32, - 72, 76, 83, 76, 32, 83, - 104, 97, 100, 101, 114, 32, - 67, 111, 109, 112, 105, 108, - 101, 114, 32, 49, 48, 46, - 49, 0, 73, 83, 71, 78, - 80, 0, 0, 0, 2, 0, - 0, 0, 8, 0, 0, 0, - 56, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 3, 0, 0, 0, 0, 0, - 0, 0, 3, 3, 0, 0, - 65, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, - 3, 0, 0, 0, 1, 0, - 0, 0, 15, 0, 0, 0, - 84, 69, 88, 67, 79, 79, - 82, 68, 0, 83, 86, 95, - 80, 79, 83, 73, 84, 73, - 79, 78, 0, 171, 171, 171, - 79, 83, 71, 78, 44, 0, - 0, 0, 1, 0, 0, 0, - 8, 0, 0, 0, 32, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 3, 0, - 0, 0, 0, 0, 0, 0, - 15, 0, 0, 0, 83, 86, - 95, 84, 97, 114, 103, 101, - 116, 0, 171, 171, 83, 72, - 69, 88, 236, 0, 0, 0, - 80, 0, 0, 0, 59, 0, - 0, 0, 106, 8, 0, 1, - 89, 0, 0, 4, 70, 142, - 32, 0, 0, 0, 0, 0, - 2, 0, 0, 0, 90, 0, - 0, 3, 0, 96, 16, 0, - 0, 0, 0, 0, 88, 40, - 0, 4, 0, 112, 16, 0, - 0, 0, 0, 0, 85, 85, - 0, 0, 98, 16, 0, 3, - 50, 16, 16, 0, 0, 0, - 0, 0, 101, 0, 0, 3, - 242, 32, 16, 0, 0, 0, - 0, 0, 104, 0, 0, 2, - 1, 0, 0, 0, 50, 0, - 0, 11, 50, 0, 16, 0, - 0, 0, 0, 0, 230, 138, - 32, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 70, 16, - 16, 0, 0, 0, 0, 0, - 70, 128, 32, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 86, 0, 0, 6, 130, 0, - 16, 0, 0, 0, 0, 0, - 10, 128, 32, 0, 0, 0, - 0, 0, 1, 0, 0, 0, - 54, 0, 0, 6, 66, 0, - 16, 0, 0, 0, 0, 0, - 26, 128, 32, 0, 0, 0, - 0, 0, 1, 0, 0, 0, - 72, 0, 0, 141, 66, 1, - 0, 128, 67, 85, 21, 0, - 242, 32, 16, 0, 0, 0, - 0, 0, 70, 2, 16, 0, - 0, 0, 0, 0, 70, 126, - 16, 0, 0, 0, 0, 0, - 0, 96, 16, 0, 0, 0, - 0, 0, 58, 0, 16, 0, - 0, 0, 0, 0, 62, 0, - 0, 1, 83, 84, 65, 84, - 148, 0, 0, 0, 5, 0, - 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 2, 0, - 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0 -}; -#if 0 -// -// Generated by Microsoft (R) HLSL Shader Compiler 10.1 -// -// -// Buffer Definitions: -// -// cbuffer SourceRegionBuffer -// { -// -// float2 UVLeftTop; // Offset: 0 Size: 8 -// float2 UVDimensions; // Offset: 8 Size: 8 -// uint MipLevel; // Offset: 16 Size: 4 -// float LayerOrDepth; // Offset: 20 Size: 4 -// -// } -// -// -// Resource Bindings: -// -// Name Type Format Dim HLSL Bind Count -// ------------------------------ ---------- ------- ----------- -------------- ------ -// SourceSampler sampler NA NA s0 1 -// SourceTextureCube texture float4 cube t0 1 -// SourceRegionBuffer cbuffer NA NA cb0 1 -// -// -// -// Input signature: -// -// Name Index Mask Register SysValue Format Used -// -------------------- ----- ------ -------- -------- ------- ------ -// TEXCOORD 0 xy 0 NONE float xy -// SV_POSITION 0 xyzw 1 POS float -// -// -// Output signature: -// -// Name Index Mask Register SysValue Format Used -// -------------------- ----- ------ -------- -------- ------- ------ -// SV_Target 0 xyzw 0 TARGET float xyzw -// -ps_5_0 -dcl_globalFlags refactoringAllowed -dcl_constantbuffer CB0[2], immediateIndexed -dcl_sampler s0, mode_default -dcl_resource_texturecube (float,float,float,float) t0 -dcl_input_ps linear v0.xy -dcl_output o0.xyzw -dcl_temps 2 -mad r0.xy, cb0[0].zwzz, v0.xyxx, cb0[0].xyxx -mad r0.xz, r0.xxyx, l(2.000000, 0.000000, 2.000000, 0.000000), l(-1.000000, 0.000000, -1.000000, 0.000000) -ftou r1.x, cb0[1].y -switch r1.x - case l(0) - mov r0.yz, -r0.zzxz - mov r0.x, l(1.000000) - break - case l(1) - mov r0.y, l(-1.000000) - mov r0.w, -r0.z - mov r0.xyz, r0.ywxy - break - case l(2) - mul r0.xz, r0.xxzx, l(1.000000, 0.000000, -1.000000, 0.000000) - mov r0.y, l(1.000000) - break - case l(3) - mov r0.y, l(-1.000000) - break - case l(4) - mul r0.xy, r0.xzxx, l(1.000000, -1.000000, 0.000000, 0.000000) - mov r0.z, l(1.000000) - break - case l(5) - mov r0.xy, -r0.xzxx - mov r0.z, l(-1.000000) - break - default - mov r0.xyz, l(0,0,0,0) - break -endswitch -utof r0.w, cb0[1].x -sample_l_indexable(texturecube)(float,float,float,float) o0.xyzw, r0.xyzx, t0.xyzw, s0, r0.w -ret -// Approximately 35 instruction slots used -#endif - -const BYTE g_BlitFromCube[] = -{ - 68, 88, 66, 67, 10, 119, - 223, 12, 113, 233, 111, 65, - 57, 156, 6, 26, 212, 111, - 79, 35, 1, 0, 0, 0, - 176, 6, 0, 0, 5, 0, - 0, 0, 52, 0, 0, 0, - 156, 2, 0, 0, 244, 2, - 0, 0, 40, 3, 0, 0, - 20, 6, 0, 0, 82, 68, - 69, 70, 96, 2, 0, 0, - 1, 0, 0, 0, 208, 0, - 0, 0, 3, 0, 0, 0, - 60, 0, 0, 0, 0, 5, - 255, 255, 0, 1, 0, 0, - 56, 2, 0, 0, 82, 68, - 49, 49, 60, 0, 0, 0, - 24, 0, 0, 0, 32, 0, - 0, 0, 40, 0, 0, 0, - 36, 0, 0, 0, 12, 0, - 0, 0, 0, 0, 0, 0, - 156, 0, 0, 0, 3, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 1, 0, - 0, 0, 170, 0, 0, 0, - 2, 0, 0, 0, 5, 0, - 0, 0, 9, 0, 0, 0, - 255, 255, 255, 255, 0, 0, - 0, 0, 1, 0, 0, 0, - 13, 0, 0, 0, 188, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 0, - 0, 0, 1, 0, 0, 0, - 83, 111, 117, 114, 99, 101, - 83, 97, 109, 112, 108, 101, - 114, 0, 83, 111, 117, 114, - 99, 101, 84, 101, 120, 116, - 117, 114, 101, 67, 117, 98, - 101, 0, 83, 111, 117, 114, - 99, 101, 82, 101, 103, 105, - 111, 110, 66, 117, 102, 102, - 101, 114, 0, 171, 188, 0, - 0, 0, 4, 0, 0, 0, - 232, 0, 0, 0, 32, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 136, 1, - 0, 0, 0, 0, 0, 0, - 8, 0, 0, 0, 2, 0, - 0, 0, 156, 1, 0, 0, - 0, 0, 0, 0, 255, 255, - 255, 255, 0, 0, 0, 0, - 255, 255, 255, 255, 0, 0, - 0, 0, 192, 1, 0, 0, - 8, 0, 0, 0, 8, 0, - 0, 0, 2, 0, 0, 0, - 156, 1, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 255, - 0, 0, 0, 0, 255, 255, - 255, 255, 0, 0, 0, 0, - 205, 1, 0, 0, 16, 0, - 0, 0, 4, 0, 0, 0, - 2, 0, 0, 0, 220, 1, - 0, 0, 0, 0, 0, 0, - 255, 255, 255, 255, 0, 0, - 0, 0, 255, 255, 255, 255, - 0, 0, 0, 0, 0, 2, - 0, 0, 20, 0, 0, 0, - 4, 0, 0, 0, 2, 0, - 0, 0, 20, 2, 0, 0, - 0, 0, 0, 0, 255, 255, - 255, 255, 0, 0, 0, 0, - 255, 255, 255, 255, 0, 0, - 0, 0, 85, 86, 76, 101, - 102, 116, 84, 111, 112, 0, - 102, 108, 111, 97, 116, 50, - 0, 171, 171, 171, 1, 0, - 3, 0, 1, 0, 2, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 146, 1, 0, 0, 85, 86, - 68, 105, 109, 101, 110, 115, - 105, 111, 110, 115, 0, 77, - 105, 112, 76, 101, 118, 101, - 108, 0, 100, 119, 111, 114, - 100, 0, 0, 0, 19, 0, - 1, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 214, 1, - 0, 0, 76, 97, 121, 101, - 114, 79, 114, 68, 101, 112, - 116, 104, 0, 102, 108, 111, - 97, 116, 0, 171, 0, 0, - 3, 0, 1, 0, 1, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 13, 2, 0, 0, 77, 105, - 99, 114, 111, 115, 111, 102, - 116, 32, 40, 82, 41, 32, - 72, 76, 83, 76, 32, 83, - 104, 97, 100, 101, 114, 32, - 67, 111, 109, 112, 105, 108, - 101, 114, 32, 49, 48, 46, - 49, 0, 73, 83, 71, 78, - 80, 0, 0, 0, 2, 0, - 0, 0, 8, 0, 0, 0, - 56, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 3, 0, 0, 0, 0, 0, - 0, 0, 3, 3, 0, 0, - 65, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, - 3, 0, 0, 0, 1, 0, - 0, 0, 15, 0, 0, 0, - 84, 69, 88, 67, 79, 79, - 82, 68, 0, 83, 86, 95, - 80, 79, 83, 73, 84, 73, - 79, 78, 0, 171, 171, 171, - 79, 83, 71, 78, 44, 0, - 0, 0, 1, 0, 0, 0, - 8, 0, 0, 0, 32, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 3, 0, - 0, 0, 0, 0, 0, 0, - 15, 0, 0, 0, 83, 86, - 95, 84, 97, 114, 103, 101, - 116, 0, 171, 171, 83, 72, - 69, 88, 228, 2, 0, 0, - 80, 0, 0, 0, 185, 0, - 0, 0, 106, 8, 0, 1, - 89, 0, 0, 4, 70, 142, - 32, 0, 0, 0, 0, 0, - 2, 0, 0, 0, 90, 0, - 0, 3, 0, 96, 16, 0, - 0, 0, 0, 0, 88, 48, - 0, 4, 0, 112, 16, 0, - 0, 0, 0, 0, 85, 85, - 0, 0, 98, 16, 0, 3, - 50, 16, 16, 0, 0, 0, - 0, 0, 101, 0, 0, 3, - 242, 32, 16, 0, 0, 0, - 0, 0, 104, 0, 0, 2, - 2, 0, 0, 0, 50, 0, - 0, 11, 50, 0, 16, 0, - 0, 0, 0, 0, 230, 138, - 32, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 70, 16, - 16, 0, 0, 0, 0, 0, - 70, 128, 32, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 50, 0, 0, 15, 82, 0, - 16, 0, 0, 0, 0, 0, - 6, 1, 16, 0, 0, 0, - 0, 0, 2, 64, 0, 0, - 0, 0, 0, 64, 0, 0, - 0, 0, 0, 0, 0, 64, - 0, 0, 0, 0, 2, 64, - 0, 0, 0, 0, 128, 191, - 0, 0, 0, 0, 0, 0, - 128, 191, 0, 0, 0, 0, - 28, 0, 0, 6, 18, 0, - 16, 0, 1, 0, 0, 0, - 26, 128, 32, 0, 0, 0, - 0, 0, 1, 0, 0, 0, - 76, 0, 0, 3, 10, 0, - 16, 0, 1, 0, 0, 0, - 6, 0, 0, 3, 1, 64, - 0, 0, 0, 0, 0, 0, - 54, 0, 0, 6, 98, 0, - 16, 0, 0, 0, 0, 0, - 166, 8, 16, 128, 65, 0, - 0, 0, 0, 0, 0, 0, - 54, 0, 0, 5, 18, 0, - 16, 0, 0, 0, 0, 0, - 1, 64, 0, 0, 0, 0, - 128, 63, 2, 0, 0, 1, - 6, 0, 0, 3, 1, 64, - 0, 0, 1, 0, 0, 0, - 54, 0, 0, 5, 34, 0, - 16, 0, 0, 0, 0, 0, - 1, 64, 0, 0, 0, 0, - 128, 191, 54, 0, 0, 6, - 130, 0, 16, 0, 0, 0, - 0, 0, 42, 0, 16, 128, - 65, 0, 0, 0, 0, 0, - 0, 0, 54, 0, 0, 5, - 114, 0, 16, 0, 0, 0, - 0, 0, 214, 4, 16, 0, - 0, 0, 0, 0, 2, 0, - 0, 1, 6, 0, 0, 3, - 1, 64, 0, 0, 2, 0, - 0, 0, 56, 0, 0, 10, - 82, 0, 16, 0, 0, 0, - 0, 0, 6, 2, 16, 0, - 0, 0, 0, 0, 2, 64, - 0, 0, 0, 0, 128, 63, - 0, 0, 0, 0, 0, 0, - 128, 191, 0, 0, 0, 0, - 54, 0, 0, 5, 34, 0, - 16, 0, 0, 0, 0, 0, - 1, 64, 0, 0, 0, 0, - 128, 63, 2, 0, 0, 1, - 6, 0, 0, 3, 1, 64, - 0, 0, 3, 0, 0, 0, - 54, 0, 0, 5, 34, 0, - 16, 0, 0, 0, 0, 0, - 1, 64, 0, 0, 0, 0, - 128, 191, 2, 0, 0, 1, - 6, 0, 0, 3, 1, 64, - 0, 0, 4, 0, 0, 0, - 56, 0, 0, 10, 50, 0, - 16, 0, 0, 0, 0, 0, - 134, 0, 16, 0, 0, 0, - 0, 0, 2, 64, 0, 0, - 0, 0, 128, 63, 0, 0, - 128, 191, 0, 0, 0, 0, - 0, 0, 0, 0, 54, 0, - 0, 5, 66, 0, 16, 0, - 0, 0, 0, 0, 1, 64, - 0, 0, 0, 0, 128, 63, - 2, 0, 0, 1, 6, 0, - 0, 3, 1, 64, 0, 0, - 5, 0, 0, 0, 54, 0, - 0, 6, 50, 0, 16, 0, - 0, 0, 0, 0, 134, 0, - 16, 128, 65, 0, 0, 0, - 0, 0, 0, 0, 54, 0, - 0, 5, 66, 0, 16, 0, - 0, 0, 0, 0, 1, 64, - 0, 0, 0, 0, 128, 191, - 2, 0, 0, 1, 10, 0, - 0, 1, 54, 0, 0, 8, - 114, 0, 16, 0, 0, 0, - 0, 0, 2, 64, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 2, 0, - 0, 1, 23, 0, 0, 1, - 86, 0, 0, 6, 130, 0, - 16, 0, 0, 0, 0, 0, - 10, 128, 32, 0, 0, 0, - 0, 0, 1, 0, 0, 0, - 72, 0, 0, 141, 130, 1, - 0, 128, 67, 85, 21, 0, - 242, 32, 16, 0, 0, 0, - 0, 0, 70, 2, 16, 0, - 0, 0, 0, 0, 70, 126, - 16, 0, 0, 0, 0, 0, - 0, 96, 16, 0, 0, 0, - 0, 0, 58, 0, 16, 0, - 0, 0, 0, 0, 62, 0, - 0, 1, 83, 84, 65, 84, - 148, 0, 0, 0, 35, 0, - 0, 0, 2, 0, 0, 0, - 0, 0, 0, 0, 2, 0, - 0, 0, 7, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 8, 0, 0, 0, - 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 8, 0, 0, 0, - 0, 0, 0, 0, 2, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0 -}; -#if 0 -// -// Generated by Microsoft (R) HLSL Shader Compiler 10.1 -// -// -// Buffer Definitions: -// -// cbuffer SourceRegionBuffer -// { -// -// float2 UVLeftTop; // Offset: 0 Size: 8 -// float2 UVDimensions; // Offset: 8 Size: 8 -// uint MipLevel; // Offset: 16 Size: 4 -// float LayerOrDepth; // Offset: 20 Size: 4 -// -// } -// -// -// Resource Bindings: -// -// Name Type Format Dim HLSL Bind Count -// ------------------------------ ---------- ------- ----------- -------------- ------ -// SourceSampler sampler NA NA s0 1 -// SourceTextureCubeArray texture float4 cubearray t0 1 -// SourceRegionBuffer cbuffer NA NA cb0 1 -// -// -// -// Input signature: -// -// Name Index Mask Register SysValue Format Used -// -------------------- ----- ------ -------- -------- ------- ------ -// TEXCOORD 0 xy 0 NONE float xy -// SV_POSITION 0 xyzw 1 POS float -// -// -// Output signature: -// -// Name Index Mask Register SysValue Format Used -// -------------------- ----- ------ -------- -------- ------- ------ -// SV_Target 0 xyzw 0 TARGET float xyzw -// -ps_5_0 -dcl_globalFlags refactoringAllowed -dcl_constantbuffer CB0[2], immediateIndexed -dcl_sampler s0, mode_default -dcl_resource_texturecubearray (float,float,float,float) t0 -dcl_input_ps linear v0.xy -dcl_output o0.xyzw -dcl_temps 3 -mad r0.xy, cb0[0].zwzz, v0.xyxx, cb0[0].xyxx -mad r0.xz, r0.xxyx, l(2.000000, 0.000000, 2.000000, 0.000000), l(-1.000000, 0.000000, -1.000000, 0.000000) -ftou r1.x, cb0[1].y -udiv r1.x, r2.x, r1.x, l(6) -switch r2.x - case l(0) - mov r0.yz, -r0.zzxz - mov r0.x, l(1.000000) - break - case l(1) - mov r0.y, l(-1.000000) - mov r0.w, -r0.z - mov r0.xyz, r0.ywxy - break - case l(2) - mul r0.xz, r0.xxzx, l(1.000000, 0.000000, -1.000000, 0.000000) - mov r0.y, l(1.000000) - break - case l(3) - mov r0.y, l(-1.000000) - break - case l(4) - mul r0.xy, r0.xzxx, l(1.000000, -1.000000, 0.000000, 0.000000) - mov r0.z, l(1.000000) - break - case l(5) - mov r0.xy, -r0.xzxx - mov r0.z, l(-1.000000) - break -endswitch -utof r0.w, r1.x -utof r1.x, cb0[1].x -sample_l_indexable(texturecubearray)(float,float,float,float) o0.xyzw, r0.xyzw, t0.xyzw, s0, r1.x -ret -// Approximately 34 instruction slots used -#endif - -const BYTE g_BlitFromCubeArray[] = -{ - 68, 88, 66, 67, 59, 147, - 203, 21, 33, 208, 116, 167, - 11, 254, 103, 46, 206, 206, - 176, 66, 1, 0, 0, 0, - 196, 6, 0, 0, 5, 0, - 0, 0, 52, 0, 0, 0, - 160, 2, 0, 0, 248, 2, - 0, 0, 44, 3, 0, 0, - 40, 6, 0, 0, 82, 68, - 69, 70, 100, 2, 0, 0, - 1, 0, 0, 0, 212, 0, - 0, 0, 3, 0, 0, 0, - 60, 0, 0, 0, 0, 5, - 255, 255, 0, 1, 0, 0, - 60, 2, 0, 0, 82, 68, - 49, 49, 60, 0, 0, 0, - 24, 0, 0, 0, 32, 0, - 0, 0, 40, 0, 0, 0, - 36, 0, 0, 0, 12, 0, - 0, 0, 0, 0, 0, 0, - 156, 0, 0, 0, 3, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 1, 0, - 0, 0, 170, 0, 0, 0, - 2, 0, 0, 0, 5, 0, - 0, 0, 10, 0, 0, 0, - 255, 255, 255, 255, 0, 0, - 0, 0, 1, 0, 0, 0, - 13, 0, 0, 0, 193, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 0, - 0, 0, 1, 0, 0, 0, - 83, 111, 117, 114, 99, 101, - 83, 97, 109, 112, 108, 101, - 114, 0, 83, 111, 117, 114, - 99, 101, 84, 101, 120, 116, - 117, 114, 101, 67, 117, 98, - 101, 65, 114, 114, 97, 121, - 0, 83, 111, 117, 114, 99, - 101, 82, 101, 103, 105, 111, - 110, 66, 117, 102, 102, 101, - 114, 0, 193, 0, 0, 0, - 4, 0, 0, 0, 236, 0, - 0, 0, 32, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 140, 1, 0, 0, - 0, 0, 0, 0, 8, 0, - 0, 0, 2, 0, 0, 0, - 160, 1, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 255, - 0, 0, 0, 0, 255, 255, - 255, 255, 0, 0, 0, 0, - 196, 1, 0, 0, 8, 0, - 0, 0, 8, 0, 0, 0, - 2, 0, 0, 0, 160, 1, - 0, 0, 0, 0, 0, 0, - 255, 255, 255, 255, 0, 0, - 0, 0, 255, 255, 255, 255, - 0, 0, 0, 0, 209, 1, - 0, 0, 16, 0, 0, 0, - 4, 0, 0, 0, 2, 0, - 0, 0, 224, 1, 0, 0, - 0, 0, 0, 0, 255, 255, - 255, 255, 0, 0, 0, 0, - 255, 255, 255, 255, 0, 0, - 0, 0, 4, 2, 0, 0, - 20, 0, 0, 0, 4, 0, - 0, 0, 2, 0, 0, 0, - 24, 2, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 255, - 0, 0, 0, 0, 255, 255, - 255, 255, 0, 0, 0, 0, - 85, 86, 76, 101, 102, 116, - 84, 111, 112, 0, 102, 108, - 111, 97, 116, 50, 0, 171, - 171, 171, 1, 0, 3, 0, - 1, 0, 2, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 150, 1, - 0, 0, 85, 86, 68, 105, - 109, 101, 110, 115, 105, 111, - 110, 115, 0, 77, 105, 112, - 76, 101, 118, 101, 108, 0, - 100, 119, 111, 114, 100, 0, - 0, 0, 19, 0, 1, 0, - 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 218, 1, 0, 0, - 76, 97, 121, 101, 114, 79, - 114, 68, 101, 112, 116, 104, - 0, 102, 108, 111, 97, 116, - 0, 171, 0, 0, 3, 0, - 1, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 17, 2, - 0, 0, 77, 105, 99, 114, - 111, 115, 111, 102, 116, 32, - 40, 82, 41, 32, 72, 76, - 83, 76, 32, 83, 104, 97, - 100, 101, 114, 32, 67, 111, - 109, 112, 105, 108, 101, 114, - 32, 49, 48, 46, 49, 0, - 73, 83, 71, 78, 80, 0, - 0, 0, 2, 0, 0, 0, - 8, 0, 0, 0, 56, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 3, 0, - 0, 0, 0, 0, 0, 0, - 3, 3, 0, 0, 65, 0, - 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 3, 0, - 0, 0, 1, 0, 0, 0, - 15, 0, 0, 0, 84, 69, - 88, 67, 79, 79, 82, 68, - 0, 83, 86, 95, 80, 79, - 83, 73, 84, 73, 79, 78, - 0, 171, 171, 171, 79, 83, - 71, 78, 44, 0, 0, 0, - 1, 0, 0, 0, 8, 0, - 0, 0, 32, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 3, 0, 0, 0, - 0, 0, 0, 0, 15, 0, - 0, 0, 83, 86, 95, 84, - 97, 114, 103, 101, 116, 0, - 171, 171, 83, 72, 69, 88, - 244, 2, 0, 0, 80, 0, - 0, 0, 189, 0, 0, 0, - 106, 8, 0, 1, 89, 0, - 0, 4, 70, 142, 32, 0, - 0, 0, 0, 0, 2, 0, - 0, 0, 90, 0, 0, 3, - 0, 96, 16, 0, 0, 0, - 0, 0, 88, 80, 0, 4, - 0, 112, 16, 0, 0, 0, - 0, 0, 85, 85, 0, 0, - 98, 16, 0, 3, 50, 16, - 16, 0, 0, 0, 0, 0, - 101, 0, 0, 3, 242, 32, - 16, 0, 0, 0, 0, 0, - 104, 0, 0, 2, 3, 0, - 0, 0, 50, 0, 0, 11, - 50, 0, 16, 0, 0, 0, - 0, 0, 230, 138, 32, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 70, 16, 16, 0, - 0, 0, 0, 0, 70, 128, - 32, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 50, 0, - 0, 15, 82, 0, 16, 0, - 0, 0, 0, 0, 6, 1, - 16, 0, 0, 0, 0, 0, - 2, 64, 0, 0, 0, 0, - 0, 64, 0, 0, 0, 0, - 0, 0, 0, 64, 0, 0, - 0, 0, 2, 64, 0, 0, - 0, 0, 128, 191, 0, 0, - 0, 0, 0, 0, 128, 191, - 0, 0, 0, 0, 28, 0, - 0, 6, 18, 0, 16, 0, - 1, 0, 0, 0, 26, 128, - 32, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 78, 0, - 0, 9, 18, 0, 16, 0, - 1, 0, 0, 0, 18, 0, - 16, 0, 2, 0, 0, 0, - 10, 0, 16, 0, 1, 0, - 0, 0, 1, 64, 0, 0, - 6, 0, 0, 0, 76, 0, - 0, 3, 10, 0, 16, 0, - 2, 0, 0, 0, 6, 0, - 0, 3, 1, 64, 0, 0, - 0, 0, 0, 0, 54, 0, - 0, 6, 98, 0, 16, 0, - 0, 0, 0, 0, 166, 8, - 16, 128, 65, 0, 0, 0, - 0, 0, 0, 0, 54, 0, - 0, 5, 18, 0, 16, 0, - 0, 0, 0, 0, 1, 64, - 0, 0, 0, 0, 128, 63, - 2, 0, 0, 1, 6, 0, - 0, 3, 1, 64, 0, 0, - 1, 0, 0, 0, 54, 0, - 0, 5, 34, 0, 16, 0, - 0, 0, 0, 0, 1, 64, - 0, 0, 0, 0, 128, 191, - 54, 0, 0, 6, 130, 0, - 16, 0, 0, 0, 0, 0, - 42, 0, 16, 128, 65, 0, - 0, 0, 0, 0, 0, 0, - 54, 0, 0, 5, 114, 0, - 16, 0, 0, 0, 0, 0, - 214, 4, 16, 0, 0, 0, - 0, 0, 2, 0, 0, 1, - 6, 0, 0, 3, 1, 64, - 0, 0, 2, 0, 0, 0, - 56, 0, 0, 10, 82, 0, - 16, 0, 0, 0, 0, 0, - 6, 2, 16, 0, 0, 0, - 0, 0, 2, 64, 0, 0, - 0, 0, 128, 63, 0, 0, - 0, 0, 0, 0, 128, 191, - 0, 0, 0, 0, 54, 0, - 0, 5, 34, 0, 16, 0, - 0, 0, 0, 0, 1, 64, - 0, 0, 0, 0, 128, 63, - 2, 0, 0, 1, 6, 0, - 0, 3, 1, 64, 0, 0, - 3, 0, 0, 0, 54, 0, - 0, 5, 34, 0, 16, 0, - 0, 0, 0, 0, 1, 64, - 0, 0, 0, 0, 128, 191, - 2, 0, 0, 1, 6, 0, - 0, 3, 1, 64, 0, 0, - 4, 0, 0, 0, 56, 0, - 0, 10, 50, 0, 16, 0, - 0, 0, 0, 0, 134, 0, - 16, 0, 0, 0, 0, 0, - 2, 64, 0, 0, 0, 0, - 128, 63, 0, 0, 128, 191, - 0, 0, 0, 0, 0, 0, - 0, 0, 54, 0, 0, 5, - 66, 0, 16, 0, 0, 0, - 0, 0, 1, 64, 0, 0, - 0, 0, 128, 63, 2, 0, - 0, 1, 6, 0, 0, 3, - 1, 64, 0, 0, 5, 0, - 0, 0, 54, 0, 0, 6, - 50, 0, 16, 0, 0, 0, - 0, 0, 134, 0, 16, 128, - 65, 0, 0, 0, 0, 0, - 0, 0, 54, 0, 0, 5, - 66, 0, 16, 0, 0, 0, - 0, 0, 1, 64, 0, 0, - 0, 0, 128, 191, 2, 0, - 0, 1, 23, 0, 0, 1, - 86, 0, 0, 5, 130, 0, - 16, 0, 0, 0, 0, 0, - 10, 0, 16, 0, 1, 0, - 0, 0, 86, 0, 0, 6, - 18, 0, 16, 0, 1, 0, - 0, 0, 10, 128, 32, 0, - 0, 0, 0, 0, 1, 0, - 0, 0, 72, 0, 0, 141, - 130, 2, 0, 128, 67, 85, - 21, 0, 242, 32, 16, 0, - 0, 0, 0, 0, 70, 14, - 16, 0, 0, 0, 0, 0, - 70, 126, 16, 0, 0, 0, - 0, 0, 0, 96, 16, 0, - 0, 0, 0, 0, 10, 0, - 16, 0, 1, 0, 0, 0, - 62, 0, 0, 1, 83, 84, - 65, 84, 148, 0, 0, 0, - 34, 0, 0, 0, 3, 0, - 0, 0, 0, 0, 0, 0, - 2, 0, 0, 0, 7, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 7, 0, - 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 7, 0, - 0, 0, 0, 0, 0, 0, - 3, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0 -}; diff --git a/libs/SDL3/src/gpu/d3d11/SDL_gpu_d3d11.c b/libs/SDL3/src/gpu/d3d11/SDL_gpu_d3d11.c deleted file mode 100644 index f2a864ba..00000000 --- a/libs/SDL3/src/gpu/d3d11/SDL_gpu_d3d11.c +++ /dev/null @@ -1,6534 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2024 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ - -#include "SDL_internal.h" - -#ifdef SDL_GPU_D3D11 - -#define D3D11_NO_HELPERS -#define CINTERFACE -#define COBJMACROS - -#include -#include -#include -#include -#include - -#include "../SDL_sysgpu.h" - -#ifdef __IDXGIInfoQueue_INTERFACE_DEFINED__ -#define HAVE_IDXGIINFOQUEUE -#endif - -// Function Pointer Signatures -typedef HRESULT(WINAPI *PFN_CREATE_DXGI_FACTORY1)(const GUID *riid, void **ppFactory); -typedef HRESULT(WINAPI *PFN_DXGI_GET_DEBUG_INTERFACE)(const GUID *riid, void **ppDebug); - -// IIDs (from https://www.magnumdb.com/) -static const IID D3D_IID_IDXGIFactory1 = { 0x770aae78, 0xf26f, 0x4dba, { 0xa8, 0x29, 0x25, 0x3c, 0x83, 0xd1, 0xb3, 0x87 } }; -static const IID D3D_IID_IDXGIFactory4 = { 0x1bc6ea02, 0xef36, 0x464f, { 0xbf, 0x0c, 0x21, 0xca, 0x39, 0xe5, 0x16, 0x8a } }; -static const IID D3D_IID_IDXGIFactory5 = { 0x7632e1f5, 0xee65, 0x4dca, { 0x87, 0xfd, 0x84, 0xcd, 0x75, 0xf8, 0x83, 0x8d } }; -static const IID D3D_IID_IDXGIFactory6 = { 0xc1b6694f, 0xff09, 0x44a9, { 0xb0, 0x3c, 0x77, 0x90, 0x0a, 0x0a, 0x1d, 0x17 } }; -static const IID D3D_IID_IDXGIAdapter1 = { 0x29038f61, 0x3839, 0x4626, { 0x91, 0xfd, 0x08, 0x68, 0x79, 0x01, 0x1a, 0x05 } }; -static const IID D3D_IID_IDXGISwapChain3 = { 0x94d99bdb, 0xf1f8, 0x4ab0, { 0xb2, 0x36, 0x7d, 0xa0, 0x17, 0x0e, 0xda, 0xb1 } }; -static const IID D3D_IID_ID3D11Texture2D = { 0x6f15aaf2, 0xd208, 0x4e89, { 0x9a, 0xb4, 0x48, 0x95, 0x35, 0xd3, 0x4f, 0x9c } }; -static const IID D3D_IID_ID3DUserDefinedAnnotation = { 0xb2daad8b, 0x03d4, 0x4dbf, { 0x95, 0xeb, 0x32, 0xab, 0x4b, 0x63, 0xd0, 0xab } }; -static const IID D3D_IID_ID3D11Device1 = { 0xa04bfb29, 0x08ef, 0x43d6, { 0xa4, 0x9c, 0xa9, 0xbd, 0xbd, 0xcb, 0xe6, 0x86 } }; -static const IID D3D_IID_IDXGIDebug = { 0x119e7452, 0xde9e, 0x40fe, { 0x88, 0x06, 0x88, 0xf9, 0x0c, 0x12, 0xb4, 0x41 } }; -#ifdef HAVE_IDXGIINFOQUEUE -static const IID D3D_IID_IDXGIInfoQueue = { 0xd67441c7, 0x672a, 0x476f, { 0x9e, 0x82, 0xcd, 0x55, 0xb4, 0x49, 0x49, 0xce } }; -#endif - -static const GUID D3D_IID_D3DDebugObjectName = { 0x429b8c22, 0x9188, 0x4b0c, { 0x87, 0x42, 0xac, 0xb0, 0xbf, 0x85, 0xc2, 0x00 } }; -static const GUID D3D_IID_DXGI_DEBUG_ALL = { 0xe48ae283, 0xda80, 0x490b, { 0x87, 0xe6, 0x43, 0xe9, 0xa9, 0xcf, 0xda, 0x08 } }; - -// Defines - -#if defined(_WIN32) -#define D3D11_DLL "d3d11.dll" -#define DXGI_DLL "dxgi.dll" -#define DXGIDEBUG_DLL "dxgidebug.dll" -#elif defined(__APPLE__) -#define D3D11_DLL "libdxvk_d3d11.0.dylib" -#define DXGI_DLL "libdxvk_dxgi.0.dylib" -#define DXGIDEBUG_DLL "libdxvk_dxgidebug.0.dylib" -#else -#define D3D11_DLL "libdxvk_d3d11.so.0" -#define DXGI_DLL "libdxvk_dxgi.so.0" -#define DXGIDEBUG_DLL "libdxvk_dxgidebug.so.0" -#endif - -#define D3D11_CREATE_DEVICE_FUNC "D3D11CreateDevice" -#define CREATE_DXGI_FACTORY1_FUNC "CreateDXGIFactory1" -#define DXGI_GET_DEBUG_INTERFACE_FUNC "DXGIGetDebugInterface" -#define WINDOW_PROPERTY_DATA "SDL_GPUD3D11WindowPropertyData" - -#define SDL_GPU_SHADERSTAGE_COMPUTE 2 - -#ifdef _WIN32 -#define HRESULT_FMT "(0x%08lX)" -#else -#define HRESULT_FMT "(0x%08X)" -#endif - -// Built-in shaders, compiled with compile_shaders.bat - -#define g_FullscreenVert D3D11_FullscreenVert -#define g_BlitFrom2D D3D11_BlitFrom2D -#define g_BlitFrom2DArray D3D11_BlitFrom2DArray -#define g_BlitFrom3D D3D11_BlitFrom3D -#define g_BlitFromCube D3D11_BlitFromCube -#define g_BlitFromCubeArray D3D11_BlitFromCubeArray -#include "D3D11_Blit.h" -#undef g_FullscreenVert -#undef g_BlitFrom2D -#undef g_BlitFrom2DArray -#undef g_BlitFrom3D -#undef g_BlitFromCube -#undef g_BlitFromCubeArray - -// Macros - -#define SET_ERROR_AND_RETURN(fmt, msg, ret) \ - if (renderer->debugMode) { \ - SDL_LogError(SDL_LOG_CATEGORY_GPU, fmt, msg); \ - } \ - SDL_SetError(fmt, msg); \ - return ret; \ - -#define SET_STRING_ERROR_AND_RETURN(msg, ret) SET_ERROR_AND_RETURN("%s", msg, ret) - -#define CHECK_D3D11_ERROR_AND_RETURN(msg, ret) \ - if (FAILED(res)) { \ - D3D11_INTERNAL_SetError(renderer, msg, res); \ - return ret; \ - } - -#define TRACK_RESOURCE(resource, type, array, count, capacity) \ - Uint32 i; \ - \ - for (i = 0; i < commandBuffer->count; i += 1) { \ - if (commandBuffer->array[i] == resource) { \ - return; \ - } \ - } \ - \ - if (commandBuffer->count == commandBuffer->capacity) { \ - commandBuffer->capacity += 1; \ - commandBuffer->array = SDL_realloc( \ - commandBuffer->array, \ - commandBuffer->capacity * sizeof(type)); \ - } \ - commandBuffer->array[commandBuffer->count] = resource; \ - commandBuffer->count += 1; \ - SDL_AtomicIncRef(&resource->referenceCount); - -// Forward Declarations - -static bool D3D11_Wait(SDL_GPURenderer *driverData); -static void D3D11_ReleaseWindow( - SDL_GPURenderer *driverData, - SDL_Window *window); -static void D3D11_INTERNAL_DestroyBlitPipelines(SDL_GPURenderer *driverData); - -// Conversions - -static SDL_GPUTextureFormat SwapchainCompositionToSDLTextureFormat[] = { - SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM, // SDR - SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM_SRGB, // SDR_SRGB - SDL_GPU_TEXTUREFORMAT_R16G16B16A16_FLOAT, // HDR - SDL_GPU_TEXTUREFORMAT_R10G10B10A2_UNORM, // HDR_ADVANCED -}; - -static DXGI_FORMAT SwapchainCompositionToTextureFormat[] = { - DXGI_FORMAT_B8G8R8A8_UNORM, // SDR - DXGI_FORMAT_B8G8R8A8_UNORM, /* SDR_SRGB */ // NOTE: The RTV uses the sRGB format - DXGI_FORMAT_R16G16B16A16_FLOAT, // HDR - DXGI_FORMAT_R10G10B10A2_UNORM, // HDR_ADVANCED -}; - -static DXGI_COLOR_SPACE_TYPE SwapchainCompositionToColorSpace[] = { - DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709, // SDR - DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709, // SDR_SRGB - DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709, // HDR - DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020 // HDR_ADVANCED -}; - -static DXGI_FORMAT SDLToD3D11_TextureFormat[] = { - DXGI_FORMAT_UNKNOWN, // INVALID - DXGI_FORMAT_A8_UNORM, // A8_UNORM - DXGI_FORMAT_R8_UNORM, // R8_UNORM - DXGI_FORMAT_R8G8_UNORM, // R8G8_UNORM - DXGI_FORMAT_R8G8B8A8_UNORM, // R8G8B8A8_UNORM - DXGI_FORMAT_R16_UNORM, // R16_UNORM - DXGI_FORMAT_R16G16_UNORM, // R16G16_UNORM - DXGI_FORMAT_R16G16B16A16_UNORM, // R16G16B16A16_UNORM - DXGI_FORMAT_R10G10B10A2_UNORM, // R10G10B10A2_UNORM - DXGI_FORMAT_B5G6R5_UNORM, // B5G6R5_UNORM - DXGI_FORMAT_B5G5R5A1_UNORM, // B5G5R5A1_UNORM - DXGI_FORMAT_B4G4R4A4_UNORM, // B4G4R4A4_UNORM - DXGI_FORMAT_B8G8R8A8_UNORM, // B8G8R8A8_UNORM - DXGI_FORMAT_BC1_UNORM, // BC1_UNORM - DXGI_FORMAT_BC2_UNORM, // BC2_UNORM - DXGI_FORMAT_BC3_UNORM, // BC3_UNORM - DXGI_FORMAT_BC4_UNORM, // BC4_UNORM - DXGI_FORMAT_BC5_UNORM, // BC5_UNORM - DXGI_FORMAT_BC7_UNORM, // BC7_UNORM - DXGI_FORMAT_BC6H_SF16, // BC6H_FLOAT - DXGI_FORMAT_BC6H_UF16, // BC6H_UFLOAT - DXGI_FORMAT_R8_SNORM, // R8_SNORM - DXGI_FORMAT_R8G8_SNORM, // R8G8_SNORM - DXGI_FORMAT_R8G8B8A8_SNORM, // R8G8B8A8_SNORM - DXGI_FORMAT_R16_SNORM, // R16_SNORM - DXGI_FORMAT_R16G16_SNORM, // R16G16_SNORM - DXGI_FORMAT_R16G16B16A16_SNORM, // R16G16B16A16_SNORM - DXGI_FORMAT_R16_FLOAT, // R16_FLOAT - DXGI_FORMAT_R16G16_FLOAT, // R16G16_FLOAT - DXGI_FORMAT_R16G16B16A16_FLOAT, // R16G16B16A16_FLOAT - DXGI_FORMAT_R32_FLOAT, // R32_FLOAT - DXGI_FORMAT_R32G32_FLOAT, // R32G32_FLOAT - DXGI_FORMAT_R32G32B32A32_FLOAT, // R32G32B32A32_FLOAT - DXGI_FORMAT_R11G11B10_FLOAT, // R11G11B10_UFLOAT - DXGI_FORMAT_R8_UINT, // R8_UINT - DXGI_FORMAT_R8G8_UINT, // R8G8_UINT - DXGI_FORMAT_R8G8B8A8_UINT, // R8G8B8A8_UINT - DXGI_FORMAT_R16_UINT, // R16_UINT - DXGI_FORMAT_R16G16_UINT, // R16G16_UINT - DXGI_FORMAT_R16G16B16A16_UINT, // R16G16B16A16_UINT - DXGI_FORMAT_R32_UINT, // R32_UINT - DXGI_FORMAT_R32G32_UINT, // R32G32_UINT - DXGI_FORMAT_R32G32B32A32_UINT, // R32G32B32A32_UINT - DXGI_FORMAT_R8_SINT, // R8_INT - DXGI_FORMAT_R8G8_SINT, // R8G8_INT - DXGI_FORMAT_R8G8B8A8_SINT, // R8G8B8A8_INT - DXGI_FORMAT_R16_SINT, // R16_INT - DXGI_FORMAT_R16G16_SINT, // R16G16_INT - DXGI_FORMAT_R16G16B16A16_SINT, // R16G16B16A16_INT - DXGI_FORMAT_R32_SINT, // R32_INT - DXGI_FORMAT_R32G32_SINT, // R32G32_INT - DXGI_FORMAT_R32G32B32A32_SINT, // R32G32B32A32_INT - DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, // R8G8B8A8_UNORM_SRGB - DXGI_FORMAT_B8G8R8A8_UNORM_SRGB, // B8G8R8A8_UNORM_SRGB - DXGI_FORMAT_BC1_UNORM_SRGB, // BC1_UNORM_SRGB - DXGI_FORMAT_BC2_UNORM_SRGB, // BC2_UNORM_SRGB - DXGI_FORMAT_BC3_UNORM_SRGB, // BC3_UNORM_SRGB - DXGI_FORMAT_BC7_UNORM_SRGB, // BC7_UNORM_SRGB - DXGI_FORMAT_D16_UNORM, // D16_UNORM - DXGI_FORMAT_D24_UNORM_S8_UINT, // D24_UNORM - DXGI_FORMAT_D32_FLOAT, // D32_FLOAT - DXGI_FORMAT_D24_UNORM_S8_UINT, // D24_UNORM_S8_UINT - DXGI_FORMAT_D32_FLOAT_S8X24_UINT, // D32_FLOAT_S8_UINT - DXGI_FORMAT_UNKNOWN, // ASTC_4x4_UNORM - DXGI_FORMAT_UNKNOWN, // ASTC_5x4_UNORM - DXGI_FORMAT_UNKNOWN, // ASTC_5x5_UNORM - DXGI_FORMAT_UNKNOWN, // ASTC_6x5_UNORM - DXGI_FORMAT_UNKNOWN, // ASTC_6x6_UNORM - DXGI_FORMAT_UNKNOWN, // ASTC_8x5_UNORM - DXGI_FORMAT_UNKNOWN, // ASTC_8x6_UNORM - DXGI_FORMAT_UNKNOWN, // ASTC_8x8_UNORM - DXGI_FORMAT_UNKNOWN, // ASTC_10x5_UNORM - DXGI_FORMAT_UNKNOWN, // ASTC_10x6_UNORM - DXGI_FORMAT_UNKNOWN, // ASTC_10x8_UNORM - DXGI_FORMAT_UNKNOWN, // ASTC_10x10_UNORM - DXGI_FORMAT_UNKNOWN, // ASTC_12x10_UNORM - DXGI_FORMAT_UNKNOWN, // ASTC_12x12_UNORM - DXGI_FORMAT_UNKNOWN, // ASTC_4x4_UNORM_SRGB - DXGI_FORMAT_UNKNOWN, // ASTC_5x4_UNORM_SRGB - DXGI_FORMAT_UNKNOWN, // ASTC_5x5_UNORM_SRGB - DXGI_FORMAT_UNKNOWN, // ASTC_6x5_UNORM_SRGB - DXGI_FORMAT_UNKNOWN, // ASTC_6x6_UNORM_SRGB - DXGI_FORMAT_UNKNOWN, // ASTC_8x5_UNORM_SRGB - DXGI_FORMAT_UNKNOWN, // ASTC_8x6_UNORM_SRGB - DXGI_FORMAT_UNKNOWN, // ASTC_8x8_UNORM_SRGB - DXGI_FORMAT_UNKNOWN, // ASTC_10x5_UNORM_SRGB - DXGI_FORMAT_UNKNOWN, // ASTC_10x6_UNORM_SRGB - DXGI_FORMAT_UNKNOWN, // ASTC_10x8_UNORM_SRGB - DXGI_FORMAT_UNKNOWN, // ASTC_10x10_UNORM_SRGB - DXGI_FORMAT_UNKNOWN, // ASTC_12x10_UNORM_SRGB - DXGI_FORMAT_UNKNOWN, // ASTC_12x12_UNORM_SRGB - DXGI_FORMAT_UNKNOWN, // ASTC_4x4_FLOAT - DXGI_FORMAT_UNKNOWN, // ASTC_5x4_FLOAT - DXGI_FORMAT_UNKNOWN, // ASTC_5x5_FLOAT - DXGI_FORMAT_UNKNOWN, // ASTC_6x5_FLOAT - DXGI_FORMAT_UNKNOWN, // ASTC_6x6_FLOAT - DXGI_FORMAT_UNKNOWN, // ASTC_8x5_FLOAT - DXGI_FORMAT_UNKNOWN, // ASTC_8x6_FLOAT - DXGI_FORMAT_UNKNOWN, // ASTC_8x8_FLOAT - DXGI_FORMAT_UNKNOWN, // ASTC_10x5_FLOAT - DXGI_FORMAT_UNKNOWN, // ASTC_10x6_FLOAT - DXGI_FORMAT_UNKNOWN, // ASTC_10x8_FLOAT - DXGI_FORMAT_UNKNOWN, // ASTC_10x10_FLOAT - DXGI_FORMAT_UNKNOWN, // ASTC_12x10_FLOAT - DXGI_FORMAT_UNKNOWN, // ASTC_12x12_FLOAT -}; -SDL_COMPILE_TIME_ASSERT(SDLToD3D11_TextureFormat, SDL_arraysize(SDLToD3D11_TextureFormat) == SDL_GPU_TEXTUREFORMAT_MAX_ENUM_VALUE); - -static DXGI_FORMAT SDLToD3D11_VertexFormat[] = { - DXGI_FORMAT_UNKNOWN, // INVALID - DXGI_FORMAT_R32_SINT, // INT - DXGI_FORMAT_R32G32_SINT, // INT2 - DXGI_FORMAT_R32G32B32_SINT, // INT3 - DXGI_FORMAT_R32G32B32A32_SINT, // INT4 - DXGI_FORMAT_R32_UINT, // UINT - DXGI_FORMAT_R32G32_UINT, // UINT2 - DXGI_FORMAT_R32G32B32_UINT, // UINT3 - DXGI_FORMAT_R32G32B32A32_UINT, // UINT4 - DXGI_FORMAT_R32_FLOAT, // FLOAT - DXGI_FORMAT_R32G32_FLOAT, // FLOAT2 - DXGI_FORMAT_R32G32B32_FLOAT, // FLOAT3 - DXGI_FORMAT_R32G32B32A32_FLOAT, // FLOAT4 - DXGI_FORMAT_R8G8_SINT, // BYTE2 - DXGI_FORMAT_R8G8B8A8_SINT, // BYTE4 - DXGI_FORMAT_R8G8_UINT, // UBYTE2 - DXGI_FORMAT_R8G8B8A8_UINT, // UBYTE4 - DXGI_FORMAT_R8G8_SNORM, // BYTE2_NORM - DXGI_FORMAT_R8G8B8A8_SNORM, // BYTE4_NORM - DXGI_FORMAT_R8G8_UNORM, // UBYTE2_NORM - DXGI_FORMAT_R8G8B8A8_UNORM, // UBYTE4_NORM - DXGI_FORMAT_R16G16_SINT, // SHORT2 - DXGI_FORMAT_R16G16B16A16_SINT, // SHORT4 - DXGI_FORMAT_R16G16_UINT, // USHORT2 - DXGI_FORMAT_R16G16B16A16_UINT, // USHORT4 - DXGI_FORMAT_R16G16_SNORM, // SHORT2_NORM - DXGI_FORMAT_R16G16B16A16_SNORM, // SHORT4_NORM - DXGI_FORMAT_R16G16_UNORM, // USHORT2_NORM - DXGI_FORMAT_R16G16B16A16_UNORM, // USHORT4_NORM - DXGI_FORMAT_R16G16_FLOAT, // HALF2 - DXGI_FORMAT_R16G16B16A16_FLOAT // HALF4 -}; -SDL_COMPILE_TIME_ASSERT(SDLToD3D11_VertexFormat, SDL_arraysize(SDLToD3D11_VertexFormat) == SDL_GPU_VERTEXELEMENTFORMAT_MAX_ENUM_VALUE); - -static Uint32 SDLToD3D11_SampleCount[] = { - 1, // SDL_GPU_SAMPLECOUNT_1 - 2, // SDL_GPU_SAMPLECOUNT_2 - 4, // SDL_GPU_SAMPLECOUNT_4 - 8 // SDL_GPU_SAMPLECOUNT_8 -}; - -static DXGI_FORMAT SDLToD3D11_IndexType[] = { - DXGI_FORMAT_R16_UINT, // 16BIT - DXGI_FORMAT_R32_UINT // 32BIT -}; - -static D3D11_PRIMITIVE_TOPOLOGY SDLToD3D11_PrimitiveType[] = { - D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST, // TRIANGLELIST - D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, // TRIANGLESTRIP - D3D_PRIMITIVE_TOPOLOGY_LINELIST, // LINELIST - D3D_PRIMITIVE_TOPOLOGY_LINESTRIP, // LINESTRIP - D3D_PRIMITIVE_TOPOLOGY_POINTLIST // POINTLIST -}; - -static D3D11_CULL_MODE SDLToD3D11_CullMode[] = { - D3D11_CULL_NONE, // NONE - D3D11_CULL_FRONT, // FRONT - D3D11_CULL_BACK // BACK -}; - -static D3D11_BLEND SDLToD3D11_BlendFactor[] = { - D3D11_BLEND_ZERO, // INVALID - D3D11_BLEND_ZERO, // ZERO - D3D11_BLEND_ONE, // ONE - D3D11_BLEND_SRC_COLOR, // SRC_COLOR - D3D11_BLEND_INV_SRC_COLOR, // ONE_MINUS_SRC_COLOR - D3D11_BLEND_DEST_COLOR, // DST_COLOR - D3D11_BLEND_INV_DEST_COLOR, // ONE_MINUS_DST_COLOR - D3D11_BLEND_SRC_ALPHA, // SRC_ALPHA - D3D11_BLEND_INV_SRC_ALPHA, // ONE_MINUS_SRC_ALPHA - D3D11_BLEND_DEST_ALPHA, // DST_ALPHA - D3D11_BLEND_INV_DEST_ALPHA, // ONE_MINUS_DST_ALPHA - D3D11_BLEND_BLEND_FACTOR, // CONSTANT_COLOR - D3D11_BLEND_INV_BLEND_FACTOR, // ONE_MINUS_CONSTANT_COLOR - D3D11_BLEND_SRC_ALPHA_SAT, // SRC_ALPHA_SATURATE -}; -SDL_COMPILE_TIME_ASSERT(SDLToD3D11_BlendFactor, SDL_arraysize(SDLToD3D11_BlendFactor) == SDL_GPU_BLENDFACTOR_MAX_ENUM_VALUE); - -static D3D11_BLEND SDLToD3D11_BlendFactorAlpha[] = { - D3D11_BLEND_ZERO, // ALPHA - D3D11_BLEND_ZERO, // ZERO - D3D11_BLEND_ONE, // ONE - D3D11_BLEND_SRC_ALPHA, // SRC_COLOR - D3D11_BLEND_INV_SRC_ALPHA, // ONE_MINUS_SRC_COLOR - D3D11_BLEND_DEST_ALPHA, // DST_COLOR - D3D11_BLEND_INV_DEST_ALPHA, // ONE_MINUS_DST_COLOR - D3D11_BLEND_SRC_ALPHA, // SRC_ALPHA - D3D11_BLEND_INV_SRC_ALPHA, // ONE_MINUS_SRC_ALPHA - D3D11_BLEND_DEST_ALPHA, // DST_ALPHA - D3D11_BLEND_INV_DEST_ALPHA, // ONE_MINUS_DST_ALPHA - D3D11_BLEND_BLEND_FACTOR, // CONSTANT_COLOR - D3D11_BLEND_INV_BLEND_FACTOR, // ONE_MINUS_CONSTANT_COLOR - D3D11_BLEND_SRC_ALPHA_SAT, // SRC_ALPHA_SATURATE -}; -SDL_COMPILE_TIME_ASSERT(SDLToD3D11_BlendFactorAlpha, SDL_arraysize(SDLToD3D11_BlendFactorAlpha) == SDL_GPU_BLENDFACTOR_MAX_ENUM_VALUE); - -static D3D11_BLEND_OP SDLToD3D11_BlendOp[] = { - D3D11_BLEND_OP_ADD, // INVALID - D3D11_BLEND_OP_ADD, // ADD - D3D11_BLEND_OP_SUBTRACT, // SUBTRACT - D3D11_BLEND_OP_REV_SUBTRACT, // REVERSE_SUBTRACT - D3D11_BLEND_OP_MIN, // MIN - D3D11_BLEND_OP_MAX // MAX -}; -SDL_COMPILE_TIME_ASSERT(SDLToD3D11_BlendOp, SDL_arraysize(SDLToD3D11_BlendOp) == SDL_GPU_BLENDOP_MAX_ENUM_VALUE); - -static D3D11_COMPARISON_FUNC SDLToD3D11_CompareOp[] = { - D3D11_COMPARISON_NEVER, // INVALID - D3D11_COMPARISON_NEVER, // NEVER - D3D11_COMPARISON_LESS, // LESS - D3D11_COMPARISON_EQUAL, // EQUAL - D3D11_COMPARISON_LESS_EQUAL, // LESS_OR_EQUAL - D3D11_COMPARISON_GREATER, // GREATER - D3D11_COMPARISON_NOT_EQUAL, // NOT_EQUAL - D3D11_COMPARISON_GREATER_EQUAL, // GREATER_OR_EQUAL - D3D11_COMPARISON_ALWAYS // ALWAYS -}; -SDL_COMPILE_TIME_ASSERT(SDLToD3D11_CompareOp, SDL_arraysize(SDLToD3D11_CompareOp) == SDL_GPU_COMPAREOP_MAX_ENUM_VALUE); - -static D3D11_STENCIL_OP SDLToD3D11_StencilOp[] = { - D3D11_STENCIL_OP_KEEP, // INVALID - D3D11_STENCIL_OP_KEEP, // KEEP - D3D11_STENCIL_OP_ZERO, // ZERO - D3D11_STENCIL_OP_REPLACE, // REPLACE - D3D11_STENCIL_OP_INCR_SAT, // INCREMENT_AND_CLAMP - D3D11_STENCIL_OP_DECR_SAT, // DECREMENT_AND_CLAMP - D3D11_STENCIL_OP_INVERT, // INVERT - D3D11_STENCIL_OP_INCR, // INCREMENT_AND_WRAP - D3D11_STENCIL_OP_DECR // DECREMENT_AND_WRAP -}; -SDL_COMPILE_TIME_ASSERT(SDLToD3D11_StencilOp, SDL_arraysize(SDLToD3D11_StencilOp) == SDL_GPU_STENCILOP_MAX_ENUM_VALUE); - -static D3D11_INPUT_CLASSIFICATION SDLToD3D11_VertexInputRate[] = { - D3D11_INPUT_PER_VERTEX_DATA, // VERTEX - D3D11_INPUT_PER_INSTANCE_DATA // INSTANCE -}; - -static D3D11_TEXTURE_ADDRESS_MODE SDLToD3D11_SamplerAddressMode[] = { - D3D11_TEXTURE_ADDRESS_WRAP, // REPEAT - D3D11_TEXTURE_ADDRESS_MIRROR, // MIRRORED_REPEAT - D3D11_TEXTURE_ADDRESS_CLAMP // CLAMP_TO_EDGE -}; - -static D3D11_FILTER SDLToD3D11_Filter(const SDL_GPUSamplerCreateInfo *createInfo) -{ - if (createInfo->min_filter == SDL_GPU_FILTER_LINEAR) { - if (createInfo->mag_filter == SDL_GPU_FILTER_LINEAR) { - if (createInfo->mipmap_mode == SDL_GPU_SAMPLERMIPMAPMODE_LINEAR) { - return D3D11_FILTER_MIN_MAG_MIP_LINEAR; - } else { - return D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT; - } - } else { - if (createInfo->mipmap_mode == SDL_GPU_SAMPLERMIPMAPMODE_LINEAR) { - return D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR; - } else { - return D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT; - } - } - } else { - if (createInfo->mag_filter == SDL_GPU_FILTER_LINEAR) { - if (createInfo->mipmap_mode == SDL_GPU_SAMPLERMIPMAPMODE_LINEAR) { - return D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR; - } else { - return D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT; - } - } else { - if (createInfo->mipmap_mode == SDL_GPU_SAMPLERMIPMAPMODE_LINEAR) { - return D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR; - } else { - return D3D11_FILTER_MIN_MAG_MIP_POINT; - } - } - } -} - -// Structs - -typedef struct D3D11Texture D3D11Texture; - -typedef struct D3D11TextureContainer -{ - TextureCommonHeader header; - - D3D11Texture *activeTexture; - - Uint32 textureCapacity; - Uint32 textureCount; - D3D11Texture **textures; - - char *debugName; - bool canBeCycled; -} D3D11TextureContainer; - -typedef struct D3D11TextureSubresource -{ - D3D11Texture *parent; - Uint32 layer; - Uint32 level; - Uint32 depth; // total depth - Uint32 index; - - // One RTV per depth slice - ID3D11RenderTargetView **colorTargetViews; // NULL if not a color target - - ID3D11UnorderedAccessView *uav; // NULL if not a storage texture - ID3D11DepthStencilView *depthStencilTargetView; // NULL if not a depth stencil target -} D3D11TextureSubresource; - -struct D3D11Texture -{ - D3D11TextureContainer *container; - Uint32 containerIndex; - - ID3D11Resource *handle; /* ID3D11Texture2D* or ID3D11Texture3D* */ - ID3D11ShaderResourceView *shaderView; - - D3D11TextureSubresource *subresources; - Uint32 subresourceCount; /* layerCount * num_levels */ - - SDL_AtomicInt referenceCount; -}; - -typedef struct D3D11Fence -{ - ID3D11Query *handle; - SDL_AtomicInt referenceCount; -} D3D11Fence; - -typedef struct D3D11WindowData -{ - SDL_Window *window; - IDXGISwapChain *swapchain; - D3D11Texture texture; - D3D11TextureContainer textureContainer; - SDL_GPUPresentMode presentMode; - SDL_GPUSwapchainComposition swapchainComposition; - DXGI_FORMAT swapchainFormat; - DXGI_COLOR_SPACE_TYPE swapchainColorSpace; - Uint32 width; - Uint32 height; - SDL_GPUFence *inFlightFences[MAX_FRAMES_IN_FLIGHT]; - Uint32 frameCounter; - bool needsSwapchainRecreate; -} D3D11WindowData; - -typedef struct D3D11Shader -{ - ID3D11DeviceChild *handle; // ID3D11VertexShader, ID3D11PixelShader, ID3D11ComputeShader - void *bytecode; - size_t bytecodeSize; - - Uint32 numSamplers; - Uint32 numUniformBuffers; - Uint32 numStorageBuffers; - Uint32 numStorageTextures; -} D3D11Shader; - -typedef struct D3D11GraphicsPipeline -{ - Sint32 numColorTargets; - DXGI_FORMAT colorTargetFormats[MAX_COLOR_TARGET_BINDINGS]; - ID3D11BlendState *colorTargetBlendState; - Uint32 sampleMask; - - SDL_GPUMultisampleState multisampleState; - - Uint8 hasDepthStencilTarget; - DXGI_FORMAT depthStencilTargetFormat; - ID3D11DepthStencilState *depthStencilState; - - SDL_GPUPrimitiveType primitiveType; - ID3D11RasterizerState *rasterizerState; - - ID3D11VertexShader *vertexShader; - ID3D11PixelShader *fragmentShader; - - ID3D11InputLayout *inputLayout; - Uint32 vertexStrides[MAX_VERTEX_BUFFERS]; - - Uint32 vertexSamplerCount; - Uint32 vertexUniformBufferCount; - Uint32 vertexStorageBufferCount; - Uint32 vertexStorageTextureCount; - - Uint32 fragmentSamplerCount; - Uint32 fragmentUniformBufferCount; - Uint32 fragmentStorageBufferCount; - Uint32 fragmentStorageTextureCount; -} D3D11GraphicsPipeline; - -typedef struct D3D11ComputePipeline -{ - ID3D11ComputeShader *computeShader; - - Uint32 numSamplers; - Uint32 numReadonlyStorageTextures; - Uint32 numReadWriteStorageTextures; - Uint32 numReadonlyStorageBuffers; - Uint32 numReadWriteStorageBuffers; - Uint32 numUniformBuffers; -} D3D11ComputePipeline; - -typedef struct D3D11Buffer -{ - ID3D11Buffer *handle; - ID3D11UnorderedAccessView *uav; - ID3D11ShaderResourceView *srv; - Uint32 size; - SDL_AtomicInt referenceCount; -} D3D11Buffer; - -typedef struct D3D11BufferContainer -{ - D3D11Buffer *activeBuffer; - - Uint32 bufferCapacity; - Uint32 bufferCount; - D3D11Buffer **buffers; - - D3D11_BUFFER_DESC bufferDesc; - - char *debugName; -} D3D11BufferContainer; - -typedef struct D3D11BufferDownload -{ - ID3D11Buffer *stagingBuffer; - Uint32 dstOffset; - Uint32 size; -} D3D11BufferDownload; - -typedef struct D3D11TextureDownload -{ - ID3D11Resource *stagingTexture; - Uint32 width; - Uint32 height; - Uint32 depth; - Uint32 bufferOffset; - Uint32 bytesPerRow; - Uint32 bytesPerDepthSlice; -} D3D11TextureDownload; - -typedef struct D3D11TransferBuffer -{ - Uint8 *data; - Uint32 size; - SDL_AtomicInt referenceCount; - - D3D11BufferDownload *bufferDownloads; - Uint32 bufferDownloadCount; - Uint32 bufferDownloadCapacity; - - D3D11TextureDownload *textureDownloads; - Uint32 textureDownloadCount; - Uint32 textureDownloadCapacity; -} D3D11TransferBuffer; - -typedef struct D3D11TransferBufferContainer -{ - D3D11TransferBuffer *activeBuffer; - - /* These are all the buffers that have been used by this container. - * If the resource is bound and then updated with DISCARD, a new resource - * will be added to this list. - * These can be reused after they are submitted and command processing is complete. - */ - Uint32 bufferCapacity; - Uint32 bufferCount; - D3D11TransferBuffer **buffers; -} D3D11TransferBufferContainer; - -typedef struct D3D11UniformBuffer -{ - ID3D11Buffer *buffer; - void *mappedData; - - Uint32 drawOffset; - Uint32 writeOffset; - Uint32 currentBlockSize; -} D3D11UniformBuffer; - -typedef struct D3D11Sampler -{ - ID3D11SamplerState *handle; -} D3D11Sampler; - -typedef struct D3D11Renderer D3D11Renderer; - -typedef struct D3D11CommandBuffer -{ - CommandBufferCommonHeader common; - D3D11Renderer *renderer; - - // Deferred Context - ID3D11DeviceContext1 *context; - - // Presentation - D3D11WindowData **windowDatas; - Uint32 windowDataCount; - Uint32 windowDataCapacity; - - // Render Pass - D3D11GraphicsPipeline *graphicsPipeline; - Uint8 stencilRef; - SDL_FColor blendConstants; - D3D11TextureSubresource *colorTargetSubresources[MAX_COLOR_TARGET_BINDINGS]; - D3D11TextureSubresource *colorResolveSubresources[MAX_COLOR_TARGET_BINDINGS]; - - // Compute Pass - D3D11ComputePipeline *computePipeline; - - // Debug Annotation - ID3DUserDefinedAnnotation *annotation; - - // Resource slot state - - bool needVertexBufferBind; - - bool needVertexSamplerBind; - bool needVertexStorageTextureBind; - bool needVertexStorageBufferBind; - bool needVertexUniformBufferBind; - - bool needFragmentSamplerBind; - bool needFragmentStorageTextureBind; - bool needFragmentStorageBufferBind; - bool needFragmentUniformBufferBind; - - bool needComputeSamplerBind; - bool needComputeReadOnlyTextureBind; - bool needComputeReadOnlyBufferBind; - bool needComputeUniformBufferBind; - - // defer OMSetBlendState because it combines three different states - bool needBlendStateSet; - - ID3D11Buffer *vertexBuffers[MAX_VERTEX_BUFFERS]; - Uint32 vertexBufferOffsets[MAX_VERTEX_BUFFERS]; - Uint32 vertexBufferCount; - - D3D11Texture *vertexSamplerTextures[MAX_TEXTURE_SAMPLERS_PER_STAGE]; - D3D11Sampler *vertexSamplers[MAX_TEXTURE_SAMPLERS_PER_STAGE]; - D3D11Texture *vertexStorageTextures[MAX_STORAGE_TEXTURES_PER_STAGE]; - D3D11Buffer *vertexStorageBuffers[MAX_STORAGE_BUFFERS_PER_STAGE]; - - D3D11Texture *fragmentSamplerTextures[MAX_TEXTURE_SAMPLERS_PER_STAGE]; - D3D11Sampler *fragmentSamplers[MAX_TEXTURE_SAMPLERS_PER_STAGE]; - D3D11Texture *fragmentStorageTextures[MAX_STORAGE_TEXTURES_PER_STAGE]; - D3D11Buffer *fragmentStorageBuffers[MAX_STORAGE_BUFFERS_PER_STAGE]; - - D3D11Texture *computeSamplerTextures[MAX_TEXTURE_SAMPLERS_PER_STAGE]; - D3D11Sampler *computeSamplers[MAX_TEXTURE_SAMPLERS_PER_STAGE]; - D3D11Texture *computeReadOnlyStorageTextures[MAX_STORAGE_TEXTURES_PER_STAGE]; - D3D11Buffer *computeReadOnlyStorageBuffers[MAX_STORAGE_BUFFERS_PER_STAGE]; - D3D11TextureSubresource *computeReadWriteStorageTextureSubresources[MAX_COMPUTE_WRITE_TEXTURES]; - D3D11Buffer *computeReadWriteStorageBuffers[MAX_COMPUTE_WRITE_BUFFERS]; - - // Uniform buffers - D3D11UniformBuffer *vertexUniformBuffers[MAX_UNIFORM_BUFFERS_PER_STAGE]; - D3D11UniformBuffer *fragmentUniformBuffers[MAX_UNIFORM_BUFFERS_PER_STAGE]; - D3D11UniformBuffer *computeUniformBuffers[MAX_UNIFORM_BUFFERS_PER_STAGE]; - - // Fences - D3D11Fence *fence; - bool autoReleaseFence; - - // Reference Counting - D3D11Buffer **usedBuffers; - Uint32 usedBufferCount; - Uint32 usedBufferCapacity; - - D3D11TransferBuffer **usedTransferBuffers; - Uint32 usedTransferBufferCount; - Uint32 usedTransferBufferCapacity; - - D3D11Texture **usedTextures; - Uint32 usedTextureCount; - Uint32 usedTextureCapacity; - - D3D11UniformBuffer **usedUniformBuffers; - Uint32 usedUniformBufferCount; - Uint32 usedUniformBufferCapacity; -} D3D11CommandBuffer; - -struct D3D11Renderer -{ - ID3D11Device1 *device; - ID3D11DeviceContext *immediateContext; - IDXGIFactory1 *factory; - IDXGIAdapter1 *adapter; - IDXGIDebug *dxgiDebug; -#ifdef HAVE_IDXGIINFOQUEUE - IDXGIInfoQueue *dxgiInfoQueue; -#endif - - SDL_SharedObject *d3d11_dll; - SDL_SharedObject *dxgi_dll; - SDL_SharedObject *dxgidebug_dll; - - Uint8 debugMode; - BOOL supportsTearing; - Uint8 supportsFlipDiscard; - - SDL_iconv_t iconv; - - // Blit - BlitPipelineCacheEntry blitPipelines[5]; - SDL_GPUSampler *blitNearestSampler; - SDL_GPUSampler *blitLinearSampler; - - // Resource Tracking - D3D11WindowData **claimedWindows; - Uint32 claimedWindowCount; - Uint32 claimedWindowCapacity; - - D3D11CommandBuffer **availableCommandBuffers; - Uint32 availableCommandBufferCount; - Uint32 availableCommandBufferCapacity; - - D3D11CommandBuffer **submittedCommandBuffers; - Uint32 submittedCommandBufferCount; - Uint32 submittedCommandBufferCapacity; - - D3D11Fence **availableFences; - Uint32 availableFenceCount; - Uint32 availableFenceCapacity; - - D3D11UniformBuffer **uniformBufferPool; - Uint32 uniformBufferPoolCount; - Uint32 uniformBufferPoolCapacity; - - D3D11TransferBufferContainer **transferBufferContainersToDestroy; - Uint32 transferBufferContainersToDestroyCount; - Uint32 transferBufferContainersToDestroyCapacity; - - D3D11BufferContainer **bufferContainersToDestroy; - Uint32 bufferContainersToDestroyCount; - Uint32 bufferContainersToDestroyCapacity; - - D3D11TextureContainer **textureContainersToDestroy; - Uint32 textureContainersToDestroyCount; - Uint32 textureContainersToDestroyCapacity; - - SDL_Mutex *contextLock; - SDL_Mutex *acquireCommandBufferLock; - SDL_Mutex *acquireUniformBufferLock; - SDL_Mutex *fenceLock; - SDL_Mutex *windowLock; - - // Null arrays for resetting resource slots - ID3D11RenderTargetView *nullRTVs[MAX_COLOR_TARGET_BINDINGS]; - - ID3D11ShaderResourceView *nullSRVs[MAX_TEXTURE_SAMPLERS_PER_STAGE * 2 + - MAX_STORAGE_TEXTURES_PER_STAGE + - MAX_STORAGE_BUFFERS_PER_STAGE]; - - ID3D11SamplerState *nullSamplers[MAX_TEXTURE_SAMPLERS_PER_STAGE * 2]; - - ID3D11UnorderedAccessView *nullUAVs[MAX_COMPUTE_WRITE_TEXTURES + - MAX_COMPUTE_WRITE_BUFFERS]; -}; - -// Logging - -static void D3D11_INTERNAL_SetError( - D3D11Renderer *renderer, - const char *msg, - HRESULT res) -{ -#define MAX_ERROR_LEN 1024 // FIXME: Arbitrary! - - // Buffer for text, ensure space for \0 terminator after buffer - char wszMsgBuff[MAX_ERROR_LEN + 1]; - DWORD dwChars; // Number of chars returned. - - if (res == DXGI_ERROR_DEVICE_REMOVED) { - res = ID3D11Device_GetDeviceRemovedReason(renderer->device); - } - - // Try to get the message from the system errors. -#ifdef _WIN32 - dwChars = FormatMessageA( - FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, - res, - 0, - wszMsgBuff, - MAX_ERROR_LEN, - NULL); -#else - // FIXME: Do we have error strings in dxvk-native? -flibit - dwChars = 0; -#endif - - // No message? Screw it, just post the code. - if (dwChars == 0) { - if (renderer->debugMode) { - SDL_LogError(SDL_LOG_CATEGORY_GPU, "%s! Error Code: " HRESULT_FMT, msg, res); - } - SDL_SetError("%s! Error Code: " HRESULT_FMT, msg, res); - return; - } - - // Ensure valid range - dwChars = SDL_min(dwChars, MAX_ERROR_LEN); - - // Trim whitespace from tail of message - while (dwChars > 0) { - if (wszMsgBuff[dwChars - 1] <= ' ') { - dwChars--; - } else { - break; - } - } - - // Ensure null-terminated string - wszMsgBuff[dwChars] = '\0'; - - if (renderer->debugMode) { - SDL_LogError(SDL_LOG_CATEGORY_GPU, "%s! Error Code: %s " HRESULT_FMT, msg, wszMsgBuff, res); - } - SDL_SetError("%s! Error Code: %s " HRESULT_FMT, msg, wszMsgBuff, res); -} - -// Helper Functions - -static inline Uint32 D3D11_INTERNAL_CalcSubresource( - Uint32 mipLevel, - Uint32 layer, - Uint32 numLevels) -{ - return mipLevel + (layer * numLevels); -} - -static inline Uint32 D3D11_INTERNAL_NextHighestAlignment( - Uint32 n, - Uint32 align) -{ - return align * ((n + align - 1) / align); -} - -static DXGI_FORMAT D3D11_INTERNAL_GetTypelessFormat( - DXGI_FORMAT typedFormat) -{ - switch (typedFormat) { - case DXGI_FORMAT_D16_UNORM: - return DXGI_FORMAT_R16_TYPELESS; - case DXGI_FORMAT_D32_FLOAT: - return DXGI_FORMAT_R32_TYPELESS; - case DXGI_FORMAT_D24_UNORM_S8_UINT: - return DXGI_FORMAT_R24G8_TYPELESS; - case DXGI_FORMAT_D32_FLOAT_S8X24_UINT: - return DXGI_FORMAT_R32G8X24_TYPELESS; - default: - return DXGI_FORMAT_UNKNOWN; - } -} - -static DXGI_FORMAT D3D11_INTERNAL_GetSampleableFormat( - DXGI_FORMAT format) -{ - switch (format) { - case DXGI_FORMAT_R16_TYPELESS: - return DXGI_FORMAT_R16_UNORM; - case DXGI_FORMAT_R32_TYPELESS: - return DXGI_FORMAT_R32_FLOAT; - case DXGI_FORMAT_R24G8_TYPELESS: - return DXGI_FORMAT_R24_UNORM_X8_TYPELESS; - case DXGI_FORMAT_R32G8X24_TYPELESS: - return DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS; - default: - return format; - } -} - -// Quit - -static void D3D11_INTERNAL_DestroyBufferContainer( - D3D11BufferContainer *container) -{ - for (Uint32 i = 0; i < container->bufferCount; i += 1) { - D3D11Buffer *d3d11Buffer = container->buffers[i]; - - if (d3d11Buffer->uav != NULL) { - ID3D11UnorderedAccessView_Release(d3d11Buffer->uav); - } - - if (d3d11Buffer->srv != NULL) { - ID3D11ShaderResourceView_Release(d3d11Buffer->srv); - } - - ID3D11Buffer_Release(d3d11Buffer->handle); - - SDL_free(d3d11Buffer); - } - - SDL_free(container->buffers); - SDL_free(container); -} - -static void D3D11_DestroyDevice( - SDL_GPUDevice *device) -{ - D3D11Renderer *renderer = (D3D11Renderer *)device->driverData; - - // Flush any remaining GPU work... - D3D11_Wait(device->driverData); - - // Release the window data - for (Sint32 i = renderer->claimedWindowCount - 1; i >= 0; i -= 1) { - D3D11_ReleaseWindow(device->driverData, renderer->claimedWindows[i]->window); - } - SDL_free(renderer->claimedWindows); - - // Release the blit resources - D3D11_INTERNAL_DestroyBlitPipelines(device->driverData); - - // Release UBOs - for (Uint32 i = 0; i < renderer->uniformBufferPoolCount; i += 1) { - ID3D11Buffer_Release(renderer->uniformBufferPool[i]->buffer); - SDL_free(renderer->uniformBufferPool[i]); - } - SDL_free(renderer->uniformBufferPool); - - // Release command buffer infrastructure - for (Uint32 i = 0; i < renderer->availableCommandBufferCount; i += 1) { - D3D11CommandBuffer *commandBuffer = renderer->availableCommandBuffers[i]; - if (commandBuffer->annotation) { - ID3DUserDefinedAnnotation_Release(commandBuffer->annotation); - } - ID3D11DeviceContext_Release(commandBuffer->context); - SDL_free(commandBuffer->usedBuffers); - SDL_free(commandBuffer->usedTransferBuffers); - SDL_free(commandBuffer); - } - SDL_free(renderer->availableCommandBuffers); - SDL_free(renderer->submittedCommandBuffers); - - // Release fence infrastructure - for (Uint32 i = 0; i < renderer->availableFenceCount; i += 1) { - D3D11Fence *fence = renderer->availableFences[i]; - ID3D11Query_Release(fence->handle); - SDL_free(fence); - } - SDL_free(renderer->availableFences); - - // Release the iconv, if applicable - if (renderer->iconv != NULL) { - SDL_iconv_close(renderer->iconv); - } - - // Release the mutexes - SDL_DestroyMutex(renderer->acquireCommandBufferLock); - SDL_DestroyMutex(renderer->acquireUniformBufferLock); - SDL_DestroyMutex(renderer->contextLock); - SDL_DestroyMutex(renderer->fenceLock); - SDL_DestroyMutex(renderer->windowLock); - - // Release the device and associated objects - ID3D11DeviceContext_Release(renderer->immediateContext); - ID3D11Device_Release(renderer->device); - IDXGIAdapter_Release(renderer->adapter); - IDXGIFactory_Release(renderer->factory); - - // Report leaks and clean up debug objects - if (renderer->dxgiDebug) { - IDXGIDebug_ReportLiveObjects( - renderer->dxgiDebug, - D3D_IID_DXGI_DEBUG_ALL, - DXGI_DEBUG_RLO_SUMMARY | DXGI_DEBUG_RLO_DETAIL); - IDXGIDebug_Release(renderer->dxgiDebug); - } - -#ifdef HAVE_IDXGIINFOQUEUE - if (renderer->dxgiInfoQueue) { - IDXGIInfoQueue_Release(renderer->dxgiInfoQueue); - } -#endif - - // Release the DLLs - SDL_UnloadObject(renderer->d3d11_dll); - SDL_UnloadObject(renderer->dxgi_dll); - if (renderer->dxgidebug_dll) { - SDL_UnloadObject(renderer->dxgidebug_dll); - } - - // Free the primary structures - SDL_free(renderer); - SDL_free(device); -} - -// Resource tracking - -static void D3D11_INTERNAL_TrackBuffer( - D3D11CommandBuffer *commandBuffer, - D3D11Buffer *buffer) -{ - TRACK_RESOURCE( - buffer, - D3D11Buffer *, - usedBuffers, - usedBufferCount, - usedBufferCapacity); -} - -static void D3D11_INTERNAL_TrackTransferBuffer( - D3D11CommandBuffer *commandBuffer, - D3D11TransferBuffer *buffer) -{ - TRACK_RESOURCE( - buffer, - D3D11TransferBuffer *, - usedTransferBuffers, - usedTransferBufferCount, - usedTransferBufferCapacity); -} - -static void D3D11_INTERNAL_TrackTexture( - D3D11CommandBuffer *commandBuffer, - D3D11Texture *texture) -{ - TRACK_RESOURCE( - texture, - D3D11Texture *, - usedTextures, - usedTextureCount, - usedTextureCapacity); -} - -static void D3D11_INTERNAL_TrackUniformBuffer( - D3D11CommandBuffer *commandBuffer, - D3D11UniformBuffer *uniformBuffer) -{ - Uint32 i; - for (i = 0; i < commandBuffer->usedUniformBufferCount; i += 1) { - if (commandBuffer->usedUniformBuffers[i] == uniformBuffer) { - return; - } - } - - if (commandBuffer->usedUniformBufferCount == commandBuffer->usedUniformBufferCapacity) { - commandBuffer->usedUniformBufferCapacity += 1; - commandBuffer->usedUniformBuffers = SDL_realloc( - commandBuffer->usedUniformBuffers, - commandBuffer->usedUniformBufferCapacity * sizeof(D3D11UniformBuffer *)); - } - - commandBuffer->usedUniformBuffers[commandBuffer->usedUniformBufferCount] = uniformBuffer; - commandBuffer->usedUniformBufferCount += 1; -} - -// Disposal - -static void D3D11_INTERNAL_DestroyTexture(D3D11Texture *d3d11Texture) -{ - if (d3d11Texture->shaderView) { - ID3D11ShaderResourceView_Release(d3d11Texture->shaderView); - } - - for (Uint32 subresourceIndex = 0; subresourceIndex < d3d11Texture->subresourceCount; subresourceIndex += 1) { - if (d3d11Texture->subresources[subresourceIndex].colorTargetViews != NULL) { - for (Uint32 depthIndex = 0; depthIndex < d3d11Texture->subresources[subresourceIndex].depth; depthIndex += 1) { - ID3D11RenderTargetView_Release(d3d11Texture->subresources[subresourceIndex].colorTargetViews[depthIndex]); - } - SDL_free(d3d11Texture->subresources[subresourceIndex].colorTargetViews); - } - - if (d3d11Texture->subresources[subresourceIndex].depthStencilTargetView != NULL) { - ID3D11DepthStencilView_Release(d3d11Texture->subresources[subresourceIndex].depthStencilTargetView); - } - - if (d3d11Texture->subresources[subresourceIndex].uav != NULL) { - ID3D11UnorderedAccessView_Release(d3d11Texture->subresources[subresourceIndex].uav); - } - } - SDL_free(d3d11Texture->subresources); - - ID3D11Resource_Release(d3d11Texture->handle); -} - -static void D3D11_INTERNAL_DestroyTextureContainer( - D3D11TextureContainer *container) -{ - for (Uint32 i = 0; i < container->textureCount; i += 1) { - D3D11_INTERNAL_DestroyTexture(container->textures[i]); - } - - SDL_free(container->textures); - SDL_free(container); -} - -static void D3D11_ReleaseTexture( - SDL_GPURenderer *driverData, - SDL_GPUTexture *texture) -{ - D3D11Renderer *renderer = (D3D11Renderer *)driverData; - D3D11TextureContainer *container = (D3D11TextureContainer *)texture; - - SDL_LockMutex(renderer->contextLock); - - EXPAND_ARRAY_IF_NEEDED( - renderer->textureContainersToDestroy, - D3D11TextureContainer *, - renderer->textureContainersToDestroyCount + 1, - renderer->textureContainersToDestroyCapacity, - renderer->textureContainersToDestroyCapacity + 1); - - renderer->textureContainersToDestroy[renderer->textureContainersToDestroyCount] = container; - renderer->textureContainersToDestroyCount += 1; - - SDL_UnlockMutex(renderer->contextLock); -} - -static void D3D11_ReleaseSampler( - SDL_GPURenderer *driverData, - SDL_GPUSampler *sampler) -{ - (void)driverData; // used by other backends - D3D11Sampler *d3d11Sampler = (D3D11Sampler *)sampler; - ID3D11SamplerState_Release(d3d11Sampler->handle); - SDL_free(d3d11Sampler); -} - -static void D3D11_ReleaseBuffer( - SDL_GPURenderer *driverData, - SDL_GPUBuffer *buffer) -{ - D3D11Renderer *renderer = (D3D11Renderer *)driverData; - D3D11BufferContainer *container = (D3D11BufferContainer *)buffer; - - SDL_LockMutex(renderer->contextLock); - - EXPAND_ARRAY_IF_NEEDED( - renderer->bufferContainersToDestroy, - D3D11BufferContainer *, - renderer->bufferContainersToDestroyCount + 1, - renderer->bufferContainersToDestroyCapacity, - renderer->bufferContainersToDestroyCapacity + 1); - - renderer->bufferContainersToDestroy[renderer->bufferContainersToDestroyCount] = container; - renderer->bufferContainersToDestroyCount += 1; - - SDL_UnlockMutex(renderer->contextLock); -} - -static void D3D11_ReleaseTransferBuffer( - SDL_GPURenderer *driverData, - SDL_GPUTransferBuffer *transferBuffer) -{ - D3D11Renderer *renderer = (D3D11Renderer *)driverData; - - SDL_LockMutex(renderer->contextLock); - - EXPAND_ARRAY_IF_NEEDED( - renderer->transferBufferContainersToDestroy, - D3D11TransferBufferContainer *, - renderer->transferBufferContainersToDestroyCount + 1, - renderer->transferBufferContainersToDestroyCapacity, - renderer->transferBufferContainersToDestroyCapacity + 1); - - renderer->transferBufferContainersToDestroy[renderer->transferBufferContainersToDestroyCount] = (D3D11TransferBufferContainer *)transferBuffer; - renderer->transferBufferContainersToDestroyCount += 1; - - SDL_UnlockMutex(renderer->contextLock); -} - -static void D3D11_INTERNAL_DestroyTransferBufferContainer( - D3D11TransferBufferContainer *transferBufferContainer) -{ - for (Uint32 i = 0; i < transferBufferContainer->bufferCount; i += 1) { - if (transferBufferContainer->buffers[i]->bufferDownloadCount > 0) { - SDL_free(transferBufferContainer->buffers[i]->bufferDownloads); - } - if (transferBufferContainer->buffers[i]->textureDownloadCount > 0) { - SDL_free(transferBufferContainer->buffers[i]->textureDownloads); - } - SDL_free(transferBufferContainer->buffers[i]->data); - SDL_free(transferBufferContainer->buffers[i]); - } - SDL_free(transferBufferContainer->buffers); -} - -static void D3D11_ReleaseShader( - SDL_GPURenderer *driverData, - SDL_GPUShader *shader) -{ - (void)driverData; // used by other backends - D3D11Shader *d3dShader = (D3D11Shader *)shader; - ID3D11DeviceChild_Release(d3dShader->handle); - if (d3dShader->bytecode) { - SDL_free(d3dShader->bytecode); - } - SDL_free(d3dShader); -} - -static void D3D11_ReleaseComputePipeline( - SDL_GPURenderer *driverData, - SDL_GPUComputePipeline *computePipeline) -{ - D3D11ComputePipeline *d3d11ComputePipeline = (D3D11ComputePipeline *)computePipeline; - - ID3D11ComputeShader_Release(d3d11ComputePipeline->computeShader); - - SDL_free(d3d11ComputePipeline); -} - -static void D3D11_ReleaseGraphicsPipeline( - SDL_GPURenderer *driverData, - SDL_GPUGraphicsPipeline *graphicsPipeline) -{ - (void)driverData; // used by other backends - D3D11GraphicsPipeline *d3d11GraphicsPipeline = (D3D11GraphicsPipeline *)graphicsPipeline; - - ID3D11BlendState_Release(d3d11GraphicsPipeline->colorTargetBlendState); - ID3D11DepthStencilState_Release(d3d11GraphicsPipeline->depthStencilState); - ID3D11RasterizerState_Release(d3d11GraphicsPipeline->rasterizerState); - - if (d3d11GraphicsPipeline->inputLayout) { - ID3D11InputLayout_Release(d3d11GraphicsPipeline->inputLayout); - } - - ID3D11VertexShader_Release(d3d11GraphicsPipeline->vertexShader); - ID3D11PixelShader_Release(d3d11GraphicsPipeline->fragmentShader); - - SDL_free(d3d11GraphicsPipeline); -} - -// State Creation - -static ID3D11BlendState *D3D11_INTERNAL_FetchBlendState( - D3D11Renderer *renderer, - Uint32 numColorTargets, - const SDL_GPUColorTargetDescription *colorTargets) -{ - ID3D11BlendState *result; - D3D11_BLEND_DESC blendDesc; - HRESULT res; - - /* Create a new blend state. - * The spec says the driver will not create duplicate states, so there's no need to cache. - */ - SDL_zero(blendDesc); // needed for any unused RT entries - - blendDesc.AlphaToCoverageEnable = FALSE; - blendDesc.IndependentBlendEnable = TRUE; - - for (Uint32 i = 0; i < numColorTargets; i += 1) { - SDL_GPUColorComponentFlags colorWriteMask = colorTargets[i].blend_state.enable_color_write_mask ? - colorTargets[i].blend_state.color_write_mask : - 0xF; - - blendDesc.RenderTarget[i].BlendEnable = colorTargets[i].blend_state.enable_blend; - blendDesc.RenderTarget[i].BlendOp = SDLToD3D11_BlendOp[colorTargets[i].blend_state.color_blend_op]; - blendDesc.RenderTarget[i].BlendOpAlpha = SDLToD3D11_BlendOp[colorTargets[i].blend_state.alpha_blend_op]; - blendDesc.RenderTarget[i].DestBlend = SDLToD3D11_BlendFactor[colorTargets[i].blend_state.dst_color_blendfactor]; - blendDesc.RenderTarget[i].DestBlendAlpha = SDLToD3D11_BlendFactorAlpha[colorTargets[i].blend_state.dst_alpha_blendfactor]; - blendDesc.RenderTarget[i].RenderTargetWriteMask = colorWriteMask; - blendDesc.RenderTarget[i].SrcBlend = SDLToD3D11_BlendFactor[colorTargets[i].blend_state.src_color_blendfactor]; - blendDesc.RenderTarget[i].SrcBlendAlpha = SDLToD3D11_BlendFactorAlpha[colorTargets[i].blend_state.src_alpha_blendfactor]; - } - - res = ID3D11Device_CreateBlendState( - renderer->device, - &blendDesc, - &result); - CHECK_D3D11_ERROR_AND_RETURN("Could not create blend state", NULL); - - return result; -} - -static ID3D11DepthStencilState *D3D11_INTERNAL_FetchDepthStencilState( - D3D11Renderer *renderer, - SDL_GPUDepthStencilState depthStencilState) -{ - ID3D11DepthStencilState *result; - D3D11_DEPTH_STENCIL_DESC dsDesc; - HRESULT res; - - /* Create a new depth-stencil state. - * The spec says the driver will not create duplicate states, so there's no need to cache. - */ - dsDesc.DepthEnable = depthStencilState.enable_depth_test; - dsDesc.StencilEnable = depthStencilState.enable_stencil_test; - dsDesc.DepthFunc = SDLToD3D11_CompareOp[depthStencilState.compare_op]; - dsDesc.DepthWriteMask = (depthStencilState.enable_depth_write ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO); - - dsDesc.BackFace.StencilFunc = SDLToD3D11_CompareOp[depthStencilState.back_stencil_state.compare_op]; - dsDesc.BackFace.StencilDepthFailOp = SDLToD3D11_StencilOp[depthStencilState.back_stencil_state.depth_fail_op]; - dsDesc.BackFace.StencilFailOp = SDLToD3D11_StencilOp[depthStencilState.back_stencil_state.fail_op]; - dsDesc.BackFace.StencilPassOp = SDLToD3D11_StencilOp[depthStencilState.back_stencil_state.pass_op]; - - dsDesc.FrontFace.StencilFunc = SDLToD3D11_CompareOp[depthStencilState.front_stencil_state.compare_op]; - dsDesc.FrontFace.StencilDepthFailOp = SDLToD3D11_StencilOp[depthStencilState.front_stencil_state.depth_fail_op]; - dsDesc.FrontFace.StencilFailOp = SDLToD3D11_StencilOp[depthStencilState.front_stencil_state.fail_op]; - dsDesc.FrontFace.StencilPassOp = SDLToD3D11_StencilOp[depthStencilState.front_stencil_state.pass_op]; - - dsDesc.StencilReadMask = depthStencilState.compare_mask; - dsDesc.StencilWriteMask = depthStencilState.write_mask; - - res = ID3D11Device_CreateDepthStencilState( - renderer->device, - &dsDesc, - &result); - CHECK_D3D11_ERROR_AND_RETURN("Could not create depth-stencil state", NULL); - - return result; -} - -static ID3D11RasterizerState *D3D11_INTERNAL_FetchRasterizerState( - D3D11Renderer *renderer, - SDL_GPURasterizerState rasterizerState) -{ - ID3D11RasterizerState *result; - D3D11_RASTERIZER_DESC rasterizerDesc; - HRESULT res; - - /* Create a new rasterizer state. - * The spec says the driver will not create duplicate states, so there's no need to cache. - */ - rasterizerDesc.AntialiasedLineEnable = FALSE; - rasterizerDesc.CullMode = SDLToD3D11_CullMode[rasterizerState.cull_mode]; - rasterizerDesc.DepthBias = SDL_lroundf(rasterizerState.depth_bias_constant_factor); - rasterizerDesc.DepthBiasClamp = rasterizerState.depth_bias_clamp; - rasterizerDesc.DepthClipEnable = rasterizerState.enable_depth_clip; - rasterizerDesc.FillMode = (rasterizerState.fill_mode == SDL_GPU_FILLMODE_FILL) ? D3D11_FILL_SOLID : D3D11_FILL_WIREFRAME; - rasterizerDesc.FrontCounterClockwise = (rasterizerState.front_face == SDL_GPU_FRONTFACE_COUNTER_CLOCKWISE); - rasterizerDesc.MultisampleEnable = TRUE; // only applies to MSAA render targets - rasterizerDesc.ScissorEnable = TRUE; - rasterizerDesc.SlopeScaledDepthBias = rasterizerState.depth_bias_slope_factor; - - res = ID3D11Device_CreateRasterizerState( - renderer->device, - &rasterizerDesc, - &result); - CHECK_D3D11_ERROR_AND_RETURN("Could not create rasterizer state", NULL); - - return result; -} - -static Uint32 D3D11_INTERNAL_FindIndexOfVertexSlot( - Uint32 targetSlot, - const SDL_GPUVertexBufferDescription *bufferDescriptions, - Uint32 numDescriptions) -{ - for (Uint32 i = 0; i < numDescriptions; i += 1) { - if (bufferDescriptions[i].slot == targetSlot) { - return i; - } - } - - SDL_LogError(SDL_LOG_CATEGORY_GPU, "Could not find vertex buffer slot %u!", targetSlot); - return 0; -} - -static ID3D11InputLayout *D3D11_INTERNAL_FetchInputLayout( - D3D11Renderer *renderer, - SDL_GPUVertexInputState inputState, - void *shaderBytes, - size_t shaderByteLength) -{ - ID3D11InputLayout *result = NULL; - D3D11_INPUT_ELEMENT_DESC *elementDescs; - Uint32 bindingIndex; - HRESULT res; - - // Don't bother creating/fetching an input layout if there are no attributes. - if (inputState.num_vertex_attributes == 0) { - return NULL; - } - - // Allocate an array of vertex elements - elementDescs = SDL_stack_alloc( - D3D11_INPUT_ELEMENT_DESC, - inputState.num_vertex_attributes); - - // Create the array of input elements - for (Uint32 i = 0; i < inputState.num_vertex_attributes; i += 1) { - elementDescs[i].AlignedByteOffset = inputState.vertex_attributes[i].offset; - elementDescs[i].Format = SDLToD3D11_VertexFormat[inputState.vertex_attributes[i].format]; - elementDescs[i].InputSlot = inputState.vertex_attributes[i].buffer_slot; - - bindingIndex = D3D11_INTERNAL_FindIndexOfVertexSlot( - elementDescs[i].InputSlot, - inputState.vertex_buffer_descriptions, - inputState.num_vertex_buffers); - elementDescs[i].InputSlotClass = SDLToD3D11_VertexInputRate[inputState.vertex_buffer_descriptions[bindingIndex].input_rate]; - // The spec requires this to be 0 for per-vertex data - elementDescs[i].InstanceDataStepRate = (inputState.vertex_buffer_descriptions[bindingIndex].input_rate == SDL_GPU_VERTEXINPUTRATE_INSTANCE) - ? inputState.vertex_buffer_descriptions[bindingIndex].instance_step_rate - : 0; - - elementDescs[i].SemanticIndex = inputState.vertex_attributes[i].location; - elementDescs[i].SemanticName = "TEXCOORD"; - } - - res = ID3D11Device_CreateInputLayout( - renderer->device, - elementDescs, - inputState.num_vertex_attributes, - shaderBytes, - shaderByteLength, - &result); - if (FAILED(res)) { - SDL_stack_free(elementDescs); - CHECK_D3D11_ERROR_AND_RETURN("Could not create input layout!", NULL) - return NULL; - } - - /* FIXME: - * These are not cached by the driver! Should we cache them, or allow duplicates? - * If we have one input layout per graphics pipeline maybe that wouldn't be so bad...? - */ - - SDL_stack_free(elementDescs); - return result; -} - -// Pipeline Creation - -static ID3D11DeviceChild *D3D11_INTERNAL_CreateID3D11Shader( - D3D11Renderer *renderer, - Uint32 stage, - const Uint8 *code, - size_t codeSize, - const char *entrypointName, - void **pBytecode, - size_t *pBytecodeSize) -{ - ID3D11DeviceChild *handle = NULL; - HRESULT res; - - // Create the shader from the byte blob - if (stage == SDL_GPU_SHADERSTAGE_VERTEX) { - res = ID3D11Device_CreateVertexShader( - renderer->device, - code, - codeSize, - NULL, - (ID3D11VertexShader **)&handle); - CHECK_D3D11_ERROR_AND_RETURN("Could not create vertex shader", NULL) - } else if (stage == SDL_GPU_SHADERSTAGE_FRAGMENT) { - res = ID3D11Device_CreatePixelShader( - renderer->device, - code, - codeSize, - NULL, - (ID3D11PixelShader **)&handle); - CHECK_D3D11_ERROR_AND_RETURN("Could not create pixel shader", NULL) - } else if (stage == SDL_GPU_SHADERSTAGE_COMPUTE) { - res = ID3D11Device_CreateComputeShader( - renderer->device, - code, - codeSize, - NULL, - (ID3D11ComputeShader **)&handle); - CHECK_D3D11_ERROR_AND_RETURN("Could not create compute shader", NULL) - } - - if (pBytecode != NULL) { - *pBytecode = SDL_malloc(codeSize); - SDL_memcpy(*pBytecode, code, codeSize); - *pBytecodeSize = codeSize; - } - - return handle; -} - -static SDL_GPUComputePipeline *D3D11_CreateComputePipeline( - SDL_GPURenderer *driverData, - const SDL_GPUComputePipelineCreateInfo *createinfo) -{ - D3D11Renderer *renderer = (D3D11Renderer *)driverData; - ID3D11ComputeShader *shader; - D3D11ComputePipeline *pipeline; - - shader = (ID3D11ComputeShader *)D3D11_INTERNAL_CreateID3D11Shader( - renderer, - SDL_GPU_SHADERSTAGE_COMPUTE, - createinfo->code, - createinfo->code_size, - createinfo->entrypoint, - NULL, - NULL); - if (shader == NULL) { - return NULL; - } - - pipeline = SDL_malloc(sizeof(D3D11ComputePipeline)); - pipeline->computeShader = shader; - pipeline->numSamplers = createinfo->num_samplers; - pipeline->numReadonlyStorageTextures = createinfo->num_readonly_storage_textures; - pipeline->numReadWriteStorageTextures = createinfo->num_readwrite_storage_textures; - pipeline->numReadonlyStorageBuffers = createinfo->num_readonly_storage_buffers; - pipeline->numReadWriteStorageBuffers = createinfo->num_readwrite_storage_buffers; - pipeline->numUniformBuffers = createinfo->num_uniform_buffers; - // thread counts are ignored in d3d11 - - return (SDL_GPUComputePipeline *)pipeline; -} - -static SDL_GPUGraphicsPipeline *D3D11_CreateGraphicsPipeline( - SDL_GPURenderer *driverData, - const SDL_GPUGraphicsPipelineCreateInfo *createinfo) -{ - D3D11Renderer *renderer = (D3D11Renderer *)driverData; - D3D11Shader *vertShader = (D3D11Shader *)createinfo->vertex_shader; - D3D11Shader *fragShader = (D3D11Shader *)createinfo->fragment_shader; - D3D11GraphicsPipeline *pipeline = SDL_malloc(sizeof(D3D11GraphicsPipeline)); - - // Blend - - pipeline->colorTargetBlendState = D3D11_INTERNAL_FetchBlendState( - renderer, - createinfo->target_info.num_color_targets, - createinfo->target_info.color_target_descriptions); - - if (pipeline->colorTargetBlendState == NULL) { - return NULL; - } - - pipeline->numColorTargets = createinfo->target_info.num_color_targets; - for (Sint32 i = 0; i < pipeline->numColorTargets; i += 1) { - pipeline->colorTargetFormats[i] = SDLToD3D11_TextureFormat[createinfo->target_info.color_target_descriptions[i].format]; - } - - // Multisample - - pipeline->multisampleState = createinfo->multisample_state; - pipeline->sampleMask = createinfo->multisample_state.enable_mask ? - createinfo->multisample_state.sample_mask : - 0xFFFFFFFF; - - // Depth-Stencil - - pipeline->depthStencilState = D3D11_INTERNAL_FetchDepthStencilState( - renderer, - createinfo->depth_stencil_state); - - if (pipeline->depthStencilState == NULL) { - return NULL; - } - - pipeline->hasDepthStencilTarget = createinfo->target_info.has_depth_stencil_target; - pipeline->depthStencilTargetFormat = SDLToD3D11_TextureFormat[createinfo->target_info.depth_stencil_format]; - - // Rasterizer - - pipeline->primitiveType = createinfo->primitive_type; - pipeline->rasterizerState = D3D11_INTERNAL_FetchRasterizerState( - renderer, - createinfo->rasterizer_state); - - if (pipeline->rasterizerState == NULL) { - return NULL; - } - - // Shaders - - pipeline->vertexShader = (ID3D11VertexShader *)vertShader->handle; - ID3D11VertexShader_AddRef(pipeline->vertexShader); - - pipeline->fragmentShader = (ID3D11PixelShader *)fragShader->handle; - ID3D11PixelShader_AddRef(pipeline->fragmentShader); - - // Input Layout - - pipeline->inputLayout = D3D11_INTERNAL_FetchInputLayout( - renderer, - createinfo->vertex_input_state, - vertShader->bytecode, - vertShader->bytecodeSize); - - SDL_zeroa(pipeline->vertexStrides); - if (createinfo->vertex_input_state.num_vertex_buffers > 0) { - for (Uint32 i = 0; i < createinfo->vertex_input_state.num_vertex_buffers; i += 1) { - pipeline->vertexStrides[createinfo->vertex_input_state.vertex_buffer_descriptions[i].slot] = - createinfo->vertex_input_state.vertex_buffer_descriptions[i].pitch; - } - } - - // Resource layout - - pipeline->vertexSamplerCount = vertShader->numSamplers; - pipeline->vertexStorageTextureCount = vertShader->numStorageTextures; - pipeline->vertexStorageBufferCount = vertShader->numStorageBuffers; - pipeline->vertexUniformBufferCount = vertShader->numUniformBuffers; - - pipeline->fragmentSamplerCount = fragShader->numSamplers; - pipeline->fragmentStorageTextureCount = fragShader->numStorageTextures; - pipeline->fragmentStorageBufferCount = fragShader->numStorageBuffers; - pipeline->fragmentUniformBufferCount = fragShader->numUniformBuffers; - - return (SDL_GPUGraphicsPipeline *)pipeline; -} - -// Debug Naming - -static void D3D11_INTERNAL_SetBufferName( - D3D11Renderer *renderer, - D3D11Buffer *buffer, - const char *text) -{ - if (renderer->debugMode) { - ID3D11DeviceChild_SetPrivateData( - buffer->handle, - &D3D_IID_D3DDebugObjectName, - (UINT)SDL_strlen(text), - text); - } -} - -static void D3D11_SetBufferName( - SDL_GPURenderer *driverData, - SDL_GPUBuffer *buffer, - const char *text) -{ - D3D11Renderer *renderer = (D3D11Renderer *)driverData; - D3D11BufferContainer *container = (D3D11BufferContainer *)buffer; - size_t textLength = SDL_strlen(text) + 1; - - if (renderer->debugMode) { - container->debugName = SDL_realloc( - container->debugName, - textLength); - - SDL_utf8strlcpy( - container->debugName, - text, - textLength); - - for (Uint32 i = 0; i < container->bufferCount; i += 1) { - D3D11_INTERNAL_SetBufferName( - renderer, - container->buffers[i], - text); - } - } -} - -static void D3D11_INTERNAL_SetTextureName( - D3D11Renderer *renderer, - D3D11Texture *texture, - const char *text) -{ - if (renderer->debugMode) { - ID3D11DeviceChild_SetPrivateData( - texture->handle, - &D3D_IID_D3DDebugObjectName, - (UINT)SDL_strlen(text), - text); - } -} - -static void D3D11_SetTextureName( - SDL_GPURenderer *driverData, - SDL_GPUTexture *texture, - const char *text) -{ - D3D11Renderer *renderer = (D3D11Renderer *)driverData; - D3D11TextureContainer *container = (D3D11TextureContainer *)texture; - size_t textLength = SDL_strlen(text) + 1; - - if (renderer->debugMode) { - container->debugName = SDL_realloc( - container->debugName, - textLength); - - SDL_utf8strlcpy( - container->debugName, - text, - textLength); - - for (Uint32 i = 0; i < container->textureCount; i += 1) { - D3D11_INTERNAL_SetTextureName( - renderer, - container->textures[i], - text); - } - } -} - -static bool D3D11_INTERNAL_StrToWStr( - D3D11Renderer *renderer, - const char *str, - wchar_t *wstr, - size_t wstrSize) -{ - size_t inlen, result; - size_t outlen = wstrSize; - - if (renderer->iconv == NULL) { - renderer->iconv = SDL_iconv_open("WCHAR_T", "UTF-8"); - SDL_assert(renderer->iconv); - } - - // Convert... - inlen = SDL_strlen(str) + 1; - result = SDL_iconv( - renderer->iconv, - &str, - &inlen, - (char **)&wstr, - &outlen); - - // Check... - switch (result) { - case SDL_ICONV_ERROR: - case SDL_ICONV_E2BIG: - case SDL_ICONV_EILSEQ: - case SDL_ICONV_EINVAL: - SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "Failed to convert string to wchar_t!"); - return false; - default: - break; - } - - return true; -} - -static void D3D11_InsertDebugLabel( - SDL_GPUCommandBuffer *commandBuffer, - const char *text) -{ - D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer; - D3D11Renderer *renderer = (D3D11Renderer *)d3d11CommandBuffer->renderer; - - if (d3d11CommandBuffer->annotation == NULL) { - return; - } - - wchar_t wstr[256]; - if (!D3D11_INTERNAL_StrToWStr(renderer, text, wstr, sizeof(wstr))) { - return; - } - - ID3DUserDefinedAnnotation_SetMarker(d3d11CommandBuffer->annotation, wstr); -} - -static void D3D11_PushDebugGroup( - SDL_GPUCommandBuffer *commandBuffer, - const char *name) -{ - D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer; - D3D11Renderer *renderer = (D3D11Renderer *)d3d11CommandBuffer->renderer; - - if (d3d11CommandBuffer->annotation == NULL) { - return; - } - - wchar_t wstr[256]; - if (!D3D11_INTERNAL_StrToWStr(renderer, name, wstr, sizeof(wstr))) { - return; - } - - ID3DUserDefinedAnnotation_BeginEvent(d3d11CommandBuffer->annotation, wstr); -} - -static void D3D11_PopDebugGroup( - SDL_GPUCommandBuffer *commandBuffer) -{ - D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer; - if (d3d11CommandBuffer->annotation == NULL) { - return; - } - ID3DUserDefinedAnnotation_EndEvent(d3d11CommandBuffer->annotation); -} - -// Resource Creation - -static SDL_GPUSampler *D3D11_CreateSampler( - SDL_GPURenderer *driverData, - const SDL_GPUSamplerCreateInfo *createinfo) -{ - D3D11Renderer *renderer = (D3D11Renderer *)driverData; - D3D11_SAMPLER_DESC samplerDesc; - ID3D11SamplerState *samplerStateHandle; - D3D11Sampler *d3d11Sampler; - HRESULT res; - - samplerDesc.AddressU = SDLToD3D11_SamplerAddressMode[createinfo->address_mode_u]; - samplerDesc.AddressV = SDLToD3D11_SamplerAddressMode[createinfo->address_mode_v]; - samplerDesc.AddressW = SDLToD3D11_SamplerAddressMode[createinfo->address_mode_w]; - samplerDesc.ComparisonFunc = (createinfo->enable_compare ? SDLToD3D11_CompareOp[createinfo->compare_op] : SDLToD3D11_CompareOp[SDL_GPU_COMPAREOP_ALWAYS]); - samplerDesc.MaxAnisotropy = (createinfo->enable_anisotropy ? (UINT)createinfo->max_anisotropy : 0); - samplerDesc.Filter = SDLToD3D11_Filter(createinfo); - samplerDesc.MaxLOD = createinfo->max_lod; - samplerDesc.MinLOD = createinfo->min_lod; - samplerDesc.MipLODBias = createinfo->mip_lod_bias; - SDL_zeroa(samplerDesc.BorderColor); // arbitrary, unused - - res = ID3D11Device_CreateSamplerState( - renderer->device, - &samplerDesc, - &samplerStateHandle); - CHECK_D3D11_ERROR_AND_RETURN("Could not create sampler state", NULL); - - d3d11Sampler = (D3D11Sampler *)SDL_malloc(sizeof(D3D11Sampler)); - d3d11Sampler->handle = samplerStateHandle; - - return (SDL_GPUSampler *)d3d11Sampler; -} - -SDL_GPUShader *D3D11_CreateShader( - SDL_GPURenderer *driverData, - const SDL_GPUShaderCreateInfo *createinfo) -{ - D3D11Renderer *renderer = (D3D11Renderer *)driverData; - ID3D11DeviceChild *handle; - void *bytecode = NULL; - size_t bytecodeSize = 0; - D3D11Shader *shader; - - handle = D3D11_INTERNAL_CreateID3D11Shader( - renderer, - createinfo->stage, - createinfo->code, - createinfo->code_size, - createinfo->entrypoint, - createinfo->stage == SDL_GPU_SHADERSTAGE_VERTEX ? &bytecode : NULL, - createinfo->stage == SDL_GPU_SHADERSTAGE_VERTEX ? &bytecodeSize : NULL); - if (handle == NULL) { - return NULL; - } - - shader = (D3D11Shader *)SDL_calloc(1, sizeof(D3D11Shader)); - shader->handle = handle; - shader->numSamplers = createinfo->num_samplers; - shader->numStorageBuffers = createinfo->num_storage_buffers; - shader->numStorageTextures = createinfo->num_storage_textures; - shader->numUniformBuffers = createinfo->num_uniform_buffers; - if (createinfo->stage == SDL_GPU_SHADERSTAGE_VERTEX) { - // Store the raw bytecode and its length for creating InputLayouts - shader->bytecode = bytecode; - shader->bytecodeSize = bytecodeSize; - } - - return (SDL_GPUShader *)shader; -} - -static D3D11Texture *D3D11_INTERNAL_CreateTexture( - D3D11Renderer *renderer, - const SDL_GPUTextureCreateInfo *createInfo, - D3D11_SUBRESOURCE_DATA *initialData) -{ - Uint8 needsSRV, isColorTarget, isDepthStencil, isMultisample, isStaging, needSubresourceUAV, isMippable; - DXGI_FORMAT format; - ID3D11Resource *textureHandle; - ID3D11ShaderResourceView *srv = NULL; - D3D11Texture *d3d11Texture; - HRESULT res; - - isColorTarget = createInfo->usage & SDL_GPU_TEXTUREUSAGE_COLOR_TARGET; - isDepthStencil = createInfo->usage & SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET; - needsSRV = - (createInfo->usage & SDL_GPU_TEXTUREUSAGE_SAMPLER) || - (createInfo->usage & SDL_GPU_TEXTUREUSAGE_GRAPHICS_STORAGE_READ) || - (createInfo->usage & SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_READ); - needSubresourceUAV = - (createInfo->usage & SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE) || - (createInfo->usage & SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_SIMULTANEOUS_READ_WRITE); - isMultisample = createInfo->sample_count > SDL_GPU_SAMPLECOUNT_1; - isStaging = createInfo->usage == 0; - isMippable = - createInfo->num_levels > 1 && - (createInfo->usage & SDL_GPU_TEXTUREUSAGE_SAMPLER) && - (createInfo->usage & SDL_GPU_TEXTUREUSAGE_COLOR_TARGET); - format = SDLToD3D11_TextureFormat[createInfo->format]; - if (isDepthStencil) { - format = D3D11_INTERNAL_GetTypelessFormat(format); - } - - Uint32 layerCount = createInfo->type == SDL_GPU_TEXTURETYPE_3D ? 1 : createInfo->layer_count_or_depth; - Uint32 depth = createInfo->type == SDL_GPU_TEXTURETYPE_3D ? createInfo->layer_count_or_depth : 1; - - if (createInfo->type != SDL_GPU_TEXTURETYPE_3D) { - D3D11_TEXTURE2D_DESC desc2D; - - desc2D.BindFlags = 0; - if (needsSRV) { - desc2D.BindFlags |= D3D11_BIND_SHADER_RESOURCE; - } - if (needSubresourceUAV) { - desc2D.BindFlags |= D3D11_BIND_UNORDERED_ACCESS; - } - if (isColorTarget) { - desc2D.BindFlags |= D3D11_BIND_RENDER_TARGET; - } - if (isDepthStencil) { - desc2D.BindFlags |= D3D11_BIND_DEPTH_STENCIL; - } - - desc2D.Width = createInfo->width; - desc2D.Height = createInfo->height; - desc2D.ArraySize = layerCount; - desc2D.CPUAccessFlags = isStaging ? D3D11_CPU_ACCESS_WRITE : 0; - desc2D.Format = format; - desc2D.MipLevels = createInfo->num_levels; - desc2D.MiscFlags = 0; - desc2D.SampleDesc.Count = SDLToD3D11_SampleCount[createInfo->sample_count]; - desc2D.SampleDesc.Quality = isMultisample ? D3D11_STANDARD_MULTISAMPLE_PATTERN : 0; - desc2D.Usage = isStaging ? D3D11_USAGE_STAGING : D3D11_USAGE_DEFAULT; - - if (createInfo->type == SDL_GPU_TEXTURETYPE_CUBE || createInfo->type == SDL_GPU_TEXTURETYPE_CUBE_ARRAY) { - desc2D.MiscFlags |= D3D11_RESOURCE_MISC_TEXTURECUBE; - } - if (isMippable) { - desc2D.MiscFlags |= D3D11_RESOURCE_MISC_GENERATE_MIPS; - } - - res = ID3D11Device_CreateTexture2D( - renderer->device, - &desc2D, - initialData, - (ID3D11Texture2D **)&textureHandle); - CHECK_D3D11_ERROR_AND_RETURN("Could not create Texture2D", NULL); - - // Create the SRV, if applicable - if (needsSRV) { - D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc; - srvDesc.Format = D3D11_INTERNAL_GetSampleableFormat(format); - - if (createInfo->type == SDL_GPU_TEXTURETYPE_CUBE) { - srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE; - srvDesc.TextureCube.MipLevels = desc2D.MipLevels; - srvDesc.TextureCube.MostDetailedMip = 0; - } else if (createInfo->type == SDL_GPU_TEXTURETYPE_CUBE_ARRAY) { - srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBEARRAY; - srvDesc.TextureCubeArray.MipLevels = desc2D.MipLevels; - srvDesc.TextureCubeArray.MostDetailedMip = 0; - srvDesc.TextureCubeArray.First2DArrayFace = 0; - srvDesc.TextureCubeArray.NumCubes = layerCount / 6; - } else if (createInfo->type == SDL_GPU_TEXTURETYPE_2D_ARRAY) { - srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY; - srvDesc.Texture2DArray.MipLevels = desc2D.MipLevels; - srvDesc.Texture2DArray.MostDetailedMip = 0; - srvDesc.Texture2DArray.FirstArraySlice = 0; - srvDesc.Texture2DArray.ArraySize = layerCount; - } else { - srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; - srvDesc.Texture2D.MipLevels = desc2D.MipLevels; - srvDesc.Texture2D.MostDetailedMip = 0; - } - - res = ID3D11Device_CreateShaderResourceView( - renderer->device, - textureHandle, - &srvDesc, - &srv); - if (FAILED(res)) { - ID3D11Resource_Release(textureHandle); - D3D11_INTERNAL_SetError(renderer, "Could not create SRV for 2D texture", res); - return NULL; - } - } - } else { - D3D11_TEXTURE3D_DESC desc3D; - - desc3D.BindFlags = 0; - if (needsSRV) { - desc3D.BindFlags |= D3D11_BIND_SHADER_RESOURCE; - } - if (needSubresourceUAV) { - desc3D.BindFlags |= D3D11_BIND_UNORDERED_ACCESS; - } - if (isColorTarget) { - desc3D.BindFlags |= D3D11_BIND_RENDER_TARGET; - } - - desc3D.Width = createInfo->width; - desc3D.Height = createInfo->height; - desc3D.Depth = depth; - desc3D.CPUAccessFlags = isStaging ? D3D11_CPU_ACCESS_WRITE : 0; - desc3D.Format = format; - desc3D.MipLevels = createInfo->num_levels; - desc3D.MiscFlags = isMippable ? D3D11_RESOURCE_MISC_GENERATE_MIPS : 0; - desc3D.Usage = isStaging ? D3D11_USAGE_STAGING : D3D11_USAGE_DEFAULT; - - res = ID3D11Device_CreateTexture3D( - renderer->device, - &desc3D, - initialData, - (ID3D11Texture3D **)&textureHandle); - CHECK_D3D11_ERROR_AND_RETURN("Could not create Texture3D", NULL); - - // Create the SRV, if applicable - if (needsSRV) { - D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc; - srvDesc.Format = format; - srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D; - srvDesc.Texture3D.MipLevels = desc3D.MipLevels; - srvDesc.Texture3D.MostDetailedMip = 0; - - res = ID3D11Device_CreateShaderResourceView( - renderer->device, - textureHandle, - &srvDesc, - &srv); - if (FAILED(res)) { - ID3D11Resource_Release(textureHandle); - D3D11_INTERNAL_SetError(renderer, "Could not create SRV for 3D texture", res); - return NULL; - } - } - } - - d3d11Texture = (D3D11Texture *)SDL_malloc(sizeof(D3D11Texture)); - d3d11Texture->handle = textureHandle; - d3d11Texture->shaderView = srv; - SDL_SetAtomicInt(&d3d11Texture->referenceCount, 0); - d3d11Texture->container = NULL; - d3d11Texture->containerIndex = 0; - - d3d11Texture->subresourceCount = createInfo->num_levels * layerCount; - d3d11Texture->subresources = SDL_malloc( - d3d11Texture->subresourceCount * sizeof(D3D11TextureSubresource)); - - for (Uint32 layerIndex = 0; layerIndex < layerCount; layerIndex += 1) { - for (Uint32 levelIndex = 0; levelIndex < createInfo->num_levels; levelIndex += 1) { - Uint32 subresourceIndex = D3D11_INTERNAL_CalcSubresource( - levelIndex, - layerIndex, - createInfo->num_levels); - - d3d11Texture->subresources[subresourceIndex].parent = d3d11Texture; - d3d11Texture->subresources[subresourceIndex].layer = layerIndex; - d3d11Texture->subresources[subresourceIndex].level = levelIndex; - d3d11Texture->subresources[subresourceIndex].depth = depth; - d3d11Texture->subresources[subresourceIndex].index = subresourceIndex; - - d3d11Texture->subresources[subresourceIndex].colorTargetViews = NULL; - d3d11Texture->subresources[subresourceIndex].uav = NULL; - d3d11Texture->subresources[subresourceIndex].depthStencilTargetView = NULL; - - if (isDepthStencil) { - - D3D11_DEPTH_STENCIL_VIEW_DESC dsvDesc; - dsvDesc.Format = SDLToD3D11_TextureFormat[createInfo->format]; - dsvDesc.Flags = 0; - - if (isMultisample) { - dsvDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMS; - } else { - dsvDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D; - dsvDesc.Texture2D.MipSlice = levelIndex; - } - - res = ID3D11Device_CreateDepthStencilView( - renderer->device, - d3d11Texture->handle, - &dsvDesc, - &d3d11Texture->subresources[subresourceIndex].depthStencilTargetView); - CHECK_D3D11_ERROR_AND_RETURN("Could not create DSV!", NULL); - - } else if (isColorTarget) { - - d3d11Texture->subresources[subresourceIndex].colorTargetViews = SDL_calloc(depth, sizeof(ID3D11RenderTargetView *)); - - for (Uint32 depthIndex = 0; depthIndex < depth; depthIndex += 1) { - D3D11_RENDER_TARGET_VIEW_DESC rtvDesc; - rtvDesc.Format = SDLToD3D11_TextureFormat[createInfo->format]; - - if (createInfo->type == SDL_GPU_TEXTURETYPE_2D_ARRAY || createInfo->type == SDL_GPU_TEXTURETYPE_CUBE || createInfo->type == SDL_GPU_TEXTURETYPE_CUBE_ARRAY) { - rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY; - rtvDesc.Texture2DArray.MipSlice = levelIndex; - rtvDesc.Texture2DArray.FirstArraySlice = layerIndex; - rtvDesc.Texture2DArray.ArraySize = 1; - } else if (createInfo->type == SDL_GPU_TEXTURETYPE_3D) { - rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE3D; - rtvDesc.Texture3D.MipSlice = levelIndex; - rtvDesc.Texture3D.FirstWSlice = depthIndex; - rtvDesc.Texture3D.WSize = 1; - } else if (isMultisample) { - rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMS; - } else { - rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; - rtvDesc.Texture2D.MipSlice = levelIndex; - } - - res = ID3D11Device_CreateRenderTargetView( - renderer->device, - d3d11Texture->handle, - &rtvDesc, - &d3d11Texture->subresources[subresourceIndex].colorTargetViews[depthIndex]); - CHECK_D3D11_ERROR_AND_RETURN("Could not create RTV!", NULL); - } - } - - if (needSubresourceUAV) { - D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc; - uavDesc.Format = format; - - if (createInfo->type == SDL_GPU_TEXTURETYPE_2D_ARRAY || createInfo->type == SDL_GPU_TEXTURETYPE_CUBE || createInfo->type == SDL_GPU_TEXTURETYPE_CUBE_ARRAY) { - uavDesc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2DARRAY; - uavDesc.Texture2DArray.MipSlice = levelIndex; - uavDesc.Texture2DArray.FirstArraySlice = layerIndex; - uavDesc.Texture2DArray.ArraySize = 1; - } else if (createInfo->type == SDL_GPU_TEXTURETYPE_3D) { - uavDesc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE3D; - uavDesc.Texture3D.MipSlice = levelIndex; - uavDesc.Texture3D.FirstWSlice = 0; - uavDesc.Texture3D.WSize = depth; - } else { - uavDesc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2D; - uavDesc.Texture2D.MipSlice = levelIndex; - } - - res = ID3D11Device_CreateUnorderedAccessView( - renderer->device, - d3d11Texture->handle, - &uavDesc, - &d3d11Texture->subresources[subresourceIndex].uav); - CHECK_D3D11_ERROR_AND_RETURN("Could not create UAV!", NULL); - } - } - } - - return d3d11Texture; -} - -static bool D3D11_SupportsSampleCount( - SDL_GPURenderer *driverData, - SDL_GPUTextureFormat format, - SDL_GPUSampleCount sampleCount) -{ - D3D11Renderer *renderer = (D3D11Renderer *)driverData; - Uint32 levels; - - HRESULT res = ID3D11Device_CheckMultisampleQualityLevels( - renderer->device, - SDLToD3D11_TextureFormat[format], - SDLToD3D11_SampleCount[sampleCount], - &levels); - - return SUCCEEDED(res) && levels > 0; -} - -static SDL_GPUTexture *D3D11_CreateTexture( - SDL_GPURenderer *driverData, - const SDL_GPUTextureCreateInfo *createinfo) -{ - D3D11Renderer *renderer = (D3D11Renderer *)driverData; - D3D11TextureContainer *container; - D3D11Texture *texture; - - texture = D3D11_INTERNAL_CreateTexture( - renderer, - createinfo, - NULL); - - if (texture == NULL) { - return NULL; - } - - container = SDL_malloc(sizeof(D3D11TextureContainer)); - container->header.info = *createinfo; - container->canBeCycled = 1; - container->activeTexture = texture; - container->textureCapacity = 1; - container->textureCount = 1; - container->textures = SDL_malloc( - container->textureCapacity * sizeof(D3D11Texture *)); - container->textures[0] = texture; - container->debugName = NULL; - - texture->container = container; - texture->containerIndex = 0; - - return (SDL_GPUTexture *)container; -} - -static void D3D11_INTERNAL_CycleActiveTexture( - D3D11Renderer *renderer, - D3D11TextureContainer *container) -{ - for (Uint32 i = 0; i < container->textureCount; i += 1) { - if (SDL_GetAtomicInt(&container->textures[i]->referenceCount) == 0) { - container->activeTexture = container->textures[i]; - return; - } - } - - D3D11Texture *texture = D3D11_INTERNAL_CreateTexture( - renderer, - &container->header.info, - NULL); - if (texture == NULL) { - return; - } - - // No texture is available, generate a new one. - - EXPAND_ARRAY_IF_NEEDED( - container->textures, - D3D11Texture *, - container->textureCount + 1, - container->textureCapacity, - container->textureCapacity + 1); - - container->textures[container->textureCount] = texture; - texture->container = container; - texture->containerIndex = container->textureCount; - container->textureCount += 1; - - container->activeTexture = container->textures[container->textureCount - 1]; - - if (renderer->debugMode && container->debugName != NULL) { - D3D11_INTERNAL_SetTextureName( - renderer, - container->activeTexture, - container->debugName); - } -} - -static D3D11TextureSubresource *D3D11_INTERNAL_FetchTextureSubresource( - D3D11TextureContainer *container, - Uint32 layer, - Uint32 level) -{ - Uint32 index = D3D11_INTERNAL_CalcSubresource( - level, - layer, - container->header.info.num_levels); - return &container->activeTexture->subresources[index]; -} - -static D3D11TextureSubresource *D3D11_INTERNAL_PrepareTextureSubresourceForWrite( - D3D11Renderer *renderer, - D3D11TextureContainer *container, - Uint32 layer, - Uint32 level, - bool cycle) -{ - D3D11TextureSubresource *subresource = D3D11_INTERNAL_FetchTextureSubresource( - container, - layer, - level); - - if ( - container->canBeCycled && - cycle && - SDL_GetAtomicInt(&subresource->parent->referenceCount) > 0) { - D3D11_INTERNAL_CycleActiveTexture( - renderer, - container); - - subresource = D3D11_INTERNAL_FetchTextureSubresource( - container, - layer, - level); - } - - return subresource; -} - -static D3D11Buffer *D3D11_INTERNAL_CreateBuffer( - D3D11Renderer *renderer, - D3D11_BUFFER_DESC *bufferDesc, - Uint32 size) -{ - ID3D11Buffer *bufferHandle; - ID3D11UnorderedAccessView *uav = NULL; - ID3D11ShaderResourceView *srv = NULL; - D3D11Buffer *d3d11Buffer; - HRESULT res; - - // Storage buffers have to be 4-aligned, so might as well align them all - size = D3D11_INTERNAL_NextHighestAlignment(size, 4); - - res = ID3D11Device_CreateBuffer( - renderer->device, - bufferDesc, - NULL, - &bufferHandle); - CHECK_D3D11_ERROR_AND_RETURN("Could not create buffer", NULL); - - // Storage buffer - if (bufferDesc->MiscFlags & D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS) { - // Create a UAV for the buffer - - D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc; - uavDesc.Format = DXGI_FORMAT_R32_TYPELESS; - uavDesc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER; - uavDesc.Buffer.FirstElement = 0; - uavDesc.Buffer.NumElements = size / sizeof(Uint32); - uavDesc.Buffer.Flags = D3D11_BUFFER_UAV_FLAG_RAW; - - res = ID3D11Device_CreateUnorderedAccessView( - renderer->device, - (ID3D11Resource *)bufferHandle, - &uavDesc, - &uav); - if (FAILED(res)) { - ID3D11Buffer_Release(bufferHandle); - CHECK_D3D11_ERROR_AND_RETURN("Could not create UAV for buffer!", NULL); - } - - // Create a SRV for the buffer - - D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc; - srvDesc.Format = DXGI_FORMAT_R32_TYPELESS; - srvDesc.ViewDimension = D3D11_SRV_DIMENSION_BUFFEREX; - srvDesc.BufferEx.FirstElement = 0; - srvDesc.BufferEx.NumElements = size / sizeof(Uint32); - srvDesc.BufferEx.Flags = D3D11_BUFFEREX_SRV_FLAG_RAW; - - res = ID3D11Device_CreateShaderResourceView( - renderer->device, - (ID3D11Resource *)bufferHandle, - &srvDesc, - &srv); - if (FAILED(res)) { - ID3D11Buffer_Release(bufferHandle); - CHECK_D3D11_ERROR_AND_RETURN("Could not create SRV for buffer!", NULL); - } - } - - d3d11Buffer = SDL_malloc(sizeof(D3D11Buffer)); - d3d11Buffer->handle = bufferHandle; - d3d11Buffer->size = size; - d3d11Buffer->uav = uav; - d3d11Buffer->srv = srv; - SDL_SetAtomicInt(&d3d11Buffer->referenceCount, 0); - - return d3d11Buffer; -} - -static SDL_GPUBuffer *D3D11_CreateBuffer( - SDL_GPURenderer *driverData, - SDL_GPUBufferUsageFlags usageFlags, - Uint32 size) -{ - D3D11Renderer *renderer = (D3D11Renderer *)driverData; - D3D11BufferContainer *container; - D3D11Buffer *buffer; - D3D11_BUFFER_DESC bufferDesc; - - bufferDesc.BindFlags = 0; - if (usageFlags & SDL_GPU_BUFFERUSAGE_VERTEX) { - bufferDesc.BindFlags |= D3D11_BIND_VERTEX_BUFFER; - } - if (usageFlags & SDL_GPU_BUFFERUSAGE_INDEX) { - bufferDesc.BindFlags |= D3D11_BIND_INDEX_BUFFER; - } - if (usageFlags & SDL_GPU_BUFFERUSAGE_INDIRECT) { - bufferDesc.BindFlags |= D3D11_BIND_UNORDERED_ACCESS; - } - - if (usageFlags & (SDL_GPU_BUFFERUSAGE_GRAPHICS_STORAGE_READ | - SDL_GPU_BUFFERUSAGE_COMPUTE_STORAGE_READ | - SDL_GPU_BUFFERUSAGE_COMPUTE_STORAGE_WRITE)) { - bufferDesc.BindFlags |= D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_SHADER_RESOURCE; - } - - bufferDesc.ByteWidth = size; - bufferDesc.Usage = D3D11_USAGE_DEFAULT; - bufferDesc.CPUAccessFlags = 0; - bufferDesc.StructureByteStride = 0; - bufferDesc.MiscFlags = 0; - - if (usageFlags & SDL_GPU_BUFFERUSAGE_INDIRECT) { - bufferDesc.MiscFlags |= D3D11_RESOURCE_MISC_DRAWINDIRECT_ARGS; - } - if (usageFlags & (SDL_GPU_BUFFERUSAGE_GRAPHICS_STORAGE_READ | - SDL_GPU_BUFFERUSAGE_COMPUTE_STORAGE_READ | - SDL_GPU_BUFFERUSAGE_COMPUTE_STORAGE_WRITE)) { - bufferDesc.MiscFlags |= D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS; - } - - buffer = D3D11_INTERNAL_CreateBuffer( - renderer, - &bufferDesc, - size); - - if (buffer == NULL) { - return NULL; - } - - container = SDL_malloc(sizeof(D3D11BufferContainer)); - container->activeBuffer = buffer; - container->bufferCapacity = 1; - container->bufferCount = 1; - container->buffers = SDL_malloc( - container->bufferCapacity * sizeof(D3D11Buffer *)); - container->buffers[0] = container->activeBuffer; - container->bufferDesc = bufferDesc; - container->debugName = NULL; - - return (SDL_GPUBuffer *)container; -} - -static D3D11UniformBuffer *D3D11_INTERNAL_CreateUniformBuffer( - D3D11Renderer *renderer, - Uint32 size) -{ - D3D11UniformBuffer *uniformBuffer; - ID3D11Buffer *buffer; - D3D11_BUFFER_DESC bufferDesc; - HRESULT res; - - bufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; - bufferDesc.ByteWidth = size; - bufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; - bufferDesc.MiscFlags = 0; - bufferDesc.StructureByteStride = 0; - bufferDesc.Usage = D3D11_USAGE_DYNAMIC; - - res = ID3D11Device_CreateBuffer( - renderer->device, - &bufferDesc, - NULL, - &buffer); - CHECK_D3D11_ERROR_AND_RETURN("Could not create uniform buffer", NULL) - - uniformBuffer = SDL_malloc(sizeof(D3D11UniformBuffer)); - uniformBuffer->buffer = buffer; - uniformBuffer->writeOffset = 0; - uniformBuffer->drawOffset = 0; - uniformBuffer->currentBlockSize = 0; - - return uniformBuffer; -} - -static void D3D11_INTERNAL_CycleActiveBuffer( - D3D11Renderer *renderer, - D3D11BufferContainer *container) -{ - Uint32 size = container->activeBuffer->size; - - for (Uint32 i = 0; i < container->bufferCount; i += 1) { - if (SDL_GetAtomicInt(&container->buffers[i]->referenceCount) == 0) { - container->activeBuffer = container->buffers[i]; - return; - } - } - - EXPAND_ARRAY_IF_NEEDED( - container->buffers, - D3D11Buffer *, - container->bufferCount + 1, - container->bufferCapacity, - container->bufferCapacity + 1); - - container->buffers[container->bufferCount] = D3D11_INTERNAL_CreateBuffer( - renderer, - &container->bufferDesc, - size); - container->bufferCount += 1; - - container->activeBuffer = container->buffers[container->bufferCount - 1]; - - if (renderer->debugMode && container->debugName != NULL) { - D3D11_INTERNAL_SetBufferName( - renderer, - container->activeBuffer, - container->debugName); - } -} - -static D3D11Buffer *D3D11_INTERNAL_PrepareBufferForWrite( - D3D11Renderer *renderer, - D3D11BufferContainer *container, - bool cycle) -{ - if ( - cycle && - SDL_GetAtomicInt(&container->activeBuffer->referenceCount) > 0) { - D3D11_INTERNAL_CycleActiveBuffer( - renderer, - container); - } - - return container->activeBuffer; -} - -static D3D11TransferBuffer *D3D11_INTERNAL_CreateTransferBuffer( - D3D11Renderer *renderer, - Uint32 size) -{ - D3D11TransferBuffer *transferBuffer = SDL_malloc(sizeof(D3D11TransferBuffer)); - - transferBuffer->data = (Uint8 *)SDL_malloc(size); - transferBuffer->size = size; - SDL_SetAtomicInt(&transferBuffer->referenceCount, 0); - - transferBuffer->bufferDownloads = NULL; - transferBuffer->bufferDownloadCount = 0; - transferBuffer->bufferDownloadCapacity = 0; - - transferBuffer->textureDownloads = NULL; - transferBuffer->textureDownloadCount = 0; - transferBuffer->textureDownloadCapacity = 0; - - return transferBuffer; -} - -// This actually returns a container handle so we can rotate buffers on Cycle. -static SDL_GPUTransferBuffer *D3D11_CreateTransferBuffer( - SDL_GPURenderer *driverData, - SDL_GPUTransferBufferUsage usage, // ignored on D3D11 - Uint32 size) -{ - D3D11Renderer *renderer = (D3D11Renderer *)driverData; - D3D11TransferBufferContainer *container = (D3D11TransferBufferContainer *)SDL_malloc(sizeof(D3D11TransferBufferContainer)); - - container->bufferCapacity = 1; - container->bufferCount = 1; - container->buffers = SDL_malloc( - container->bufferCapacity * sizeof(D3D11TransferBuffer *)); - - container->buffers[0] = D3D11_INTERNAL_CreateTransferBuffer( - renderer, - size); - - container->activeBuffer = container->buffers[0]; - - return (SDL_GPUTransferBuffer *)container; -} - -// TransferBuffer Data - -static void D3D11_INTERNAL_CycleActiveTransferBuffer( - D3D11Renderer *renderer, - D3D11TransferBufferContainer *container) -{ - Uint32 size = container->activeBuffer->size; - - for (Uint32 i = 0; i < container->bufferCount; i += 1) { - if (SDL_GetAtomicInt(&container->buffers[i]->referenceCount) == 0) { - container->activeBuffer = container->buffers[i]; - return; - } - } - - EXPAND_ARRAY_IF_NEEDED( - container->buffers, - D3D11TransferBuffer *, - container->bufferCount + 1, - container->bufferCapacity, - container->bufferCapacity + 1); - - container->buffers[container->bufferCount] = D3D11_INTERNAL_CreateTransferBuffer( - renderer, - size); - container->bufferCount += 1; - - container->activeBuffer = container->buffers[container->bufferCount - 1]; -} - -static void *D3D11_MapTransferBuffer( - SDL_GPURenderer *driverData, - SDL_GPUTransferBuffer *transferBuffer, - bool cycle) -{ - D3D11Renderer *renderer = (D3D11Renderer *)driverData; - D3D11TransferBufferContainer *container = (D3D11TransferBufferContainer *)transferBuffer; - D3D11TransferBuffer *buffer = container->activeBuffer; - - // Rotate the transfer buffer if necessary - if ( - cycle && - SDL_GetAtomicInt(&container->activeBuffer->referenceCount) > 0) { - D3D11_INTERNAL_CycleActiveTransferBuffer( - renderer, - container); - buffer = container->activeBuffer; - } - - return buffer->data; -} - -static void D3D11_UnmapTransferBuffer( - SDL_GPURenderer *driverData, - SDL_GPUTransferBuffer *transferBuffer) -{ - // no-op - (void)driverData; - (void)transferBuffer; -} - -// Copy Pass - -static void D3D11_BeginCopyPass( - SDL_GPUCommandBuffer *commandBuffer) -{ - // no-op -} - -static void D3D11_UploadToTexture( - SDL_GPUCommandBuffer *commandBuffer, - const SDL_GPUTextureTransferInfo *source, - const SDL_GPUTextureRegion *destination, - bool cycle) -{ - D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer; - D3D11Renderer *renderer = (D3D11Renderer *)d3d11CommandBuffer->renderer; - D3D11TransferBufferContainer *srcTransferContainer = (D3D11TransferBufferContainer *)source->transfer_buffer; - D3D11TransferBuffer *srcTransferBuffer = srcTransferContainer->activeBuffer; - D3D11TextureContainer *dstTextureContainer = (D3D11TextureContainer *)destination->texture; - SDL_GPUTextureFormat dstFormat = dstTextureContainer->header.info.format; - Uint32 bufferStride = source->pixels_per_row; - Uint32 bufferImageHeight = source->rows_per_layer; - Sint32 w = destination->w; - Sint32 h = destination->h; - D3D11Texture *stagingTexture; - SDL_GPUTextureCreateInfo stagingTextureCreateInfo; - D3D11_SUBRESOURCE_DATA initialData; - - D3D11TextureSubresource *textureSubresource = D3D11_INTERNAL_PrepareTextureSubresourceForWrite( - renderer, - dstTextureContainer, - destination->layer, - destination->mip_level, - cycle); - - Sint32 blockWidth = Texture_GetBlockWidth(dstFormat); - Sint32 blockHeight = Texture_GetBlockHeight(dstFormat); - if (blockWidth > 1 && blockHeight > 1) { - w = (w + blockWidth - 1) & ~(blockWidth - 1); - h = (h + blockHeight - 1) & ~(blockHeight - 1); - } - - if (bufferStride == 0) { - bufferStride = w; - } - - if (bufferImageHeight == 0) { - bufferImageHeight = h; - } - - Uint32 bytesPerRow = BytesPerRow(bufferStride, dstFormat); - Uint32 bytesPerDepthSlice = bytesPerRow * bufferImageHeight; - - /* UpdateSubresource1 is completely busted on AMD, it truncates after X bytes. - * So we get to do this Fun (Tm) workaround where we create a staging texture - * with initial data before issuing a copy command. - */ - - stagingTextureCreateInfo.width = w; - stagingTextureCreateInfo.height = h; - stagingTextureCreateInfo.layer_count_or_depth = 1; - stagingTextureCreateInfo.num_levels = 1; - stagingTextureCreateInfo.type = SDL_GPU_TEXTURETYPE_2D; - stagingTextureCreateInfo.usage = 0; - stagingTextureCreateInfo.sample_count = SDL_GPU_SAMPLECOUNT_1; - stagingTextureCreateInfo.format = dstFormat; - - initialData.pSysMem = srcTransferBuffer->data + source->offset; - initialData.SysMemPitch = bytesPerRow; - initialData.SysMemSlicePitch = bytesPerDepthSlice; - - stagingTexture = D3D11_INTERNAL_CreateTexture( - renderer, - &stagingTextureCreateInfo, - &initialData); - - if (stagingTexture == NULL) { - return; - } - - ID3D11DeviceContext_CopySubresourceRegion( - d3d11CommandBuffer->context, - textureSubresource->parent->handle, - textureSubresource->index, - destination->x, - destination->y, - destination->z, - stagingTexture->handle, - 0, - NULL); - - // Clean up the staging texture - D3D11_INTERNAL_DestroyTexture(stagingTexture); - - D3D11_INTERNAL_TrackTexture(d3d11CommandBuffer, textureSubresource->parent); - D3D11_INTERNAL_TrackTransferBuffer(d3d11CommandBuffer, srcTransferBuffer); -} - -static void D3D11_UploadToBuffer( - SDL_GPUCommandBuffer *commandBuffer, - const SDL_GPUTransferBufferLocation *source, - const SDL_GPUBufferRegion *destination, - bool cycle) -{ - D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer; - D3D11Renderer *renderer = (D3D11Renderer *)d3d11CommandBuffer->renderer; - D3D11TransferBufferContainer *transferContainer = (D3D11TransferBufferContainer *)source->transfer_buffer; - D3D11TransferBuffer *d3d11TransferBuffer = transferContainer->activeBuffer; - D3D11BufferContainer *bufferContainer = (D3D11BufferContainer *)destination->buffer; - D3D11Buffer *d3d11Buffer = D3D11_INTERNAL_PrepareBufferForWrite( - renderer, - bufferContainer, - cycle); - ID3D11Buffer *stagingBuffer; - D3D11_BUFFER_DESC stagingBufferDesc; - D3D11_SUBRESOURCE_DATA stagingBufferData; - HRESULT res; - - // Upload to staging buffer immediately - stagingBufferDesc.ByteWidth = destination->size; - stagingBufferDesc.Usage = D3D11_USAGE_STAGING; - stagingBufferDesc.BindFlags = 0; - stagingBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; - stagingBufferDesc.MiscFlags = 0; - stagingBufferDesc.StructureByteStride = 0; - - stagingBufferData.pSysMem = d3d11TransferBuffer->data + source->offset; - stagingBufferData.SysMemPitch = 0; - stagingBufferData.SysMemSlicePitch = 0; - - res = ID3D11Device_CreateBuffer( - renderer->device, - &stagingBufferDesc, - &stagingBufferData, - &stagingBuffer); - CHECK_D3D11_ERROR_AND_RETURN("Could not create staging buffer", ) - - // Copy from staging buffer to buffer - ID3D11DeviceContext1_CopySubresourceRegion( - d3d11CommandBuffer->context, - (ID3D11Resource *)d3d11Buffer->handle, - 0, - destination->offset, - 0, - 0, - (ID3D11Resource *)stagingBuffer, - 0, - NULL); - - ID3D11Buffer_Release(stagingBuffer); - - D3D11_INTERNAL_TrackBuffer(d3d11CommandBuffer, d3d11Buffer); - D3D11_INTERNAL_TrackTransferBuffer(d3d11CommandBuffer, d3d11TransferBuffer); -} - -static void D3D11_DownloadFromTexture( - SDL_GPUCommandBuffer *commandBuffer, - const SDL_GPUTextureRegion *source, - const SDL_GPUTextureTransferInfo *destination) -{ - D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer; - D3D11Renderer *renderer = d3d11CommandBuffer->renderer; - D3D11TransferBufferContainer *dstTransferContainer = (D3D11TransferBufferContainer *)destination->transfer_buffer; - D3D11TransferBuffer *d3d11TransferBuffer = dstTransferContainer->activeBuffer; - D3D11TextureContainer *srcTextureContainer = (D3D11TextureContainer *)source->texture; - SDL_GPUTextureFormat srcFormat = srcTextureContainer->header.info.format; - D3D11_TEXTURE2D_DESC stagingDesc2D; - D3D11_TEXTURE3D_DESC stagingDesc3D; - D3D11TextureSubresource *textureSubresource = D3D11_INTERNAL_FetchTextureSubresource( - srcTextureContainer, - source->layer, - source->mip_level); - D3D11TextureDownload *textureDownload; - Uint32 bufferStride = destination->pixels_per_row; - Uint32 bufferImageHeight = destination->rows_per_layer; - Uint32 bytesPerRow, bytesPerDepthSlice; - D3D11_BOX srcBox = { source->x, source->y, source->z, source->x + source->w, source->y + source->h, source->z + source->d }; - HRESULT res; - - if (d3d11TransferBuffer->textureDownloadCount >= d3d11TransferBuffer->textureDownloadCapacity) { - d3d11TransferBuffer->textureDownloadCapacity += 1; - d3d11TransferBuffer->textureDownloads = SDL_realloc( - d3d11TransferBuffer->textureDownloads, - d3d11TransferBuffer->textureDownloadCapacity * sizeof(D3D11TextureDownload)); - } - - textureDownload = &d3d11TransferBuffer->textureDownloads[d3d11TransferBuffer->textureDownloadCount]; - d3d11TransferBuffer->textureDownloadCount += 1; - - if (bufferStride == 0) { - bufferStride = source->w; - } - - if (bufferImageHeight == 0) { - bufferImageHeight = source->h; - } - - bytesPerRow = BytesPerRow(bufferStride, srcFormat); - bytesPerDepthSlice = bytesPerRow * bufferImageHeight; - - if (source->d == 1) { - stagingDesc2D.Width = source->w; - stagingDesc2D.Height = source->h; - stagingDesc2D.MipLevels = 1; - stagingDesc2D.ArraySize = 1; - stagingDesc2D.Format = SDLToD3D11_TextureFormat[srcFormat]; - stagingDesc2D.SampleDesc.Count = 1; - stagingDesc2D.SampleDesc.Quality = 0; - stagingDesc2D.Usage = D3D11_USAGE_STAGING; - stagingDesc2D.BindFlags = 0; - stagingDesc2D.CPUAccessFlags = D3D11_CPU_ACCESS_READ; - stagingDesc2D.MiscFlags = 0; - - res = ID3D11Device_CreateTexture2D( - renderer->device, - &stagingDesc2D, - NULL, - (ID3D11Texture2D **)&textureDownload->stagingTexture); - CHECK_D3D11_ERROR_AND_RETURN("Staging texture creation failed", ) - } else { - stagingDesc3D.Width = source->w; - stagingDesc3D.Height = source->h; - stagingDesc3D.Depth = source->d; - stagingDesc3D.MipLevels = 1; - stagingDesc3D.Format = SDLToD3D11_TextureFormat[srcFormat]; - stagingDesc3D.Usage = D3D11_USAGE_STAGING; - stagingDesc3D.BindFlags = 0; - stagingDesc3D.CPUAccessFlags = D3D11_CPU_ACCESS_READ; - stagingDesc3D.MiscFlags = 0; - - res = ID3D11Device_CreateTexture3D( - renderer->device, - &stagingDesc3D, - NULL, - (ID3D11Texture3D **)&textureDownload->stagingTexture); - } - - textureDownload->width = source->w; - textureDownload->height = source->h; - textureDownload->depth = source->d; - textureDownload->bufferOffset = destination->offset; - textureDownload->bytesPerRow = bytesPerRow; - textureDownload->bytesPerDepthSlice = bytesPerDepthSlice; - - ID3D11DeviceContext1_CopySubresourceRegion1( - d3d11CommandBuffer->context, - textureDownload->stagingTexture, - 0, - 0, - 0, - 0, - textureSubresource->parent->handle, - textureSubresource->index, - &srcBox, - D3D11_COPY_NO_OVERWRITE); - - D3D11_INTERNAL_TrackTexture(d3d11CommandBuffer, textureSubresource->parent); - D3D11_INTERNAL_TrackTransferBuffer(d3d11CommandBuffer, d3d11TransferBuffer); -} - -static void D3D11_DownloadFromBuffer( - SDL_GPUCommandBuffer *commandBuffer, - const SDL_GPUBufferRegion *source, - const SDL_GPUTransferBufferLocation *destination) -{ - D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer; - D3D11Renderer *renderer = d3d11CommandBuffer->renderer; - D3D11TransferBufferContainer *dstTransferContainer = (D3D11TransferBufferContainer *)destination->transfer_buffer; - D3D11TransferBuffer *d3d11TransferBuffer = dstTransferContainer->activeBuffer; - D3D11BufferContainer *srcBufferContainer = (D3D11BufferContainer *)source->buffer; - D3D11BufferDownload *bufferDownload; - D3D11_BOX srcBox = { source->offset, 0, 0, source->size, 1, 1 }; - D3D11_BUFFER_DESC stagingBufferDesc; - HRESULT res; - - if (d3d11TransferBuffer->bufferDownloadCount >= d3d11TransferBuffer->bufferDownloadCapacity) { - d3d11TransferBuffer->bufferDownloadCapacity += 1; - d3d11TransferBuffer->bufferDownloads = SDL_realloc( - d3d11TransferBuffer->bufferDownloads, - d3d11TransferBuffer->bufferDownloadCapacity * sizeof(D3D11BufferDownload)); - } - - bufferDownload = &d3d11TransferBuffer->bufferDownloads[d3d11TransferBuffer->bufferDownloadCount]; - d3d11TransferBuffer->bufferDownloadCount += 1; - - stagingBufferDesc.ByteWidth = source->size; - stagingBufferDesc.Usage = D3D11_USAGE_STAGING; - stagingBufferDesc.BindFlags = 0; - stagingBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; - stagingBufferDesc.MiscFlags = 0; - stagingBufferDesc.StructureByteStride = 0; - - res = ID3D11Device_CreateBuffer( - renderer->device, - &stagingBufferDesc, - NULL, - &bufferDownload->stagingBuffer); - CHECK_D3D11_ERROR_AND_RETURN("Could not create staging buffer", ) - - ID3D11DeviceContext1_CopySubresourceRegion1( - d3d11CommandBuffer->context, - (ID3D11Resource *)bufferDownload->stagingBuffer, - 0, - 0, - 0, - 0, - (ID3D11Resource *)srcBufferContainer->activeBuffer->handle, - 0, - &srcBox, - D3D11_COPY_NO_OVERWRITE); - - bufferDownload->dstOffset = destination->offset; - bufferDownload->size = source->size; - - D3D11_INTERNAL_TrackBuffer(d3d11CommandBuffer, srcBufferContainer->activeBuffer); - D3D11_INTERNAL_TrackTransferBuffer(d3d11CommandBuffer, d3d11TransferBuffer); -} - -static void D3D11_CopyTextureToTexture( - SDL_GPUCommandBuffer *commandBuffer, - const SDL_GPUTextureLocation *source, - const SDL_GPUTextureLocation *destination, - Uint32 w, - Uint32 h, - Uint32 d, - bool cycle) -{ - D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer; - D3D11Renderer *renderer = (D3D11Renderer *)d3d11CommandBuffer->renderer; - D3D11TextureContainer *srcContainer = (D3D11TextureContainer *)source->texture; - D3D11TextureContainer *dstContainer = (D3D11TextureContainer *)destination->texture; - - D3D11_BOX srcBox = { source->x, source->y, source->z, source->x + w, source->y + h, source->z + d }; - - D3D11TextureSubresource *srcSubresource = D3D11_INTERNAL_FetchTextureSubresource( - srcContainer, - source->layer, - source->mip_level); - - D3D11TextureSubresource *dstSubresource = D3D11_INTERNAL_PrepareTextureSubresourceForWrite( - renderer, - dstContainer, - destination->layer, - destination->mip_level, - cycle); - - ID3D11DeviceContext1_CopySubresourceRegion( - d3d11CommandBuffer->context, - dstSubresource->parent->handle, - dstSubresource->index, - destination->x, - destination->y, - destination->z, - srcSubresource->parent->handle, - srcSubresource->index, - &srcBox); - - D3D11_INTERNAL_TrackTexture(d3d11CommandBuffer, srcSubresource->parent); - D3D11_INTERNAL_TrackTexture(d3d11CommandBuffer, dstSubresource->parent); -} - -static void D3D11_CopyBufferToBuffer( - SDL_GPUCommandBuffer *commandBuffer, - const SDL_GPUBufferLocation *source, - const SDL_GPUBufferLocation *destination, - Uint32 size, - bool cycle) -{ - D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer; - D3D11Renderer *renderer = (D3D11Renderer *)d3d11CommandBuffer->renderer; - D3D11BufferContainer *srcBufferContainer = (D3D11BufferContainer *)source->buffer; - D3D11BufferContainer *dstBufferContainer = (D3D11BufferContainer *)destination->buffer; - D3D11_BOX srcBox = { source->offset, 0, 0, source->offset + size, 1, 1 }; - - D3D11Buffer *srcBuffer = srcBufferContainer->activeBuffer; - D3D11Buffer *dstBuffer = D3D11_INTERNAL_PrepareBufferForWrite( - renderer, - dstBufferContainer, - cycle); - - ID3D11DeviceContext1_CopySubresourceRegion( - d3d11CommandBuffer->context, - (ID3D11Resource *)dstBuffer->handle, - 0, - destination->offset, - 0, - 0, - (ID3D11Resource *)srcBuffer->handle, - 0, - &srcBox); - - D3D11_INTERNAL_TrackBuffer(d3d11CommandBuffer, srcBuffer); - D3D11_INTERNAL_TrackBuffer(d3d11CommandBuffer, dstBuffer); -} - -static void D3D11_GenerateMipmaps( - SDL_GPUCommandBuffer *commandBuffer, - SDL_GPUTexture *texture) -{ - D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer; - D3D11TextureContainer *d3d11TextureContainer = (D3D11TextureContainer *)texture; - - ID3D11DeviceContext1_GenerateMips( - d3d11CommandBuffer->context, - d3d11TextureContainer->activeTexture->shaderView); - - D3D11_INTERNAL_TrackTexture( - d3d11CommandBuffer, - d3d11TextureContainer->activeTexture); -} - -static void D3D11_EndCopyPass( - SDL_GPUCommandBuffer *commandBuffer) -{ - // no-op -} - -// Graphics State - -static void D3D11_INTERNAL_AllocateCommandBuffers( - D3D11Renderer *renderer, - Uint32 allocateCount) -{ - D3D11CommandBuffer *commandBuffer; - HRESULT res; - - renderer->availableCommandBufferCapacity += allocateCount; - - renderer->availableCommandBuffers = SDL_realloc( - renderer->availableCommandBuffers, - sizeof(D3D11CommandBuffer *) * renderer->availableCommandBufferCapacity); - - for (Uint32 i = 0; i < allocateCount; i += 1) { - commandBuffer = SDL_calloc(1, sizeof(D3D11CommandBuffer)); - commandBuffer->renderer = renderer; - - // Deferred Device Context - res = ID3D11Device1_CreateDeferredContext1( - renderer->device, - 0, - &commandBuffer->context); - CHECK_D3D11_ERROR_AND_RETURN("Could not create deferred context", ); - - // Initialize debug annotation support, if available - ID3D11DeviceContext_QueryInterface( - commandBuffer->context, - &D3D_IID_ID3DUserDefinedAnnotation, - (void **)&commandBuffer->annotation); - - // Window handling - commandBuffer->windowDataCapacity = 1; - commandBuffer->windowDataCount = 0; - commandBuffer->windowDatas = SDL_malloc( - commandBuffer->windowDataCapacity * sizeof(D3D11WindowData *)); - - // Reference Counting - commandBuffer->usedBufferCapacity = 4; - commandBuffer->usedBufferCount = 0; - commandBuffer->usedBuffers = SDL_malloc( - commandBuffer->usedBufferCapacity * sizeof(D3D11Buffer *)); - - commandBuffer->usedTransferBufferCapacity = 4; - commandBuffer->usedTransferBufferCount = 0; - commandBuffer->usedTransferBuffers = SDL_malloc( - commandBuffer->usedTransferBufferCapacity * sizeof(D3D11TransferBuffer *)); - - commandBuffer->usedTextureCapacity = 4; - commandBuffer->usedTextureCount = 0; - commandBuffer->usedTextures = SDL_malloc( - commandBuffer->usedTextureCapacity * sizeof(D3D11Texture *)); - - commandBuffer->usedUniformBufferCapacity = 4; - commandBuffer->usedUniformBufferCount = 0; - commandBuffer->usedUniformBuffers = SDL_malloc( - commandBuffer->usedUniformBufferCapacity * sizeof(D3D11UniformBuffer *)); - - renderer->availableCommandBuffers[renderer->availableCommandBufferCount] = commandBuffer; - renderer->availableCommandBufferCount += 1; - } -} - -static D3D11CommandBuffer *D3D11_INTERNAL_GetInactiveCommandBufferFromPool( - D3D11Renderer *renderer) -{ - D3D11CommandBuffer *commandBuffer; - - if (renderer->availableCommandBufferCount == 0) { - D3D11_INTERNAL_AllocateCommandBuffers( - renderer, - renderer->availableCommandBufferCapacity); - } - - commandBuffer = renderer->availableCommandBuffers[renderer->availableCommandBufferCount - 1]; - renderer->availableCommandBufferCount -= 1; - - return commandBuffer; -} - -static bool D3D11_INTERNAL_CreateFence( - D3D11Renderer *renderer) -{ - D3D11_QUERY_DESC queryDesc; - ID3D11Query *queryHandle; - D3D11Fence *fence; - HRESULT res; - - queryDesc.Query = D3D11_QUERY_EVENT; - queryDesc.MiscFlags = 0; - res = ID3D11Device_CreateQuery( - renderer->device, - &queryDesc, - &queryHandle); - CHECK_D3D11_ERROR_AND_RETURN("Could not create query", false); - - fence = SDL_malloc(sizeof(D3D11Fence)); - fence->handle = queryHandle; - SDL_SetAtomicInt(&fence->referenceCount, 0); - - // Add it to the available pool - if (renderer->availableFenceCount >= renderer->availableFenceCapacity) { - renderer->availableFenceCapacity *= 2; - renderer->availableFences = SDL_realloc( - renderer->availableFences, - sizeof(D3D11Fence *) * renderer->availableFenceCapacity); - } - - renderer->availableFences[renderer->availableFenceCount] = fence; - renderer->availableFenceCount += 1; - - return true; -} - -static bool D3D11_INTERNAL_AcquireFence( - D3D11CommandBuffer *commandBuffer) -{ - D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer; - D3D11Renderer *renderer = (D3D11Renderer *)d3d11CommandBuffer->renderer; - D3D11Fence *fence; - - // Acquire a fence from the pool - SDL_LockMutex(renderer->fenceLock); - - if (renderer->availableFenceCount == 0) { - if (!D3D11_INTERNAL_CreateFence(renderer)) { - SDL_UnlockMutex(renderer->fenceLock); - return false; - } - } - - fence = renderer->availableFences[renderer->availableFenceCount - 1]; - renderer->availableFenceCount -= 1; - - SDL_UnlockMutex(renderer->fenceLock); - - // Associate the fence with the command buffer - commandBuffer->fence = fence; - (void)SDL_AtomicIncRef(&commandBuffer->fence->referenceCount); - - return true; -} - -static SDL_GPUCommandBuffer *D3D11_AcquireCommandBuffer( - SDL_GPURenderer *driverData) -{ - D3D11Renderer *renderer = (D3D11Renderer *)driverData; - D3D11CommandBuffer *commandBuffer; - Uint32 i; - - SDL_LockMutex(renderer->acquireCommandBufferLock); - - commandBuffer = D3D11_INTERNAL_GetInactiveCommandBufferFromPool(renderer); - commandBuffer->graphicsPipeline = NULL; - commandBuffer->stencilRef = 0; - commandBuffer->blendConstants.r = 1.0f; - commandBuffer->blendConstants.g = 1.0f; - commandBuffer->blendConstants.b = 1.0f; - commandBuffer->blendConstants.a = 1.0f; - commandBuffer->computePipeline = NULL; - for (i = 0; i < MAX_COLOR_TARGET_BINDINGS; i += 1) { - commandBuffer->colorTargetSubresources[i] = NULL; - commandBuffer->colorResolveSubresources[i] = NULL; - } - - for (i = 0; i < MAX_UNIFORM_BUFFERS_PER_STAGE; i += 1) { - commandBuffer->vertexUniformBuffers[i] = NULL; - commandBuffer->fragmentUniformBuffers[i] = NULL; - commandBuffer->computeUniformBuffers[i] = NULL; - } - - commandBuffer->needVertexSamplerBind = true; - commandBuffer->needVertexStorageTextureBind = true; - commandBuffer->needVertexStorageBufferBind = true; - commandBuffer->needVertexUniformBufferBind = true; - commandBuffer->needFragmentSamplerBind = true; - commandBuffer->needFragmentStorageTextureBind = true; - commandBuffer->needFragmentStorageBufferBind = true; - commandBuffer->needFragmentUniformBufferBind = true; - commandBuffer->needComputeUniformBufferBind = true; - commandBuffer->needBlendStateSet = true; - - SDL_zeroa(commandBuffer->vertexSamplers); - SDL_zeroa(commandBuffer->vertexSamplerTextures); - SDL_zeroa(commandBuffer->vertexStorageTextures); - SDL_zeroa(commandBuffer->vertexStorageBuffers); - - SDL_zeroa(commandBuffer->fragmentSamplers); - SDL_zeroa(commandBuffer->fragmentSamplerTextures); - SDL_zeroa(commandBuffer->fragmentStorageTextures); - SDL_zeroa(commandBuffer->fragmentStorageBuffers); - - SDL_zeroa(commandBuffer->computeSamplers); - SDL_zeroa(commandBuffer->computeSamplerTextures); - SDL_zeroa(commandBuffer->computeReadOnlyStorageTextures); - SDL_zeroa(commandBuffer->computeReadOnlyStorageBuffers); - SDL_zeroa(commandBuffer->computeReadWriteStorageTextureSubresources); - SDL_zeroa(commandBuffer->computeReadWriteStorageBuffers); - - commandBuffer->autoReleaseFence = true; - - SDL_UnlockMutex(renderer->acquireCommandBufferLock); - - return (SDL_GPUCommandBuffer *)commandBuffer; -} - -static D3D11UniformBuffer *D3D11_INTERNAL_AcquireUniformBufferFromPool( - D3D11CommandBuffer *commandBuffer) -{ - D3D11Renderer *renderer = commandBuffer->renderer; - D3D11UniformBuffer *uniformBuffer; - - SDL_LockMutex(renderer->acquireUniformBufferLock); - - if (renderer->uniformBufferPoolCount > 0) { - uniformBuffer = renderer->uniformBufferPool[renderer->uniformBufferPoolCount - 1]; - renderer->uniformBufferPoolCount -= 1; - } else { - uniformBuffer = D3D11_INTERNAL_CreateUniformBuffer( - renderer, - UNIFORM_BUFFER_SIZE); - } - - SDL_UnlockMutex(renderer->acquireUniformBufferLock); - - D3D11_INTERNAL_TrackUniformBuffer(commandBuffer, uniformBuffer); - - return uniformBuffer; -} - -static void D3D11_INTERNAL_ReturnUniformBufferToPool( - D3D11Renderer *renderer, - D3D11UniformBuffer *uniformBuffer) -{ - if (renderer->uniformBufferPoolCount >= renderer->uniformBufferPoolCapacity) { - renderer->uniformBufferPoolCapacity *= 2; - renderer->uniformBufferPool = SDL_realloc( - renderer->uniformBufferPool, - renderer->uniformBufferPoolCapacity * sizeof(D3D11UniformBuffer *)); - } - - renderer->uniformBufferPool[renderer->uniformBufferPoolCount] = uniformBuffer; - renderer->uniformBufferPoolCount += 1; - - uniformBuffer->writeOffset = 0; - uniformBuffer->drawOffset = 0; - uniformBuffer->mappedData = NULL; -} - -static void D3D11_INTERNAL_PushUniformData( - D3D11CommandBuffer *d3d11CommandBuffer, - SDL_GPUShaderStage shaderStage, - Uint32 slotIndex, - const void *data, - Uint32 length) -{ - D3D11Renderer *renderer = d3d11CommandBuffer->renderer; - D3D11UniformBuffer *d3d11UniformBuffer; - D3D11_MAPPED_SUBRESOURCE subres; - HRESULT res; - - if (shaderStage == SDL_GPU_SHADERSTAGE_VERTEX) { - if (d3d11CommandBuffer->vertexUniformBuffers[slotIndex] == NULL) { - d3d11CommandBuffer->vertexUniformBuffers[slotIndex] = D3D11_INTERNAL_AcquireUniformBufferFromPool( - d3d11CommandBuffer); - } - d3d11UniformBuffer = d3d11CommandBuffer->vertexUniformBuffers[slotIndex]; - } else if (shaderStage == SDL_GPU_SHADERSTAGE_FRAGMENT) { - if (d3d11CommandBuffer->fragmentUniformBuffers[slotIndex] == NULL) { - d3d11CommandBuffer->fragmentUniformBuffers[slotIndex] = D3D11_INTERNAL_AcquireUniformBufferFromPool( - d3d11CommandBuffer); - } - d3d11UniformBuffer = d3d11CommandBuffer->fragmentUniformBuffers[slotIndex]; - } else if (shaderStage == SDL_GPU_SHADERSTAGE_COMPUTE) { - if (d3d11CommandBuffer->computeUniformBuffers[slotIndex] == NULL) { - d3d11CommandBuffer->computeUniformBuffers[slotIndex] = D3D11_INTERNAL_AcquireUniformBufferFromPool( - d3d11CommandBuffer); - } - d3d11UniformBuffer = d3d11CommandBuffer->computeUniformBuffers[slotIndex]; - } else { - SDL_LogError(SDL_LOG_CATEGORY_GPU, "Unrecognized shader stage!"); - return; - } - - d3d11UniformBuffer->currentBlockSize = - D3D11_INTERNAL_NextHighestAlignment( - length, - 256); - - // If there is no more room, acquire a new uniform buffer - if (d3d11UniformBuffer->writeOffset + d3d11UniformBuffer->currentBlockSize >= UNIFORM_BUFFER_SIZE) { - ID3D11DeviceContext_Unmap( - d3d11CommandBuffer->context, - (ID3D11Resource *)d3d11UniformBuffer->buffer, - 0); - d3d11UniformBuffer->mappedData = NULL; - - d3d11UniformBuffer = D3D11_INTERNAL_AcquireUniformBufferFromPool(d3d11CommandBuffer); - - d3d11UniformBuffer->drawOffset = 0; - d3d11UniformBuffer->writeOffset = 0; - - if (shaderStage == SDL_GPU_SHADERSTAGE_VERTEX) { - d3d11CommandBuffer->vertexUniformBuffers[slotIndex] = d3d11UniformBuffer; - } else if (shaderStage == SDL_GPU_SHADERSTAGE_FRAGMENT) { - d3d11CommandBuffer->fragmentUniformBuffers[slotIndex] = d3d11UniformBuffer; - } else if (shaderStage == SDL_GPU_SHADERSTAGE_COMPUTE) { - d3d11CommandBuffer->computeUniformBuffers[slotIndex] = d3d11UniformBuffer; - } else { - SDL_LogError(SDL_LOG_CATEGORY_GPU, "Unrecognized shader stage!"); - } - } - - // Map the uniform data on first push - if (d3d11UniformBuffer->writeOffset == 0) { - res = ID3D11DeviceContext_Map( - d3d11CommandBuffer->context, - (ID3D11Resource *)d3d11UniformBuffer->buffer, - 0, - D3D11_MAP_WRITE_DISCARD, - 0, - &subres); - CHECK_D3D11_ERROR_AND_RETURN("Failed to map uniform buffer", ) - - d3d11UniformBuffer->mappedData = subres.pData; - } - - d3d11UniformBuffer->drawOffset = d3d11UniformBuffer->writeOffset; - - SDL_memcpy( - (Uint8 *)d3d11UniformBuffer->mappedData + d3d11UniformBuffer->writeOffset, - data, - length); - - d3d11UniformBuffer->writeOffset += d3d11UniformBuffer->currentBlockSize; - - if (shaderStage == SDL_GPU_SHADERSTAGE_VERTEX) { - d3d11CommandBuffer->needVertexUniformBufferBind = true; - } else if (shaderStage == SDL_GPU_SHADERSTAGE_FRAGMENT) { - d3d11CommandBuffer->needFragmentUniformBufferBind = true; - } else if (shaderStage == SDL_GPU_SHADERSTAGE_COMPUTE) { - d3d11CommandBuffer->needComputeUniformBufferBind = true; - } else { - SDL_LogError(SDL_LOG_CATEGORY_GPU, "Unrecognized shader stage!"); - } -} - -static void D3D11_SetViewport( - SDL_GPUCommandBuffer *commandBuffer, - const SDL_GPUViewport *viewport) -{ - D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer; - D3D11_VIEWPORT vp = { - viewport->x, - viewport->y, - viewport->w, - viewport->h, - viewport->min_depth, - viewport->max_depth - }; - - ID3D11DeviceContext_RSSetViewports( - d3d11CommandBuffer->context, - 1, - &vp); -} - -static void D3D11_SetScissor( - SDL_GPUCommandBuffer *commandBuffer, - const SDL_Rect *scissor) -{ - D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer; - D3D11_RECT rect = { - scissor->x, - scissor->y, - scissor->x + scissor->w, - scissor->y + scissor->h - }; - - ID3D11DeviceContext_RSSetScissorRects( - d3d11CommandBuffer->context, - 1, - &rect); -} - -static void D3D11_SetBlendConstants( - SDL_GPUCommandBuffer *commandBuffer, - SDL_FColor blendConstants) -{ - D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer; - d3d11CommandBuffer->blendConstants = blendConstants; - d3d11CommandBuffer->needBlendStateSet = true; -} - -static void D3D11_SetStencilReference( - SDL_GPUCommandBuffer *commandBuffer, - Uint8 reference) -{ - D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer; - - d3d11CommandBuffer->stencilRef = reference; - - if (d3d11CommandBuffer->graphicsPipeline != NULL) { - ID3D11DeviceContext_OMSetDepthStencilState( - d3d11CommandBuffer->context, - d3d11CommandBuffer->graphicsPipeline->depthStencilState, - reference); - } -} - -static void D3D11_BeginRenderPass( - SDL_GPUCommandBuffer *commandBuffer, - const SDL_GPUColorTargetInfo *colorTargetInfos, - Uint32 numColorTargets, - const SDL_GPUDepthStencilTargetInfo *depthStencilTargetInfo) -{ - D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer; - D3D11Renderer *renderer = (D3D11Renderer *)d3d11CommandBuffer->renderer; - ID3D11RenderTargetView *rtvs[MAX_COLOR_TARGET_BINDINGS]; - ID3D11DepthStencilView *dsv = NULL; - Uint32 vpWidth = SDL_MAX_UINT32; - Uint32 vpHeight = SDL_MAX_UINT32; - SDL_GPUViewport viewport; - SDL_Rect scissorRect; - - // Clear the bound targets for the current command buffer - for (Uint32 i = 0; i < MAX_COLOR_TARGET_BINDINGS; i += 1) { - d3d11CommandBuffer->colorTargetSubresources[i] = NULL; - d3d11CommandBuffer->colorResolveSubresources[i] = NULL; - } - - // Set up the new color target bindings - for (Uint32 i = 0; i < numColorTargets; i += 1) { - D3D11TextureContainer *container = (D3D11TextureContainer *)colorTargetInfos[i].texture; - D3D11TextureSubresource *subresource = D3D11_INTERNAL_PrepareTextureSubresourceForWrite( - renderer, - container, - container->header.info.type == SDL_GPU_TEXTURETYPE_3D ? 0 : colorTargetInfos[i].layer_or_depth_plane, - colorTargetInfos[i].mip_level, - colorTargetInfos[i].cycle); - - Uint32 rtvIndex = container->header.info.type == SDL_GPU_TEXTURETYPE_3D ? colorTargetInfos[i].layer_or_depth_plane : 0; - rtvs[i] = subresource->colorTargetViews[rtvIndex]; - d3d11CommandBuffer->colorTargetSubresources[i] = subresource; - - if (colorTargetInfos[i].store_op == SDL_GPU_STOREOP_RESOLVE || colorTargetInfos[i].store_op == SDL_GPU_STOREOP_RESOLVE_AND_STORE) { - D3D11TextureContainer *resolveContainer = (D3D11TextureContainer *)colorTargetInfos[i].resolve_texture; - D3D11TextureSubresource *resolveSubresource = D3D11_INTERNAL_PrepareTextureSubresourceForWrite( - renderer, - resolveContainer, - colorTargetInfos[i].resolve_layer, - colorTargetInfos[i].resolve_mip_level, - colorTargetInfos[i].cycle_resolve_texture); - - d3d11CommandBuffer->colorResolveSubresources[i] = resolveSubresource; - } - - if (colorTargetInfos[i].load_op == SDL_GPU_LOADOP_CLEAR) { - float clearColor[] = { - colorTargetInfos[i].clear_color.r, - colorTargetInfos[i].clear_color.g, - colorTargetInfos[i].clear_color.b, - colorTargetInfos[i].clear_color.a - }; - ID3D11DeviceContext_ClearRenderTargetView( - d3d11CommandBuffer->context, - rtvs[i], - clearColor); - } - - D3D11_INTERNAL_TrackTexture( - d3d11CommandBuffer, - subresource->parent); - } - - // Get the DSV for the depth stencil target, if applicable - if (depthStencilTargetInfo != NULL) { - D3D11TextureContainer *container = (D3D11TextureContainer *)depthStencilTargetInfo->texture; - D3D11TextureSubresource *subresource = D3D11_INTERNAL_PrepareTextureSubresourceForWrite( - renderer, - container, - 0, - 0, - depthStencilTargetInfo->cycle); - - dsv = subresource->depthStencilTargetView; - - D3D11_INTERNAL_TrackTexture( - d3d11CommandBuffer, - subresource->parent); - } - - // Actually set the RTs - ID3D11DeviceContext_OMSetRenderTargets( - d3d11CommandBuffer->context, - numColorTargets, - numColorTargets > 0 ? rtvs : NULL, - dsv); - - if (depthStencilTargetInfo != NULL) { - D3D11_CLEAR_FLAG dsClearFlags = 0; - if (depthStencilTargetInfo->load_op == SDL_GPU_LOADOP_CLEAR) { - dsClearFlags |= D3D11_CLEAR_DEPTH; - } - if (depthStencilTargetInfo->stencil_load_op == SDL_GPU_LOADOP_CLEAR) { - dsClearFlags |= D3D11_CLEAR_STENCIL; - } - - if (dsClearFlags != 0) { - ID3D11DeviceContext_ClearDepthStencilView( - d3d11CommandBuffer->context, - dsv, - dsClearFlags, - depthStencilTargetInfo->clear_depth, - depthStencilTargetInfo->clear_stencil); - } - } - - // The viewport cannot be larger than the smallest target. - for (Uint32 i = 0; i < numColorTargets; i += 1) { - D3D11TextureContainer *container = (D3D11TextureContainer *)colorTargetInfos[i].texture; - Uint32 w = container->header.info.width >> colorTargetInfos[i].mip_level; - Uint32 h = container->header.info.height >> colorTargetInfos[i].mip_level; - - if (w < vpWidth) { - vpWidth = w; - } - - if (h < vpHeight) { - vpHeight = h; - } - } - - if (depthStencilTargetInfo != NULL) { - D3D11TextureContainer *container = (D3D11TextureContainer *)depthStencilTargetInfo->texture; - Uint32 w = container->header.info.width; - Uint32 h = container->header.info.height; - - if (w < vpWidth) { - vpWidth = w; - } - - if (h < vpHeight) { - vpHeight = h; - } - } - - // Set sensible default states - viewport.x = 0; - viewport.y = 0; - viewport.w = (float)vpWidth; - viewport.h = (float)vpHeight; - viewport.min_depth = 0; - viewport.max_depth = 1; - - D3D11_SetViewport( - commandBuffer, - &viewport); - - scissorRect.x = 0; - scissorRect.y = 0; - scissorRect.w = (int)vpWidth; - scissorRect.h = (int)vpHeight; - - D3D11_SetScissor( - commandBuffer, - &scissorRect); - - D3D11_SetStencilReference( - commandBuffer, - 0); - - SDL_FColor blendConstants; - blendConstants.r = 1.0f; - blendConstants.g = 1.0f; - blendConstants.b = 1.0f; - blendConstants.a = 1.0f; - - D3D11_SetBlendConstants( - commandBuffer, - blendConstants); -} - -static void D3D11_BindGraphicsPipeline( - SDL_GPUCommandBuffer *commandBuffer, - SDL_GPUGraphicsPipeline *graphicsPipeline) -{ - D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer; - D3D11GraphicsPipeline *pipeline = (D3D11GraphicsPipeline *)graphicsPipeline; - d3d11CommandBuffer->graphicsPipeline = pipeline; - - ID3D11DeviceContext_OMSetDepthStencilState( - d3d11CommandBuffer->context, - pipeline->depthStencilState, - d3d11CommandBuffer->stencilRef); - - ID3D11DeviceContext_IASetPrimitiveTopology( - d3d11CommandBuffer->context, - SDLToD3D11_PrimitiveType[pipeline->primitiveType]); - - ID3D11DeviceContext_IASetInputLayout( - d3d11CommandBuffer->context, - pipeline->inputLayout); - - ID3D11DeviceContext_RSSetState( - d3d11CommandBuffer->context, - pipeline->rasterizerState); - - ID3D11DeviceContext_VSSetShader( - d3d11CommandBuffer->context, - pipeline->vertexShader, - NULL, - 0); - - ID3D11DeviceContext_PSSetShader( - d3d11CommandBuffer->context, - pipeline->fragmentShader, - NULL, - 0); - - // Acquire uniform buffers if necessary - for (Uint32 i = 0; i < pipeline->vertexUniformBufferCount; i += 1) { - if (d3d11CommandBuffer->vertexUniformBuffers[i] == NULL) { - d3d11CommandBuffer->vertexUniformBuffers[i] = D3D11_INTERNAL_AcquireUniformBufferFromPool( - d3d11CommandBuffer); - } - } - - for (Uint32 i = 0; i < pipeline->fragmentUniformBufferCount; i += 1) { - if (d3d11CommandBuffer->fragmentUniformBuffers[i] == NULL) { - d3d11CommandBuffer->fragmentUniformBuffers[i] = D3D11_INTERNAL_AcquireUniformBufferFromPool( - d3d11CommandBuffer); - } - } - - // Mark that bindings are needed - d3d11CommandBuffer->needVertexSamplerBind = true; - d3d11CommandBuffer->needVertexStorageTextureBind = true; - d3d11CommandBuffer->needVertexStorageBufferBind = true; - d3d11CommandBuffer->needVertexUniformBufferBind = true; - d3d11CommandBuffer->needFragmentSamplerBind = true; - d3d11CommandBuffer->needFragmentStorageTextureBind = true; - d3d11CommandBuffer->needFragmentStorageBufferBind = true; - d3d11CommandBuffer->needFragmentUniformBufferBind = true; - d3d11CommandBuffer->needBlendStateSet = true; -} - -static void D3D11_BindVertexBuffers( - SDL_GPUCommandBuffer *commandBuffer, - Uint32 firstSlot, - const SDL_GPUBufferBinding *bindings, - Uint32 numBindings) -{ - D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer; - - for (Uint32 i = 0; i < numBindings; i += 1) { - D3D11Buffer *currentBuffer = ((D3D11BufferContainer *)bindings[i].buffer)->activeBuffer; - d3d11CommandBuffer->vertexBuffers[firstSlot + i] = currentBuffer->handle; - d3d11CommandBuffer->vertexBufferOffsets[firstSlot + i] = bindings[i].offset; - D3D11_INTERNAL_TrackBuffer(d3d11CommandBuffer, currentBuffer); - } - - d3d11CommandBuffer->vertexBufferCount = - SDL_max(d3d11CommandBuffer->vertexBufferCount, firstSlot + numBindings); - - d3d11CommandBuffer->needVertexBufferBind = true; -} - -static void D3D11_BindIndexBuffer( - SDL_GPUCommandBuffer *commandBuffer, - const SDL_GPUBufferBinding *binding, - SDL_GPUIndexElementSize indexElementSize) -{ - D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer; - D3D11Buffer *d3d11Buffer = ((D3D11BufferContainer *)binding->buffer)->activeBuffer; - - D3D11_INTERNAL_TrackBuffer(d3d11CommandBuffer, d3d11Buffer); - - ID3D11DeviceContext_IASetIndexBuffer( - d3d11CommandBuffer->context, - d3d11Buffer->handle, - SDLToD3D11_IndexType[indexElementSize], - (UINT)binding->offset); -} - -static void D3D11_BindVertexSamplers( - SDL_GPUCommandBuffer *commandBuffer, - Uint32 firstSlot, - const SDL_GPUTextureSamplerBinding *textureSamplerBindings, - Uint32 numBindings) -{ - D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer; - - for (Uint32 i = 0; i < numBindings; i += 1) { - D3D11TextureContainer *textureContainer = (D3D11TextureContainer *)textureSamplerBindings[i].texture; - - D3D11_INTERNAL_TrackTexture( - d3d11CommandBuffer, - textureContainer->activeTexture); - - d3d11CommandBuffer->vertexSamplers[firstSlot + i] = - (D3D11Sampler *)textureSamplerBindings[i].sampler; - - d3d11CommandBuffer->vertexSamplerTextures[firstSlot + i] = - textureContainer->activeTexture; - } - - d3d11CommandBuffer->needVertexSamplerBind = true; -} - -static void D3D11_BindVertexStorageTextures( - SDL_GPUCommandBuffer *commandBuffer, - Uint32 firstSlot, - SDL_GPUTexture *const *storageTextures, - Uint32 numBindings) -{ - D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer; - - for (Uint32 i = 0; i < numBindings; i += 1) { - D3D11TextureContainer *textureContainer = (D3D11TextureContainer *)storageTextures[i]; - - D3D11_INTERNAL_TrackTexture( - d3d11CommandBuffer, - textureContainer->activeTexture); - - d3d11CommandBuffer->vertexStorageTextures[firstSlot + i] = - textureContainer->activeTexture; - } - - d3d11CommandBuffer->needVertexStorageTextureBind = true; -} - -static void D3D11_BindVertexStorageBuffers( - SDL_GPUCommandBuffer *commandBuffer, - Uint32 firstSlot, - SDL_GPUBuffer *const *storageBuffers, - Uint32 numBindings) -{ - D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer; - D3D11BufferContainer *bufferContainer; - Uint32 i; - - for (i = 0; i < numBindings; i += 1) { - bufferContainer = (D3D11BufferContainer *)storageBuffers[i]; - - D3D11_INTERNAL_TrackBuffer( - d3d11CommandBuffer, - bufferContainer->activeBuffer); - - d3d11CommandBuffer->vertexStorageBuffers[firstSlot + i] = - bufferContainer->activeBuffer; - } - - d3d11CommandBuffer->needVertexStorageBufferBind = true; -} - -static void D3D11_BindFragmentSamplers( - SDL_GPUCommandBuffer *commandBuffer, - Uint32 firstSlot, - const SDL_GPUTextureSamplerBinding *textureSamplerBindings, - Uint32 numBindings) -{ - D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer; - - for (Uint32 i = 0; i < numBindings; i += 1) { - D3D11TextureContainer *textureContainer = (D3D11TextureContainer *)textureSamplerBindings[i].texture; - - D3D11_INTERNAL_TrackTexture( - d3d11CommandBuffer, - textureContainer->activeTexture); - - d3d11CommandBuffer->fragmentSamplers[firstSlot + i] = - (D3D11Sampler *)textureSamplerBindings[i].sampler; - - d3d11CommandBuffer->fragmentSamplerTextures[firstSlot + i] = - (D3D11Texture *)textureContainer->activeTexture; - } - - d3d11CommandBuffer->needFragmentSamplerBind = true; -} - -static void D3D11_BindFragmentStorageTextures( - SDL_GPUCommandBuffer *commandBuffer, - Uint32 firstSlot, - SDL_GPUTexture *const *storageTextures, - Uint32 numBindings) -{ - D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer; - - for (Uint32 i = 0; i < numBindings; i += 1) { - D3D11TextureContainer *textureContainer = (D3D11TextureContainer *)storageTextures[i]; - - D3D11_INTERNAL_TrackTexture( - d3d11CommandBuffer, - textureContainer->activeTexture); - - d3d11CommandBuffer->fragmentStorageTextures[firstSlot + i] = - textureContainer->activeTexture; - } - - d3d11CommandBuffer->needFragmentStorageTextureBind = true; -} - -static void D3D11_BindFragmentStorageBuffers( - SDL_GPUCommandBuffer *commandBuffer, - Uint32 firstSlot, - SDL_GPUBuffer *const *storageBuffers, - Uint32 numBindings) -{ - D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer; - D3D11BufferContainer *bufferContainer; - Uint32 i; - - for (i = 0; i < numBindings; i += 1) { - bufferContainer = (D3D11BufferContainer *)storageBuffers[i]; - - D3D11_INTERNAL_TrackBuffer( - d3d11CommandBuffer, - bufferContainer->activeBuffer); - - d3d11CommandBuffer->fragmentStorageBuffers[firstSlot + i] = - bufferContainer->activeBuffer; - } - - d3d11CommandBuffer->needFragmentStorageBufferBind = true; -} - -static void D3D11_INTERNAL_BindGraphicsResources( - D3D11CommandBuffer *commandBuffer) -{ - D3D11GraphicsPipeline *graphicsPipeline = commandBuffer->graphicsPipeline; - - ID3D11Buffer *nullBuf = NULL; - Uint32 offsetInConstants, blockSizeInConstants; - - if (commandBuffer->needVertexBufferBind) { - ID3D11DeviceContext_IASetVertexBuffers( - commandBuffer->context, - 0, - commandBuffer->vertexBufferCount, - commandBuffer->vertexBuffers, - graphicsPipeline->vertexStrides, - commandBuffer->vertexBufferOffsets); - } - - if (commandBuffer->needVertexSamplerBind) { - if (graphicsPipeline->vertexSamplerCount > 0) { - ID3D11SamplerState *samplerStates[MAX_TEXTURE_SAMPLERS_PER_STAGE]; - ID3D11ShaderResourceView *srvs[MAX_TEXTURE_SAMPLERS_PER_STAGE]; - - for (Uint32 i = 0; i < graphicsPipeline->vertexSamplerCount; i += 1) { - samplerStates[i] = commandBuffer->vertexSamplers[i]->handle; - srvs[i] = commandBuffer->vertexSamplerTextures[i]->shaderView; - } - - ID3D11DeviceContext_VSSetSamplers( - commandBuffer->context, - 0, - graphicsPipeline->vertexSamplerCount, - samplerStates); - - ID3D11DeviceContext_VSSetShaderResources( - commandBuffer->context, - 0, - graphicsPipeline->vertexSamplerCount, - srvs); - } - - commandBuffer->needVertexSamplerBind = false; - } - - if (commandBuffer->needVertexStorageTextureBind) { - if (graphicsPipeline->vertexStorageTextureCount > 0) { - ID3D11ShaderResourceView *srvs[MAX_STORAGE_TEXTURES_PER_STAGE]; - - for (Uint32 i = 0; i < graphicsPipeline->vertexStorageTextureCount; i += 1) { - srvs[i] = commandBuffer->vertexStorageTextures[i]->shaderView; - } - - ID3D11DeviceContext_VSSetShaderResources( - commandBuffer->context, - graphicsPipeline->vertexSamplerCount, - graphicsPipeline->vertexStorageTextureCount, - srvs); - } - - commandBuffer->needVertexStorageTextureBind = false; - } - - if (commandBuffer->needVertexStorageBufferBind) { - if (graphicsPipeline->vertexStorageBufferCount > 0) { - ID3D11ShaderResourceView *srvs[MAX_STORAGE_BUFFERS_PER_STAGE]; - - for (Uint32 i = 0; i < graphicsPipeline->vertexStorageBufferCount; i += 1) { - srvs[i] = commandBuffer->vertexStorageBuffers[i]->srv; - } - - ID3D11DeviceContext_VSSetShaderResources( - commandBuffer->context, - graphicsPipeline->vertexSamplerCount + graphicsPipeline->vertexStorageTextureCount, - graphicsPipeline->vertexStorageBufferCount, - srvs); - } - - commandBuffer->needVertexStorageBufferBind = false; - } - - if (commandBuffer->needVertexUniformBufferBind) { - for (Uint32 i = 0; i < graphicsPipeline->vertexUniformBufferCount; i += 1) { - /* stupid workaround for god awful D3D11 drivers - * see: https://learn.microsoft.com/en-us/windows/win32/api/d3d11_1/nf-d3d11_1-id3d11devicecontext1-vssetconstantbuffers1#calling-vssetconstantbuffers1-with-command-list-emulation - */ - ID3D11DeviceContext1_VSSetConstantBuffers( - commandBuffer->context, - i, - 1, - &nullBuf); - - offsetInConstants = commandBuffer->vertexUniformBuffers[i]->drawOffset / 16; - blockSizeInConstants = commandBuffer->vertexUniformBuffers[i]->currentBlockSize / 16; - - ID3D11DeviceContext1_VSSetConstantBuffers1( - commandBuffer->context, - i, - 1, - &commandBuffer->vertexUniformBuffers[i]->buffer, - &offsetInConstants, - &blockSizeInConstants); - } - - commandBuffer->needVertexUniformBufferBind = false; - } - - if (commandBuffer->needFragmentSamplerBind) { - if (graphicsPipeline->fragmentSamplerCount > 0) { - ID3D11SamplerState *samplerStates[MAX_TEXTURE_SAMPLERS_PER_STAGE]; - ID3D11ShaderResourceView *srvs[MAX_TEXTURE_SAMPLERS_PER_STAGE]; - - for (Uint32 i = 0; i < graphicsPipeline->fragmentSamplerCount; i += 1) { - samplerStates[i] = commandBuffer->fragmentSamplers[i]->handle; - srvs[i] = commandBuffer->fragmentSamplerTextures[i]->shaderView; - } - - ID3D11DeviceContext_PSSetSamplers( - commandBuffer->context, - 0, - graphicsPipeline->fragmentSamplerCount, - samplerStates); - - ID3D11DeviceContext_PSSetShaderResources( - commandBuffer->context, - 0, - graphicsPipeline->fragmentSamplerCount, - srvs); - } - - commandBuffer->needFragmentSamplerBind = false; - } - - if (commandBuffer->needFragmentStorageTextureBind) { - if (graphicsPipeline->fragmentStorageTextureCount > 0) { - ID3D11ShaderResourceView *srvs[MAX_STORAGE_TEXTURES_PER_STAGE]; - - for (Uint32 i = 0; i < graphicsPipeline->fragmentStorageTextureCount; i += 1) { - srvs[i] = commandBuffer->fragmentStorageTextures[i]->shaderView; - } - - ID3D11DeviceContext_PSSetShaderResources( - commandBuffer->context, - graphicsPipeline->fragmentSamplerCount, - graphicsPipeline->fragmentStorageTextureCount, - srvs); - } - - commandBuffer->needFragmentStorageTextureBind = false; - } - - if (commandBuffer->needFragmentStorageBufferBind) { - if (graphicsPipeline->fragmentStorageBufferCount > 0) { - ID3D11ShaderResourceView *srvs[MAX_STORAGE_BUFFERS_PER_STAGE]; - - for (Uint32 i = 0; i < graphicsPipeline->fragmentStorageBufferCount; i += 1) { - srvs[i] = commandBuffer->fragmentStorageBuffers[i]->srv; - } - - ID3D11DeviceContext_PSSetShaderResources( - commandBuffer->context, - graphicsPipeline->fragmentSamplerCount + graphicsPipeline->fragmentStorageTextureCount, - graphicsPipeline->fragmentStorageBufferCount, - srvs); - } - - commandBuffer->needFragmentStorageBufferBind = false; - } - - if (commandBuffer->needFragmentUniformBufferBind) { - for (Uint32 i = 0; i < graphicsPipeline->fragmentUniformBufferCount; i += 1) { - /* stupid workaround for god awful D3D11 drivers - * see: https://learn.microsoft.com/en-us/windows/win32/api/d3d11_1/nf-d3d11_1-id3d11devicecontext1-pssetconstantbuffers1#calling-pssetconstantbuffers1-with-command-list-emulation - */ - ID3D11DeviceContext1_PSSetConstantBuffers( - commandBuffer->context, - i, - 1, - &nullBuf); - - offsetInConstants = commandBuffer->fragmentUniformBuffers[i]->drawOffset / 16; - blockSizeInConstants = commandBuffer->fragmentUniformBuffers[i]->currentBlockSize / 16; - - ID3D11DeviceContext1_PSSetConstantBuffers1( - commandBuffer->context, - i, - 1, - &commandBuffer->fragmentUniformBuffers[i]->buffer, - &offsetInConstants, - &blockSizeInConstants); - } - - commandBuffer->needFragmentUniformBufferBind = false; - } - - if (commandBuffer->needBlendStateSet) { - FLOAT blendFactor[4] = { - commandBuffer->blendConstants.r, - commandBuffer->blendConstants.g, - commandBuffer->blendConstants.b, - commandBuffer->blendConstants.a - }; - - ID3D11DeviceContext_OMSetBlendState( - commandBuffer->context, - graphicsPipeline->colorTargetBlendState, - blendFactor, - graphicsPipeline->sampleMask); - - commandBuffer->needBlendStateSet = false; - } -} - -static void D3D11_DrawIndexedPrimitives( - SDL_GPUCommandBuffer *commandBuffer, - Uint32 numIndices, - Uint32 numInstances, - Uint32 firstIndex, - Sint32 vertexOffset, - Uint32 firstInstance) -{ - D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer; - D3D11_INTERNAL_BindGraphicsResources(d3d11CommandBuffer); - - ID3D11DeviceContext_DrawIndexedInstanced( - d3d11CommandBuffer->context, - numIndices, - numInstances, - firstIndex, - vertexOffset, - firstInstance); -} - -static void D3D11_DrawPrimitives( - SDL_GPUCommandBuffer *commandBuffer, - Uint32 numVertices, - Uint32 numInstances, - Uint32 firstVertex, - Uint32 firstInstance) -{ - D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer; - D3D11_INTERNAL_BindGraphicsResources(d3d11CommandBuffer); - - ID3D11DeviceContext_DrawInstanced( - d3d11CommandBuffer->context, - numVertices, - numInstances, - firstVertex, - firstInstance); -} - -static void D3D11_DrawPrimitivesIndirect( - SDL_GPUCommandBuffer *commandBuffer, - SDL_GPUBuffer *buffer, - Uint32 offset, - Uint32 drawCount) -{ - D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer; - D3D11_INTERNAL_BindGraphicsResources(d3d11CommandBuffer); - - D3D11Buffer *d3d11Buffer = ((D3D11BufferContainer *)buffer)->activeBuffer; - - /* D3D11: "We have multi-draw at home!" - * Multi-draw at home: - */ - for (Uint32 i = 0; i < drawCount; i += 1) { - ID3D11DeviceContext_DrawInstancedIndirect( - d3d11CommandBuffer->context, - d3d11Buffer->handle, - offset + (sizeof(SDL_GPUIndirectDrawCommand) * i)); - } - - D3D11_INTERNAL_TrackBuffer(d3d11CommandBuffer, d3d11Buffer); -} - -static void D3D11_DrawIndexedPrimitivesIndirect( - SDL_GPUCommandBuffer *commandBuffer, - SDL_GPUBuffer *buffer, - Uint32 offset, - Uint32 drawCount) -{ - D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer; - D3D11_INTERNAL_BindGraphicsResources(d3d11CommandBuffer); - - D3D11Buffer *d3d11Buffer = ((D3D11BufferContainer *)buffer)->activeBuffer; - - /* D3D11: "We have multi-draw at home!" - * Multi-draw at home: - */ - for (Uint32 i = 0; i < drawCount; i += 1) { - ID3D11DeviceContext_DrawIndexedInstancedIndirect( - d3d11CommandBuffer->context, - d3d11Buffer->handle, - offset + (sizeof(SDL_GPUIndexedIndirectDrawCommand) * i)); - } - - D3D11_INTERNAL_TrackBuffer(d3d11CommandBuffer, d3d11Buffer); -} - -static void D3D11_EndRenderPass( - SDL_GPUCommandBuffer *commandBuffer) -{ - D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer; - D3D11Renderer *renderer = d3d11CommandBuffer->renderer; - Uint32 i; - - // Set render target slots to NULL to avoid NULL set behavior - // https://learn.microsoft.com/en-us/windows/win32/api/d3d11/nf-d3d11-id3d11devicecontext-pssetshaderresources - ID3D11DeviceContext_OMSetRenderTargets( - d3d11CommandBuffer->context, - MAX_COLOR_TARGET_BINDINGS, - renderer->nullRTVs, - NULL); - - // Resolve MSAA color render targets - for (i = 0; i < MAX_COLOR_TARGET_BINDINGS; i += 1) { - if (d3d11CommandBuffer->colorResolveSubresources[i] != NULL) { - ID3D11DeviceContext_ResolveSubresource( - d3d11CommandBuffer->context, - d3d11CommandBuffer->colorResolveSubresources[i]->parent->handle, - d3d11CommandBuffer->colorResolveSubresources[i]->index, - d3d11CommandBuffer->colorTargetSubresources[i]->parent->handle, - d3d11CommandBuffer->colorTargetSubresources[i]->index, - SDLToD3D11_TextureFormat[d3d11CommandBuffer->colorTargetSubresources[i]->parent->container->header.info.format]); - } - } - - ID3D11DeviceContext_VSSetSamplers( - d3d11CommandBuffer->context, - 0, - MAX_TEXTURE_SAMPLERS_PER_STAGE, - renderer->nullSamplers); - - ID3D11DeviceContext_VSSetShaderResources( - d3d11CommandBuffer->context, - 0, - MAX_TEXTURE_SAMPLERS_PER_STAGE * 2 + MAX_STORAGE_TEXTURES_PER_STAGE + MAX_STORAGE_BUFFERS_PER_STAGE, - renderer->nullSRVs); - - ID3D11DeviceContext_PSSetSamplers( - d3d11CommandBuffer->context, - 0, - MAX_TEXTURE_SAMPLERS_PER_STAGE, - renderer->nullSamplers); - - ID3D11DeviceContext_PSSetShaderResources( - d3d11CommandBuffer->context, - 0, - MAX_TEXTURE_SAMPLERS_PER_STAGE * 2 + MAX_STORAGE_TEXTURES_PER_STAGE + MAX_STORAGE_BUFFERS_PER_STAGE, - renderer->nullSRVs); - - // Reset bind state - SDL_zeroa(d3d11CommandBuffer->vertexBuffers); - SDL_zeroa(d3d11CommandBuffer->vertexBufferOffsets); - d3d11CommandBuffer->vertexBufferCount = 0; - - SDL_zeroa(d3d11CommandBuffer->vertexSamplers); - SDL_zeroa(d3d11CommandBuffer->vertexSamplerTextures); - SDL_zeroa(d3d11CommandBuffer->vertexStorageTextures); - SDL_zeroa(d3d11CommandBuffer->vertexStorageBuffers); - - SDL_zeroa(d3d11CommandBuffer->fragmentSamplers); - SDL_zeroa(d3d11CommandBuffer->fragmentSamplerTextures); - SDL_zeroa(d3d11CommandBuffer->fragmentStorageTextures); - SDL_zeroa(d3d11CommandBuffer->fragmentStorageBuffers); -} - -static void D3D11_PushVertexUniformData( - SDL_GPUCommandBuffer *commandBuffer, - Uint32 slotIndex, - const void *data, - Uint32 length) -{ - D3D11_INTERNAL_PushUniformData( - (D3D11CommandBuffer *)commandBuffer, - SDL_GPU_SHADERSTAGE_VERTEX, - slotIndex, - data, - length); -} - -static void D3D11_PushFragmentUniformData( - SDL_GPUCommandBuffer *commandBuffer, - Uint32 slotIndex, - const void *data, - Uint32 length) -{ - D3D11_INTERNAL_PushUniformData( - (D3D11CommandBuffer *)commandBuffer, - SDL_GPU_SHADERSTAGE_FRAGMENT, - slotIndex, - data, - length); -} - -// Blit - -static void D3D11_Blit( - SDL_GPUCommandBuffer *commandBuffer, - const SDL_GPUBlitInfo *info) -{ - D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer; - D3D11Renderer *renderer = (D3D11Renderer *)d3d11CommandBuffer->renderer; - BlitPipelineCacheEntry *blitPipelines = &renderer->blitPipelines[0]; - - SDL_GPU_BlitCommon( - commandBuffer, - info, - renderer->blitLinearSampler, - renderer->blitNearestSampler, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - &blitPipelines, - NULL, - NULL); -} - -// Compute State - -static void D3D11_BeginComputePass( - SDL_GPUCommandBuffer *commandBuffer, - const SDL_GPUStorageTextureReadWriteBinding *storageTextureBindings, - Uint32 numStorageTextureBindings, - const SDL_GPUStorageBufferReadWriteBinding *storageBufferBindings, - Uint32 numStorageBufferBindings) -{ - D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer; - D3D11TextureContainer *textureContainer; - D3D11TextureSubresource *textureSubresource; - D3D11BufferContainer *bufferContainer; - D3D11Buffer *buffer; - ID3D11UnorderedAccessView *uavs[MAX_COMPUTE_WRITE_TEXTURES + MAX_COMPUTE_WRITE_BUFFERS]; - - for (Uint32 i = 0; i < numStorageTextureBindings; i += 1) { - textureContainer = (D3D11TextureContainer *)storageTextureBindings[i].texture; - - textureSubresource = D3D11_INTERNAL_PrepareTextureSubresourceForWrite( - d3d11CommandBuffer->renderer, - textureContainer, - storageTextureBindings[i].layer, - storageTextureBindings[i].mip_level, - storageTextureBindings[i].cycle); - - D3D11_INTERNAL_TrackTexture( - d3d11CommandBuffer, - textureSubresource->parent); - - d3d11CommandBuffer->computeReadWriteStorageTextureSubresources[i] = textureSubresource; - } - - for (Uint32 i = 0; i < numStorageBufferBindings; i += 1) { - bufferContainer = (D3D11BufferContainer *)storageBufferBindings[i].buffer; - - buffer = D3D11_INTERNAL_PrepareBufferForWrite( - d3d11CommandBuffer->renderer, - bufferContainer, - storageBufferBindings[i].cycle); - - D3D11_INTERNAL_TrackBuffer( - d3d11CommandBuffer, - buffer); - - d3d11CommandBuffer->computeReadWriteStorageBuffers[i] = buffer; - } - - for (Uint32 i = 0; i < numStorageTextureBindings; i += 1) { - uavs[i] = d3d11CommandBuffer->computeReadWriteStorageTextureSubresources[i]->uav; - } - - for (Uint32 i = 0; i < numStorageBufferBindings; i += 1) { - uavs[numStorageTextureBindings + i] = d3d11CommandBuffer->computeReadWriteStorageBuffers[i]->uav; - } - - ID3D11DeviceContext_CSSetUnorderedAccessViews( - d3d11CommandBuffer->context, - 0, - numStorageTextureBindings + numStorageBufferBindings, - uavs, - NULL); -} - -static void D3D11_BindComputePipeline( - SDL_GPUCommandBuffer *commandBuffer, - SDL_GPUComputePipeline *computePipeline) -{ - D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer; - D3D11ComputePipeline *pipeline = (D3D11ComputePipeline *)computePipeline; - - d3d11CommandBuffer->computePipeline = pipeline; - - ID3D11DeviceContext_CSSetShader( - d3d11CommandBuffer->context, - pipeline->computeShader, - NULL, - 0); - - // Acquire uniform buffers if necessary - for (Uint32 i = 0; i < pipeline->numUniformBuffers; i += 1) { - if (d3d11CommandBuffer->computeUniformBuffers[i] == NULL) { - d3d11CommandBuffer->computeUniformBuffers[i] = D3D11_INTERNAL_AcquireUniformBufferFromPool( - d3d11CommandBuffer); - } - } - - d3d11CommandBuffer->needComputeSamplerBind = true; - d3d11CommandBuffer->needComputeReadOnlyTextureBind = true; - d3d11CommandBuffer->needComputeReadOnlyBufferBind = true; - d3d11CommandBuffer->needComputeUniformBufferBind = true; -} - -static void D3D11_BindComputeSamplers( - SDL_GPUCommandBuffer *commandBuffer, - Uint32 firstSlot, - const SDL_GPUTextureSamplerBinding *textureSamplerBindings, - Uint32 numBindings) -{ - D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer; - - for (Uint32 i = 0; i < numBindings; i += 1) { - D3D11TextureContainer *textureContainer = (D3D11TextureContainer *)textureSamplerBindings[i].texture; - - D3D11_INTERNAL_TrackTexture( - d3d11CommandBuffer, - textureContainer->activeTexture); - - d3d11CommandBuffer->computeSamplers[firstSlot + i] = - (D3D11Sampler *)textureSamplerBindings[i].sampler; - - d3d11CommandBuffer->computeSamplerTextures[firstSlot + i] = - textureContainer->activeTexture; - } - - d3d11CommandBuffer->needComputeSamplerBind = true; -} - -static void D3D11_BindComputeStorageTextures( - SDL_GPUCommandBuffer *commandBuffer, - Uint32 firstSlot, - SDL_GPUTexture *const *storageTextures, - Uint32 numBindings) -{ - D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer; - - for (Uint32 i = 0; i < numBindings; i += 1) { - D3D11TextureContainer *textureContainer = (D3D11TextureContainer *)storageTextures[i]; - - D3D11_INTERNAL_TrackTexture( - d3d11CommandBuffer, - textureContainer->activeTexture); - - d3d11CommandBuffer->computeReadOnlyStorageTextures[firstSlot + i] = - textureContainer->activeTexture; - } - - d3d11CommandBuffer->needComputeReadOnlyTextureBind = true; -} - -static void D3D11_BindComputeStorageBuffers( - SDL_GPUCommandBuffer *commandBuffer, - Uint32 firstSlot, - SDL_GPUBuffer *const *storageBuffers, - Uint32 numBindings) -{ - D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer; - D3D11BufferContainer *bufferContainer; - Uint32 i; - - for (i = 0; i < numBindings; i += 1) { - bufferContainer = (D3D11BufferContainer *)storageBuffers[i]; - - D3D11_INTERNAL_TrackBuffer( - d3d11CommandBuffer, - bufferContainer->activeBuffer); - - d3d11CommandBuffer->computeReadOnlyStorageBuffers[firstSlot + i] = - bufferContainer->activeBuffer; - } - - d3d11CommandBuffer->needComputeReadOnlyBufferBind = true; -} - -static void D3D11_PushComputeUniformData( - SDL_GPUCommandBuffer *commandBuffer, - Uint32 slotIndex, - const void *data, - Uint32 length) -{ - D3D11_INTERNAL_PushUniformData( - (D3D11CommandBuffer *)commandBuffer, - SDL_GPU_SHADERSTAGE_COMPUTE, - slotIndex, - data, - length); -} - -static void D3D11_INTERNAL_BindComputeResources( - D3D11CommandBuffer *commandBuffer) -{ - D3D11ComputePipeline *computePipeline = commandBuffer->computePipeline; - - ID3D11Buffer *nullBuf = NULL; - Uint32 offsetInConstants, blockSizeInConstants; - - if (commandBuffer->needComputeSamplerBind) { - if (computePipeline->numSamplers > 0) { - ID3D11SamplerState *samplers[MAX_TEXTURE_SAMPLERS_PER_STAGE]; - ID3D11ShaderResourceView *srvs[MAX_TEXTURE_SAMPLERS_PER_STAGE]; - - for (Uint32 i = 0; i < computePipeline->numSamplers; i += 1) { - samplers[i] = commandBuffer->computeSamplers[i]->handle; - srvs[i] = commandBuffer->computeSamplerTextures[i]->shaderView; - } - - ID3D11DeviceContext_CSSetSamplers( - commandBuffer->context, - 0, - computePipeline->numSamplers, - samplers); - - ID3D11DeviceContext_CSSetShaderResources( - commandBuffer->context, - 0, - computePipeline->numSamplers, - srvs); - } - - commandBuffer->needComputeSamplerBind = false; - } - - if (commandBuffer->needComputeReadOnlyTextureBind) { - if (computePipeline->numReadonlyStorageTextures > 0) { - ID3D11ShaderResourceView *srvs[MAX_STORAGE_TEXTURES_PER_STAGE]; - - for (Uint32 i = 0; i < computePipeline->numReadonlyStorageTextures; i += 1) { - srvs[i] = commandBuffer->computeReadOnlyStorageTextures[i]->shaderView; - } - - ID3D11DeviceContext_CSSetShaderResources( - commandBuffer->context, - computePipeline->numSamplers, - computePipeline->numReadonlyStorageTextures, - srvs); - } - - commandBuffer->needComputeReadOnlyTextureBind = false; - } - - if (commandBuffer->needComputeReadOnlyBufferBind) { - if (computePipeline->numReadonlyStorageBuffers > 0) { - ID3D11ShaderResourceView *srvs[MAX_STORAGE_TEXTURES_PER_STAGE]; - - for (Uint32 i = 0; i < computePipeline->numReadonlyStorageBuffers; i += 1) { - srvs[i] = commandBuffer->computeReadOnlyStorageBuffers[i]->srv; - } - - ID3D11DeviceContext_CSSetShaderResources( - commandBuffer->context, - computePipeline->numSamplers + computePipeline->numReadonlyStorageTextures, - computePipeline->numReadonlyStorageBuffers, - srvs); - } - - commandBuffer->needComputeReadOnlyBufferBind = false; - } - - if (commandBuffer->needComputeUniformBufferBind) { - for (Uint32 i = 0; i < computePipeline->numUniformBuffers; i += 1) { - /* stupid workaround for god awful D3D11 drivers - * see: https://learn.microsoft.com/en-us/windows/win32/api/d3d11_1/nf-d3d11_1-id3d11devicecontext1-vssetconstantbuffers1#calling-vssetconstantbuffers1-with-command-list-emulation - */ - ID3D11DeviceContext1_CSSetConstantBuffers( - commandBuffer->context, - i, - 1, - &nullBuf); - - offsetInConstants = commandBuffer->computeUniformBuffers[i]->drawOffset / 16; - blockSizeInConstants = commandBuffer->computeUniformBuffers[i]->currentBlockSize / 16; - - ID3D11DeviceContext1_CSSetConstantBuffers1( - commandBuffer->context, - i, - 1, - &commandBuffer->computeUniformBuffers[i]->buffer, - &offsetInConstants, - &blockSizeInConstants); - } - commandBuffer->needComputeUniformBufferBind = false; - } -} - -static void D3D11_DispatchCompute( - SDL_GPUCommandBuffer *commandBuffer, - Uint32 groupcountX, - Uint32 groupcountY, - Uint32 groupcountZ) -{ - D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer; - D3D11_INTERNAL_BindComputeResources(d3d11CommandBuffer); - - ID3D11DeviceContext_Dispatch( - d3d11CommandBuffer->context, - groupcountX, - groupcountY, - groupcountZ); -} - -static void D3D11_DispatchComputeIndirect( - SDL_GPUCommandBuffer *commandBuffer, - SDL_GPUBuffer *buffer, - Uint32 offset) -{ - D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer; - D3D11Buffer *d3d11Buffer = ((D3D11BufferContainer *)buffer)->activeBuffer; - - D3D11_INTERNAL_BindComputeResources(d3d11CommandBuffer); - - ID3D11DeviceContext_DispatchIndirect( - d3d11CommandBuffer->context, - d3d11Buffer->handle, - offset); - - D3D11_INTERNAL_TrackBuffer(d3d11CommandBuffer, d3d11Buffer); -} - -static void D3D11_EndComputePass( - SDL_GPUCommandBuffer *commandBuffer) -{ - D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer; - D3D11Renderer *renderer = d3d11CommandBuffer->renderer; - - // reset UAV slots to avoid NULL set behavior - // https://learn.microsoft.com/en-us/windows/win32/api/d3d11/nf-d3d11-id3d11devicecontext-cssetshaderresources - ID3D11DeviceContext_CSSetUnorderedAccessViews( - d3d11CommandBuffer->context, - 0, - MAX_COMPUTE_WRITE_TEXTURES + MAX_COMPUTE_WRITE_BUFFERS, - renderer->nullUAVs, - NULL); - - ID3D11DeviceContext_CSSetSamplers( - d3d11CommandBuffer->context, - 0, - MAX_TEXTURE_SAMPLERS_PER_STAGE, - renderer->nullSamplers); - - ID3D11DeviceContext_CSSetShaderResources( - d3d11CommandBuffer->context, - 0, - MAX_TEXTURE_SAMPLERS_PER_STAGE + MAX_STORAGE_TEXTURES_PER_STAGE + MAX_STORAGE_BUFFERS_PER_STAGE, - renderer->nullSRVs); - - d3d11CommandBuffer->computePipeline = NULL; - - // Reset bind state - SDL_zeroa(d3d11CommandBuffer->computeSamplers); - SDL_zeroa(d3d11CommandBuffer->computeSamplerTextures); - SDL_zeroa(d3d11CommandBuffer->computeReadOnlyStorageTextures); - SDL_zeroa(d3d11CommandBuffer->computeReadOnlyStorageBuffers); - SDL_zeroa(d3d11CommandBuffer->computeReadWriteStorageTextureSubresources); - SDL_zeroa(d3d11CommandBuffer->computeReadWriteStorageBuffers); -} - -// Fence Cleanup - -static void D3D11_INTERNAL_ReleaseFenceToPool( - D3D11Renderer *renderer, - D3D11Fence *fence) -{ - SDL_LockMutex(renderer->fenceLock); - - if (renderer->availableFenceCount == renderer->availableFenceCapacity) { - renderer->availableFenceCapacity *= 2; - renderer->availableFences = SDL_realloc( - renderer->availableFences, - renderer->availableFenceCapacity * sizeof(D3D11Fence *)); - } - renderer->availableFences[renderer->availableFenceCount] = fence; - renderer->availableFenceCount += 1; - - SDL_UnlockMutex(renderer->fenceLock); -} - -static void D3D11_ReleaseFence( - SDL_GPURenderer *driverData, - SDL_GPUFence *fence) -{ - D3D11Fence *d3d11Fence = (D3D11Fence *)fence; - - if (SDL_AtomicDecRef(&d3d11Fence->referenceCount)) { - D3D11_INTERNAL_ReleaseFenceToPool( - (D3D11Renderer *)driverData, - d3d11Fence); - } -} - -// Cleanup - -/* D3D11 does not provide a deferred texture-to-buffer copy operation, - * so instead of the transfer buffer containing an actual D3D11 buffer, - * the transfer buffer data is just a malloc'd pointer. - * In the download operation we copy data to a staging resource, and - * wait until the command buffer has finished executing to map the staging resource. - */ - -static bool D3D11_INTERNAL_MapAndCopyBufferDownload( - D3D11Renderer *renderer, - D3D11TransferBuffer *transferBuffer, - D3D11BufferDownload *bufferDownload) -{ - D3D11_MAPPED_SUBRESOURCE subres; - HRESULT res; - - SDL_LockMutex(renderer->contextLock); - res = ID3D11DeviceContext_Map( - renderer->immediateContext, - (ID3D11Resource *)bufferDownload->stagingBuffer, - 0, - D3D11_MAP_READ, - 0, - &subres); - SDL_UnlockMutex(renderer->contextLock); - - CHECK_D3D11_ERROR_AND_RETURN("Failed to map staging buffer", false) - - SDL_memcpy( - ((Uint8 *)transferBuffer->data) + bufferDownload->dstOffset, - ((Uint8 *)subres.pData), - bufferDownload->size); - - SDL_LockMutex(renderer->contextLock); - ID3D11DeviceContext_Unmap( - renderer->immediateContext, - (ID3D11Resource *)bufferDownload->stagingBuffer, - 0); - SDL_UnlockMutex(renderer->contextLock); - - ID3D11Buffer_Release(bufferDownload->stagingBuffer); - - return true; -} - -static bool D3D11_INTERNAL_MapAndCopyTextureDownload( - D3D11Renderer *renderer, - D3D11TransferBuffer *transferBuffer, - D3D11TextureDownload *textureDownload) -{ - D3D11_MAPPED_SUBRESOURCE subres; - HRESULT res; - Uint32 dataPtrOffset; - Uint32 depth, row; - - SDL_LockMutex(renderer->contextLock); - res = ID3D11DeviceContext_Map( - renderer->immediateContext, - (ID3D11Resource *)textureDownload->stagingTexture, - 0, - D3D11_MAP_READ, - 0, - &subres); - SDL_UnlockMutex(renderer->contextLock); - - CHECK_D3D11_ERROR_AND_RETURN("Could not map staging texture", false) - - for (depth = 0; depth < textureDownload->depth; depth += 1) { - dataPtrOffset = textureDownload->bufferOffset + (depth * textureDownload->bytesPerDepthSlice); - - for (row = 0; row < textureDownload->height; row += 1) { - SDL_memcpy( - transferBuffer->data + dataPtrOffset, - (Uint8 *)subres.pData + (depth * subres.DepthPitch) + (row * subres.RowPitch), - textureDownload->bytesPerRow); - dataPtrOffset += textureDownload->bytesPerRow; - } - } - - SDL_LockMutex(renderer->contextLock); - ID3D11DeviceContext_Unmap( - renderer->immediateContext, - textureDownload->stagingTexture, - 0); - SDL_UnlockMutex(renderer->contextLock); - - ID3D11Resource_Release(textureDownload->stagingTexture); - - return true; -} - -static bool D3D11_INTERNAL_CleanCommandBuffer( - D3D11Renderer *renderer, - D3D11CommandBuffer *commandBuffer, - bool cancel) -{ - Uint32 i, j; - bool result = true; - - // Perform deferred download map and copy - - for (i = 0; i < commandBuffer->usedTransferBufferCount; i += 1) { - D3D11TransferBuffer *transferBuffer = commandBuffer->usedTransferBuffers[i]; - - for (j = 0; j < transferBuffer->bufferDownloadCount; j += 1) { - if (!cancel) { - result &= D3D11_INTERNAL_MapAndCopyBufferDownload( - renderer, - transferBuffer, - &transferBuffer->bufferDownloads[j]); - } - } - - for (j = 0; j < transferBuffer->textureDownloadCount; j += 1) { - if (!cancel) { - result &= D3D11_INTERNAL_MapAndCopyTextureDownload( - renderer, - transferBuffer, - &transferBuffer->textureDownloads[j]); - } - } - - transferBuffer->bufferDownloadCount = 0; - transferBuffer->textureDownloadCount = 0; - } - - // Uniform buffers are now available - - SDL_LockMutex(renderer->acquireUniformBufferLock); - - for (i = 0; i < commandBuffer->usedUniformBufferCount; i += 1) { - D3D11_INTERNAL_ReturnUniformBufferToPool( - renderer, - commandBuffer->usedUniformBuffers[i]); - } - commandBuffer->usedUniformBufferCount = 0; - - SDL_UnlockMutex(renderer->acquireUniformBufferLock); - - // Reference Counting - - for (i = 0; i < commandBuffer->usedBufferCount; i += 1) { - (void)SDL_AtomicDecRef(&commandBuffer->usedBuffers[i]->referenceCount); - } - commandBuffer->usedBufferCount = 0; - - for (i = 0; i < commandBuffer->usedTransferBufferCount; i += 1) { - (void)SDL_AtomicDecRef(&commandBuffer->usedTransferBuffers[i]->referenceCount); - } - commandBuffer->usedTransferBufferCount = 0; - - for (i = 0; i < commandBuffer->usedTextureCount; i += 1) { - (void)SDL_AtomicDecRef(&commandBuffer->usedTextures[i]->referenceCount); - } - commandBuffer->usedTextureCount = 0; - - // Reset presentation - commandBuffer->windowDataCount = 0; - - // The fence is now available (unless SubmitAndAcquireFence was called) - if (commandBuffer->autoReleaseFence) { - D3D11_ReleaseFence( - (SDL_GPURenderer *)renderer, - (SDL_GPUFence *)commandBuffer->fence); - } - - // Return command buffer to pool - SDL_LockMutex(renderer->acquireCommandBufferLock); - if (renderer->availableCommandBufferCount == renderer->availableCommandBufferCapacity) { - renderer->availableCommandBufferCapacity += 1; - renderer->availableCommandBuffers = SDL_realloc( - renderer->availableCommandBuffers, - renderer->availableCommandBufferCapacity * sizeof(D3D11CommandBuffer *)); - } - renderer->availableCommandBuffers[renderer->availableCommandBufferCount] = commandBuffer; - renderer->availableCommandBufferCount += 1; - SDL_UnlockMutex(renderer->acquireCommandBufferLock); - - // Remove this command buffer from the submitted list - if (!cancel) { - for (i = 0; i < renderer->submittedCommandBufferCount; i += 1) { - if (renderer->submittedCommandBuffers[i] == commandBuffer) { - renderer->submittedCommandBuffers[i] = renderer->submittedCommandBuffers[renderer->submittedCommandBufferCount - 1]; - renderer->submittedCommandBufferCount -= 1; - } - } - } - - return result; -} - -static void D3D11_INTERNAL_PerformPendingDestroys( - D3D11Renderer *renderer) -{ - Sint32 referenceCount = 0; - Sint32 i; - Uint32 j; - - for (i = renderer->transferBufferContainersToDestroyCount - 1; i >= 0; i -= 1) { - referenceCount = 0; - for (j = 0; j < renderer->transferBufferContainersToDestroy[i]->bufferCount; j += 1) { - referenceCount += SDL_GetAtomicInt(&renderer->transferBufferContainersToDestroy[i]->buffers[j]->referenceCount); - } - - if (referenceCount == 0) { - D3D11_INTERNAL_DestroyTransferBufferContainer( - renderer->transferBufferContainersToDestroy[i]); - - renderer->transferBufferContainersToDestroy[i] = renderer->transferBufferContainersToDestroy[renderer->transferBufferContainersToDestroyCount - 1]; - renderer->transferBufferContainersToDestroyCount -= 1; - } - } - - for (i = renderer->bufferContainersToDestroyCount - 1; i >= 0; i -= 1) { - referenceCount = 0; - for (j = 0; j < renderer->bufferContainersToDestroy[i]->bufferCount; j += 1) { - referenceCount += SDL_GetAtomicInt(&renderer->bufferContainersToDestroy[i]->buffers[j]->referenceCount); - } - - if (referenceCount == 0) { - D3D11_INTERNAL_DestroyBufferContainer( - renderer->bufferContainersToDestroy[i]); - - renderer->bufferContainersToDestroy[i] = renderer->bufferContainersToDestroy[renderer->bufferContainersToDestroyCount - 1]; - renderer->bufferContainersToDestroyCount -= 1; - } - } - - for (i = renderer->textureContainersToDestroyCount - 1; i >= 0; i -= 1) { - referenceCount = 0; - for (j = 0; j < renderer->textureContainersToDestroy[i]->textureCount; j += 1) { - referenceCount += SDL_GetAtomicInt(&renderer->textureContainersToDestroy[i]->textures[j]->referenceCount); - } - - if (referenceCount == 0) { - D3D11_INTERNAL_DestroyTextureContainer( - renderer->textureContainersToDestroy[i]); - - renderer->textureContainersToDestroy[i] = renderer->textureContainersToDestroy[renderer->textureContainersToDestroyCount - 1]; - renderer->textureContainersToDestroyCount -= 1; - } - } -} - -// Fences - -static void D3D11_INTERNAL_WaitForFence( - D3D11Renderer *renderer, - D3D11Fence *fence) -{ - BOOL queryData; - HRESULT res; - - SDL_LockMutex(renderer->contextLock); - - do { - res = ID3D11DeviceContext_GetData( - renderer->immediateContext, - (ID3D11Asynchronous *)fence->handle, - &queryData, - sizeof(queryData), - 0); - } while (res != S_OK); // Spin until we get a result back... - - SDL_UnlockMutex(renderer->contextLock); -} - -static bool D3D11_WaitForFences( - SDL_GPURenderer *driverData, - bool waitAll, - SDL_GPUFence *const *fences, - Uint32 numFences) -{ - D3D11Renderer *renderer = (D3D11Renderer *)driverData; - D3D11Fence *fence; - BOOL queryData; - HRESULT res = S_FALSE; - - if (waitAll) { - for (Uint32 i = 0; i < numFences; i += 1) { - fence = (D3D11Fence *)fences[i]; - D3D11_INTERNAL_WaitForFence(renderer, fence); - } - } else { - SDL_LockMutex(renderer->contextLock); - - while (res != S_OK) { - for (Uint32 i = 0; i < numFences; i += 1) { - fence = (D3D11Fence *)fences[i]; - res = ID3D11DeviceContext_GetData( - renderer->immediateContext, - (ID3D11Asynchronous *)fence->handle, - &queryData, - sizeof(queryData), - 0); - if (res == S_OK) { - break; - } - } - } - - SDL_UnlockMutex(renderer->contextLock); - } - - SDL_LockMutex(renderer->contextLock); - - bool result = true; - // Check if we can perform any cleanups - for (Sint32 i = renderer->submittedCommandBufferCount - 1; i >= 0; i -= 1) { - res = ID3D11DeviceContext_GetData( - renderer->immediateContext, - (ID3D11Asynchronous *)renderer->submittedCommandBuffers[i]->fence->handle, - &queryData, - sizeof(queryData), - 0); - if (res == S_OK) { - result &= D3D11_INTERNAL_CleanCommandBuffer( - renderer, - renderer->submittedCommandBuffers[i], - false); - } - } - - D3D11_INTERNAL_PerformPendingDestroys(renderer); - - SDL_UnlockMutex(renderer->contextLock); - - return result; -} - -static bool D3D11_QueryFence( - SDL_GPURenderer *driverData, - SDL_GPUFence *fence) -{ - D3D11Renderer *renderer = (D3D11Renderer *)driverData; - D3D11Fence *d3d11Fence = (D3D11Fence *)fence; - BOOL queryData; - HRESULT res; - - SDL_LockMutex(renderer->contextLock); - - res = ID3D11DeviceContext_GetData( - renderer->immediateContext, - (ID3D11Asynchronous *)d3d11Fence->handle, - &queryData, - sizeof(queryData), - 0); - - SDL_UnlockMutex(renderer->contextLock); - - return res == S_OK; -} - -// Window and Swapchain Management - -static D3D11WindowData *D3D11_INTERNAL_FetchWindowData( - SDL_Window *window) -{ - SDL_PropertiesID properties = SDL_GetWindowProperties(window); - return (D3D11WindowData *)SDL_GetPointerProperty(properties, WINDOW_PROPERTY_DATA, NULL); -} - -static bool D3D11_INTERNAL_OnWindowResize(void *userdata, SDL_Event *e) -{ - SDL_Window *w = (SDL_Window *)userdata; - D3D11WindowData *data; - if (e->type == SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED && e->window.windowID == SDL_GetWindowID(w)) { - data = D3D11_INTERNAL_FetchWindowData(w); - data->needsSwapchainRecreate = true; - } - - return true; -} - -static bool D3D11_INTERNAL_InitializeSwapchainTexture( - D3D11Renderer *renderer, - IDXGISwapChain *swapchain, - DXGI_FORMAT swapchainFormat, - DXGI_FORMAT rtvFormat, - D3D11Texture *pTexture) -{ - ID3D11Texture2D *swapchainTexture; - D3D11_RENDER_TARGET_VIEW_DESC rtvDesc; - ID3D11RenderTargetView *rtv; - HRESULT res; - - // Clear all the texture data - SDL_zerop(pTexture); - - // Grab the buffer from the swapchain - res = IDXGISwapChain_GetBuffer( - swapchain, - 0, - &D3D_IID_ID3D11Texture2D, - (void **)&swapchainTexture); - CHECK_D3D11_ERROR_AND_RETURN("Could not get buffer from swapchain!", false); - - // Create the RTV for the swapchain - rtvDesc.Format = rtvFormat; - rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; - rtvDesc.Texture2D.MipSlice = 0; - - res = ID3D11Device_CreateRenderTargetView( - renderer->device, - (ID3D11Resource *)swapchainTexture, - &rtvDesc, - &rtv); - if (FAILED(res)) { - ID3D11Texture2D_Release(swapchainTexture); - D3D11_INTERNAL_SetError(renderer, "Swapchain RTV creation failed", res); - return false; - } - - // Fill out the texture struct - pTexture->handle = NULL; // This will be set in AcquireSwapchainTexture. - pTexture->shaderView = NULL; // We don't allow swapchain texture to be sampled - SDL_SetAtomicInt(&pTexture->referenceCount, 0); - pTexture->subresourceCount = 1; - pTexture->subresources = SDL_malloc(sizeof(D3D11TextureSubresource)); - pTexture->subresources[0].colorTargetViews = SDL_calloc(1, sizeof(ID3D11RenderTargetView *)); - pTexture->subresources[0].colorTargetViews[0] = rtv; - pTexture->subresources[0].uav = NULL; - pTexture->subresources[0].depthStencilTargetView = NULL; - pTexture->subresources[0].layer = 0; - pTexture->subresources[0].level = 0; - pTexture->subresources[0].depth = 1; - pTexture->subresources[0].index = 0; - pTexture->subresources[0].parent = pTexture; - - // Cleanup - ID3D11Texture2D_Release(swapchainTexture); - - return true; -} - -static bool D3D11_INTERNAL_CreateSwapchain( - D3D11Renderer *renderer, - D3D11WindowData *windowData, - SDL_GPUSwapchainComposition swapchainComposition, - SDL_GPUPresentMode presentMode) -{ - HWND dxgiHandle; - Uint32 i; - DXGI_SWAP_CHAIN_DESC swapchainDesc; - DXGI_FORMAT swapchainFormat; - IDXGIFactory1 *pParent; - IDXGISwapChain *swapchain; - IDXGISwapChain3 *swapchain3; - HRESULT res; - - // Get the DXGI handle -#ifdef _WIN32 - dxgiHandle = (HWND)SDL_GetPointerProperty(SDL_GetWindowProperties(windowData->window), SDL_PROP_WINDOW_WIN32_HWND_POINTER, NULL); -#else - dxgiHandle = (HWND)windowData->window; -#endif - - swapchainFormat = SwapchainCompositionToTextureFormat[swapchainComposition]; - - // Initialize the swapchain buffer descriptor - swapchainDesc.BufferDesc.Width = 0; - swapchainDesc.BufferDesc.Height = 0; - swapchainDesc.BufferDesc.RefreshRate.Numerator = 0; - swapchainDesc.BufferDesc.RefreshRate.Denominator = 0; - swapchainDesc.BufferDesc.Format = swapchainFormat; - swapchainDesc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; - swapchainDesc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED; - - // Initialize the rest of the swapchain descriptor - swapchainDesc.SampleDesc.Count = 1; - swapchainDesc.SampleDesc.Quality = 0; - swapchainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; - swapchainDesc.BufferCount = 2; - swapchainDesc.OutputWindow = dxgiHandle; - swapchainDesc.Windowed = 1; - - if (renderer->supportsTearing) { - swapchainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING; - // We know this is supported because tearing support implies DXGI 1.5+ - swapchainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; - } else { - swapchainDesc.Flags = 0; - swapchainDesc.SwapEffect = (renderer->supportsFlipDiscard ? DXGI_SWAP_EFFECT_FLIP_DISCARD : DXGI_SWAP_EFFECT_DISCARD); - } - - // Create the swapchain! - res = IDXGIFactory1_CreateSwapChain( - (IDXGIFactory1 *)renderer->factory, - (IUnknown *)renderer->device, - &swapchainDesc, - &swapchain); - CHECK_D3D11_ERROR_AND_RETURN("Could not create swapchain", false); - - /* - * The swapchain's parent is a separate factory from the factory that - * we used to create the swapchain, and only that parent can be used to - * set the window association. Trying to set an association on our factory - * will silently fail and doesn't even verify arguments or return errors. - * See https://gamedev.net/forums/topic/634235-dxgidisabling-altenter/4999955/ - */ - res = IDXGISwapChain_GetParent( - swapchain, - &D3D_IID_IDXGIFactory1, - (void **)&pParent); - if (FAILED(res)) { - SDL_LogWarn( - SDL_LOG_CATEGORY_GPU, - "Could not get swapchain parent! Error Code: " HRESULT_FMT, - res); - } else { - // Disable DXGI window crap - res = IDXGIFactory1_MakeWindowAssociation( - pParent, - dxgiHandle, - DXGI_MWA_NO_WINDOW_CHANGES); - if (FAILED(res)) { - SDL_LogWarn( - SDL_LOG_CATEGORY_GPU, - "MakeWindowAssociation failed! Error Code: " HRESULT_FMT, - res); - } - - // We're done with the parent now - IDXGIFactory1_Release(pParent); - } - - if (swapchainComposition != SDL_GPU_SWAPCHAINCOMPOSITION_SDR) { - // Set the color space, support already verified if we hit this block - IDXGISwapChain3_QueryInterface( - swapchain, - &D3D_IID_IDXGISwapChain3, - (void **)&swapchain3); - - IDXGISwapChain3_SetColorSpace1( - swapchain3, - SwapchainCompositionToColorSpace[swapchainComposition]); - - IDXGISwapChain3_Release(swapchain3); - } - - // Initialize the swapchain data - windowData->swapchain = swapchain; - windowData->presentMode = presentMode; - windowData->swapchainComposition = swapchainComposition; - windowData->swapchainFormat = swapchainFormat; - windowData->swapchainColorSpace = SwapchainCompositionToColorSpace[swapchainComposition]; - windowData->frameCounter = 0; - - for (i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) { - windowData->inFlightFences[i] = NULL; - } - - /* If a you are using a FLIP model format you can't create the swapchain as DXGI_FORMAT_B8G8R8A8_UNORM_SRGB. - * You have to create the swapchain as DXGI_FORMAT_B8G8R8A8_UNORM and then set the render target view's format to DXGI_FORMAT_B8G8R8A8_UNORM_SRGB - */ - if (!D3D11_INTERNAL_InitializeSwapchainTexture( - renderer, - swapchain, - swapchainFormat, - (swapchainComposition == SDL_GPU_SWAPCHAINCOMPOSITION_SDR_LINEAR) ? DXGI_FORMAT_B8G8R8A8_UNORM_SRGB : windowData->swapchainFormat, - &windowData->texture)) { - IDXGISwapChain_Release(swapchain); - return false; - } - - res = IDXGISwapChain_GetDesc(swapchain, &swapchainDesc); - CHECK_D3D11_ERROR_AND_RETURN("Failed to get swapchain descriptor!", false); - - // Initialize dummy container, width/height will be filled out in AcquireSwapchainTexture - SDL_zerop(&windowData->textureContainer); - windowData->textureContainer.textures = SDL_calloc(1, sizeof(D3D11Texture *)); - windowData->textureContainer.activeTexture = &windowData->texture; - windowData->textureContainer.textures[0] = &windowData->texture; - windowData->textureContainer.canBeCycled = false; - windowData->textureContainer.textureCount = 1; - windowData->textureContainer.textureCapacity = 1; - - windowData->textureContainer.header.info.layer_count_or_depth = 1; - windowData->textureContainer.header.info.format = SwapchainCompositionToSDLTextureFormat[windowData->swapchainComposition]; - windowData->textureContainer.header.info.type = SDL_GPU_TEXTURETYPE_2D; - windowData->textureContainer.header.info.num_levels = 1; - windowData->textureContainer.header.info.sample_count = SDL_GPU_SAMPLECOUNT_1; - windowData->textureContainer.header.info.usage = SDL_GPU_TEXTUREUSAGE_COLOR_TARGET; - windowData->textureContainer.header.info.width = swapchainDesc.BufferDesc.Width; - windowData->textureContainer.header.info.height = swapchainDesc.BufferDesc.Height; - - windowData->texture.container = &windowData->textureContainer; - windowData->texture.containerIndex = 0; - - windowData->width = swapchainDesc.BufferDesc.Width; - windowData->height = swapchainDesc.BufferDesc.Height; - return true; -} - -static bool D3D11_INTERNAL_ResizeSwapchain( - D3D11Renderer *renderer, - D3D11WindowData *windowData) -{ - D3D11_Wait((SDL_GPURenderer *)renderer); - - // Release the old RTV - ID3D11RenderTargetView_Release(windowData->texture.subresources[0].colorTargetViews[0]); - SDL_free(windowData->texture.subresources[0].colorTargetViews); - SDL_free(windowData->texture.subresources); - - // Resize the swapchain - HRESULT res = IDXGISwapChain_ResizeBuffers( - windowData->swapchain, - 0, // Keep buffer count the same - 0, // Use client window width - 0, // Use client window height - DXGI_FORMAT_UNKNOWN, // Keep the old format - renderer->supportsTearing ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0); - CHECK_D3D11_ERROR_AND_RETURN("Could not resize swapchain buffers", false); - - // Create the texture object for the swapchain - bool result = D3D11_INTERNAL_InitializeSwapchainTexture( - renderer, - windowData->swapchain, - windowData->swapchainFormat, - (windowData->swapchainComposition == SDL_GPU_SWAPCHAINCOMPOSITION_SDR_LINEAR) ? DXGI_FORMAT_B8G8R8A8_UNORM_SRGB : windowData->swapchainFormat, - &windowData->texture); - - DXGI_SWAP_CHAIN_DESC swapchainDesc; - res = IDXGISwapChain_GetDesc(windowData->swapchain, &swapchainDesc); - CHECK_D3D11_ERROR_AND_RETURN("Failed to get swapchain descriptor!", false); - - windowData->textureContainer.header.info.width = swapchainDesc.BufferDesc.Width; - windowData->textureContainer.header.info.height = swapchainDesc.BufferDesc.Height; - windowData->width = swapchainDesc.BufferDesc.Width; - windowData->height = swapchainDesc.BufferDesc.Height; - windowData->needsSwapchainRecreate = !result; - return result; -} - -static bool D3D11_SupportsSwapchainComposition( - SDL_GPURenderer *driverData, - SDL_Window *window, - SDL_GPUSwapchainComposition swapchainComposition) -{ - D3D11Renderer *renderer = (D3D11Renderer *)driverData; - DXGI_FORMAT format; - Uint32 formatSupport = 0; - IDXGISwapChain3 *swapchain3; - Uint32 colorSpaceSupport; - HRESULT res; - - format = SwapchainCompositionToTextureFormat[swapchainComposition]; - - res = ID3D11Device_CheckFormatSupport( - renderer->device, - format, - &formatSupport); - if (FAILED(res)) { - // Format is apparently unknown - return false; - } - - if (!(formatSupport & D3D11_FORMAT_SUPPORT_DISPLAY)) { - return false; - } - - D3D11WindowData *windowData = D3D11_INTERNAL_FetchWindowData(window); - if (windowData == NULL) { - SET_STRING_ERROR_AND_RETURN("Must claim window before querying swapchain composition support!", false) - } - - // Check the color space support if necessary - if (swapchainComposition != SDL_GPU_SWAPCHAINCOMPOSITION_SDR) { - if (SUCCEEDED(IDXGISwapChain3_QueryInterface( - windowData->swapchain, - &D3D_IID_IDXGISwapChain3, - (void **)&swapchain3))) { - IDXGISwapChain3_CheckColorSpaceSupport( - swapchain3, - SwapchainCompositionToColorSpace[swapchainComposition], - &colorSpaceSupport); - - IDXGISwapChain3_Release(swapchain3); - - if (!(colorSpaceSupport & DXGI_SWAP_CHAIN_COLOR_SPACE_SUPPORT_FLAG_PRESENT)) { - return false; - } - } else { - SET_STRING_ERROR_AND_RETURN("DXGI 1.4 not supported, cannot use composition other than SDL_GPU_SWAPCHAINCOMPOSITION_SDR!", false) - } - } - - return true; -} - -static bool D3D11_SupportsPresentMode( - SDL_GPURenderer *driverData, - SDL_Window *window, - SDL_GPUPresentMode presentMode) -{ - D3D11Renderer *renderer = (D3D11Renderer *)driverData; - (void)window; // used by other backends - switch (presentMode) { - case SDL_GPU_PRESENTMODE_IMMEDIATE: - case SDL_GPU_PRESENTMODE_VSYNC: - return true; - case SDL_GPU_PRESENTMODE_MAILBOX: - return renderer->supportsFlipDiscard; - } - SDL_assert(!"Unrecognized present mode"); - return false; -} - -static bool D3D11_ClaimWindow( - SDL_GPURenderer *driverData, - SDL_Window *window) -{ - D3D11Renderer *renderer = (D3D11Renderer *)driverData; - D3D11WindowData *windowData = D3D11_INTERNAL_FetchWindowData(window); - - if (windowData == NULL) { - windowData = (D3D11WindowData *)SDL_calloc(1, sizeof(D3D11WindowData)); - windowData->window = window; - - if (D3D11_INTERNAL_CreateSwapchain(renderer, windowData, SDL_GPU_SWAPCHAINCOMPOSITION_SDR, SDL_GPU_PRESENTMODE_VSYNC)) { - SDL_SetPointerProperty(SDL_GetWindowProperties(window), WINDOW_PROPERTY_DATA, windowData); - - SDL_LockMutex(renderer->windowLock); - if (renderer->claimedWindowCount >= renderer->claimedWindowCapacity) { - renderer->claimedWindowCapacity *= 2; - renderer->claimedWindows = SDL_realloc( - renderer->claimedWindows, - renderer->claimedWindowCapacity * sizeof(D3D11WindowData *)); - } - renderer->claimedWindows[renderer->claimedWindowCount] = windowData; - renderer->claimedWindowCount += 1; - SDL_UnlockMutex(renderer->windowLock); - - SDL_AddEventWatch(D3D11_INTERNAL_OnWindowResize, window); - - return true; - } else { - SDL_free(windowData); - SET_STRING_ERROR_AND_RETURN("Could not create swapchain, failed to claim window!", false) - } - } else { - SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "Window already claimed!"); - return false; - } -} - -static void D3D11_INTERNAL_DestroySwapchain( - D3D11Renderer *renderer, - D3D11WindowData *windowData) -{ - Uint32 i; - - D3D11_Wait((SDL_GPURenderer *)renderer); - - ID3D11RenderTargetView_Release(windowData->texture.subresources[0].colorTargetViews[0]); - SDL_free(windowData->texture.subresources[0].colorTargetViews); - SDL_free(windowData->texture.subresources); - SDL_free(windowData->textureContainer.textures); - IDXGISwapChain_Release(windowData->swapchain); - - // DXGI will crash if we don't flush deferred swapchain destruction - SDL_LockMutex(renderer->contextLock); - ID3D11DeviceContext_ClearState(renderer->immediateContext); - ID3D11DeviceContext_Flush(renderer->immediateContext); - SDL_UnlockMutex(renderer->contextLock); - - windowData->swapchain = NULL; - - for (i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) { - if (windowData->inFlightFences[i] != NULL) { - D3D11_ReleaseFence( - (SDL_GPURenderer *)renderer, - windowData->inFlightFences[i]); - } - } -} - -static void D3D11_ReleaseWindow( - SDL_GPURenderer *driverData, - SDL_Window *window) -{ - D3D11Renderer *renderer = (D3D11Renderer *)driverData; - D3D11WindowData *windowData = D3D11_INTERNAL_FetchWindowData(window); - - if (windowData == NULL) { - return; - } - - D3D11_INTERNAL_DestroySwapchain( - renderer, - windowData); - - SDL_LockMutex(renderer->windowLock); - for (Uint32 i = 0; i < renderer->claimedWindowCount; i += 1) { - if (renderer->claimedWindows[i]->window == window) { - renderer->claimedWindows[i] = renderer->claimedWindows[renderer->claimedWindowCount - 1]; - renderer->claimedWindowCount -= 1; - break; - } - } - SDL_UnlockMutex(renderer->windowLock); - - SDL_free(windowData); - - SDL_ClearProperty(SDL_GetWindowProperties(window), WINDOW_PROPERTY_DATA); - SDL_RemoveEventWatch(D3D11_INTERNAL_OnWindowResize, window); -} - -static bool D3D11_AcquireSwapchainTexture( - SDL_GPUCommandBuffer *commandBuffer, - SDL_Window *window, - SDL_GPUTexture **swapchainTexture, - Uint32 *swapchainTextureWidth, - Uint32 *swapchainTextureHeight) -{ - D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer; - D3D11Renderer *renderer = (D3D11Renderer *)d3d11CommandBuffer->renderer; - D3D11WindowData *windowData; - HRESULT res; - - *swapchainTexture = NULL; - if (swapchainTextureWidth) { - *swapchainTextureWidth = 0; - } - if (swapchainTextureHeight) { - *swapchainTextureHeight = 0; - } - - windowData = D3D11_INTERNAL_FetchWindowData(window); - if (windowData == NULL) { - SET_STRING_ERROR_AND_RETURN("Cannot acquire a swapchain texture from an unclaimed window!", false) - } - - if (windowData->needsSwapchainRecreate) { - if (!D3D11_INTERNAL_ResizeSwapchain(renderer, windowData)) { - return false; - } - } - - if (swapchainTextureWidth) { - *swapchainTextureWidth = windowData->width; - } - if (swapchainTextureHeight) { - *swapchainTextureHeight = windowData->height; - } - - if (windowData->inFlightFences[windowData->frameCounter] != NULL) { - if (windowData->presentMode == SDL_GPU_PRESENTMODE_VSYNC) { - // In VSYNC mode, block until the least recent presented frame is done - if (!D3D11_WaitForFences( - (SDL_GPURenderer *)renderer, - true, - &windowData->inFlightFences[windowData->frameCounter], - 1)) { - return false; - } - } else { - if (!D3D11_QueryFence( - (SDL_GPURenderer *)d3d11CommandBuffer->renderer, - windowData->inFlightFences[windowData->frameCounter])) { - /* - * In MAILBOX or IMMEDIATE mode, if the least recent fence is not signaled, - * return true to indicate that there is no error but rendering should be skipped - */ - return true; - } - } - - D3D11_ReleaseFence( - (SDL_GPURenderer *)d3d11CommandBuffer->renderer, - windowData->inFlightFences[windowData->frameCounter]); - - windowData->inFlightFences[windowData->frameCounter] = NULL; - } - - // Set the handle on the windowData texture data. - res = IDXGISwapChain_GetBuffer( - windowData->swapchain, - 0, - &D3D_IID_ID3D11Texture2D, - (void **)&windowData->texture.handle); - CHECK_D3D11_ERROR_AND_RETURN("Could not acquire swapchain!", false); - - // Set up presentation - if (d3d11CommandBuffer->windowDataCount == d3d11CommandBuffer->windowDataCapacity) { - d3d11CommandBuffer->windowDataCapacity += 1; - d3d11CommandBuffer->windowDatas = SDL_realloc( - d3d11CommandBuffer->windowDatas, - d3d11CommandBuffer->windowDataCapacity * sizeof(D3D11WindowData *)); - } - d3d11CommandBuffer->windowDatas[d3d11CommandBuffer->windowDataCount] = windowData; - d3d11CommandBuffer->windowDataCount += 1; - - // Return the swapchain texture - *swapchainTexture = (SDL_GPUTexture*) &windowData->textureContainer; - return true; -} - -static SDL_GPUTextureFormat D3D11_GetSwapchainTextureFormat( - SDL_GPURenderer *driverData, - SDL_Window *window) -{ - D3D11Renderer *renderer = (D3D11Renderer *)driverData; - D3D11WindowData *windowData = D3D11_INTERNAL_FetchWindowData(window); - - if (windowData == NULL) { - SET_STRING_ERROR_AND_RETURN("Cannot get swapchain format, window has not been claimed!", SDL_GPU_TEXTUREFORMAT_INVALID) - } - - return windowData->textureContainer.header.info.format; -} - -static bool D3D11_SetSwapchainParameters( - SDL_GPURenderer *driverData, - SDL_Window *window, - SDL_GPUSwapchainComposition swapchainComposition, - SDL_GPUPresentMode presentMode) -{ - D3D11Renderer *renderer = (D3D11Renderer *)driverData; - D3D11WindowData *windowData = D3D11_INTERNAL_FetchWindowData(window); - - if (windowData == NULL) { - SET_STRING_ERROR_AND_RETURN("Cannot set swapchain parameters on unclaimed window!", false) - } - - if (!D3D11_SupportsSwapchainComposition(driverData, window, swapchainComposition)) { - SET_STRING_ERROR_AND_RETURN("Swapchain composition not supported!", false) - } - - if (!D3D11_SupportsPresentMode(driverData, window, presentMode)) { - SET_STRING_ERROR_AND_RETURN("Present mode not supported!", false) - } - - if ( - swapchainComposition != windowData->swapchainComposition || - presentMode != windowData->presentMode) { - D3D11_Wait(driverData); - - // Recreate the swapchain - D3D11_INTERNAL_DestroySwapchain( - renderer, - windowData); - - return D3D11_INTERNAL_CreateSwapchain( - renderer, - windowData, - swapchainComposition, - presentMode); - } - - return true; -} - -// Submission - -static bool D3D11_Submit( - SDL_GPUCommandBuffer *commandBuffer) -{ - D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer; - D3D11Renderer *renderer = (D3D11Renderer *)d3d11CommandBuffer->renderer; - ID3D11CommandList *commandList; - HRESULT res; - - // Unmap uniform buffers - - for (Uint32 i = 0; i < MAX_UNIFORM_BUFFERS_PER_STAGE; i += 1) { - if (d3d11CommandBuffer->vertexUniformBuffers[i] != NULL) { - ID3D11DeviceContext_Unmap( - d3d11CommandBuffer->context, - (ID3D11Resource *)d3d11CommandBuffer->vertexUniformBuffers[i]->buffer, - 0); - } - - if (d3d11CommandBuffer->fragmentUniformBuffers[i] != NULL) { - ID3D11DeviceContext_Unmap( - d3d11CommandBuffer->context, - (ID3D11Resource *)d3d11CommandBuffer->fragmentUniformBuffers[i]->buffer, - 0); - } - - if (d3d11CommandBuffer->computeUniformBuffers[i] != NULL) { - ID3D11DeviceContext_Unmap( - d3d11CommandBuffer->context, - (ID3D11Resource *)d3d11CommandBuffer->computeUniformBuffers[i]->buffer, - 0); - } - } - - SDL_LockMutex(renderer->contextLock); - - if (!D3D11_INTERNAL_AcquireFence(d3d11CommandBuffer)) { - SDL_UnlockMutex(renderer->contextLock); - return false; - } - - // Notify the command buffer completion query that we have completed recording - ID3D11DeviceContext_End( - renderer->immediateContext, - (ID3D11Asynchronous *)d3d11CommandBuffer->fence->handle); - - // Serialize the commands into the command list - res = ID3D11DeviceContext_FinishCommandList( - d3d11CommandBuffer->context, - 0, - &commandList); - if (FAILED(res)) { - SDL_UnlockMutex(renderer->contextLock); - CHECK_D3D11_ERROR_AND_RETURN("Could not finish command list recording!", false) - } - - // Submit the command list to the immediate context - ID3D11DeviceContext_ExecuteCommandList( - renderer->immediateContext, - commandList, - 0); - ID3D11CommandList_Release(commandList); - - // Mark the command buffer as submitted - if (renderer->submittedCommandBufferCount >= renderer->submittedCommandBufferCapacity) { - renderer->submittedCommandBufferCapacity = renderer->submittedCommandBufferCount + 1; - - renderer->submittedCommandBuffers = SDL_realloc( - renderer->submittedCommandBuffers, - sizeof(D3D11CommandBuffer *) * renderer->submittedCommandBufferCapacity); - } - - renderer->submittedCommandBuffers[renderer->submittedCommandBufferCount] = d3d11CommandBuffer; - renderer->submittedCommandBufferCount += 1; - - bool result = true; - - // Present, if applicable - for (Uint32 i = 0; i < d3d11CommandBuffer->windowDataCount; i += 1) { - D3D11WindowData *windowData = d3d11CommandBuffer->windowDatas[i]; - - Uint32 syncInterval = 1; - if (windowData->presentMode == SDL_GPU_PRESENTMODE_IMMEDIATE || - (renderer->supportsFlipDiscard && windowData->presentMode == SDL_GPU_PRESENTMODE_MAILBOX)) { - syncInterval = 0; - } - - Uint32 presentFlags = 0; - if (renderer->supportsTearing && - windowData->presentMode == SDL_GPU_PRESENTMODE_IMMEDIATE) { - presentFlags = DXGI_PRESENT_ALLOW_TEARING; - } - - res = IDXGISwapChain_Present( - windowData->swapchain, - syncInterval, - presentFlags); - - if (FAILED(res)) { - result = false; - } - - ID3D11Texture2D_Release(windowData->texture.handle); - - windowData->inFlightFences[windowData->frameCounter] = (SDL_GPUFence*)d3d11CommandBuffer->fence; - - (void)SDL_AtomicIncRef(&d3d11CommandBuffer->fence->referenceCount); - - windowData->frameCounter = (windowData->frameCounter + 1) % MAX_FRAMES_IN_FLIGHT; - } - - // Check if we can perform any cleanups - for (Sint32 i = renderer->submittedCommandBufferCount - 1; i >= 0; i -= 1) { - BOOL queryData; - res = ID3D11DeviceContext_GetData( - renderer->immediateContext, - (ID3D11Asynchronous *)renderer->submittedCommandBuffers[i]->fence->handle, - &queryData, - sizeof(queryData), - 0); - if (res == S_OK) { - result &= D3D11_INTERNAL_CleanCommandBuffer( - renderer, - renderer->submittedCommandBuffers[i], - false); - } - } - - D3D11_INTERNAL_PerformPendingDestroys(renderer); - - SDL_UnlockMutex(renderer->contextLock); - - return result; -} - -static SDL_GPUFence *D3D11_SubmitAndAcquireFence( - SDL_GPUCommandBuffer *commandBuffer) -{ - D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer; - d3d11CommandBuffer->autoReleaseFence = false; - if (!D3D11_Submit(commandBuffer)) { - return NULL; - } - return (SDL_GPUFence *)d3d11CommandBuffer->fence; -} - -static bool D3D11_Cancel( - SDL_GPUCommandBuffer *commandBuffer) -{ - D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer; - D3D11Renderer *renderer = d3d11CommandBuffer->renderer; - bool result; - - d3d11CommandBuffer->autoReleaseFence = false; - SDL_LockMutex(renderer->contextLock); - result = D3D11_INTERNAL_CleanCommandBuffer(renderer, d3d11CommandBuffer, true); - SDL_UnlockMutex(renderer->contextLock); - - return result; -} - -static bool D3D11_Wait( - SDL_GPURenderer *driverData) -{ - D3D11Renderer *renderer = (D3D11Renderer *)driverData; - D3D11CommandBuffer *commandBuffer; - bool result = true; - - /* - * Wait for all submitted command buffers to complete. - * Sort of equivalent to vkDeviceWaitIdle. - */ - for (Uint32 i = 0; i < renderer->submittedCommandBufferCount; i += 1) { - D3D11_INTERNAL_WaitForFence( - renderer, - renderer->submittedCommandBuffers[i]->fence); - } - - SDL_LockMutex(renderer->contextLock); // This effectively acts as a lock around submittedCommandBuffers - - for (Sint32 i = renderer->submittedCommandBufferCount - 1; i >= 0; i -= 1) { - commandBuffer = renderer->submittedCommandBuffers[i]; - result &= D3D11_INTERNAL_CleanCommandBuffer(renderer, commandBuffer, false); - } - - D3D11_INTERNAL_PerformPendingDestroys(renderer); - - SDL_UnlockMutex(renderer->contextLock); - - return result; -} - -// Format Info - -static bool D3D11_SupportsTextureFormat( - SDL_GPURenderer *driverData, - SDL_GPUTextureFormat format, - SDL_GPUTextureType type, - SDL_GPUTextureUsageFlags usage) -{ - D3D11Renderer *renderer = (D3D11Renderer *)driverData; - DXGI_FORMAT dxgiFormat = SDLToD3D11_TextureFormat[format]; - DXGI_FORMAT typelessFormat = D3D11_INTERNAL_GetTypelessFormat(dxgiFormat); - UINT formatSupport, sampleableFormatSupport; - D3D11_FEATURE_DATA_FORMAT_SUPPORT2 formatSupport2 = { dxgiFormat, 0 }; - HRESULT res; - - res = ID3D11Device_CheckFormatSupport( - renderer->device, - dxgiFormat, - &formatSupport); - if (FAILED(res)) { - // Format is apparently unknown - return false; - } - - /* Depth textures are stored as typeless textures, but interpreted as color textures for sampling. - * In order to get supported usages for both interpretations, we have to do this. - */ - if (typelessFormat != DXGI_FORMAT_UNKNOWN) { - res = ID3D11Device_CheckFormatSupport( - renderer->device, - D3D11_INTERNAL_GetSampleableFormat(typelessFormat), - &sampleableFormatSupport); - if (SUCCEEDED(res)) { - formatSupport |= sampleableFormatSupport; - } - } - - // Checks for SIMULTANEOUS_READ_WRITE support - if (usage & SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_SIMULTANEOUS_READ_WRITE) { - res = ID3D11Device_CheckFeatureSupport( - renderer->device, - D3D11_FEATURE_FORMAT_SUPPORT2, - &formatSupport2, - sizeof(formatSupport2)); - if (FAILED(res)) { - // Format is apparently unknown - return false; - } - } - - // Is the texture type supported? - if (type == SDL_GPU_TEXTURETYPE_2D && !(formatSupport & D3D11_FORMAT_SUPPORT_TEXTURE2D)) { - return false; - } - if (type == SDL_GPU_TEXTURETYPE_2D_ARRAY && !(formatSupport & D3D11_FORMAT_SUPPORT_TEXTURE2D)) { - return false; - } - if (type == SDL_GPU_TEXTURETYPE_3D && !(formatSupport & D3D11_FORMAT_SUPPORT_TEXTURE3D)) { - return false; - } - if (type == SDL_GPU_TEXTURETYPE_CUBE && !(formatSupport & D3D11_FORMAT_SUPPORT_TEXTURECUBE)) { - return false; - } - if (type == SDL_GPU_TEXTURETYPE_CUBE_ARRAY && !(formatSupport & D3D11_FORMAT_SUPPORT_TEXTURECUBE)) { - return false; - } - - // Are the usage flags supported? - if ((usage & SDL_GPU_TEXTUREUSAGE_SAMPLER) && !(formatSupport & D3D11_FORMAT_SUPPORT_SHADER_SAMPLE)) { - return false; - } - if ((usage & (SDL_GPU_TEXTUREUSAGE_GRAPHICS_STORAGE_READ | SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_READ)) && !(formatSupport & D3D11_FORMAT_SUPPORT_SHADER_LOAD)) { - return false; - } - if ((usage & (SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE) && !(formatSupport & D3D11_FORMAT_SUPPORT_TYPED_UNORDERED_ACCESS_VIEW))) { - // TYPED_UNORDERED_ACCESS_VIEW implies support for typed UAV stores - return false; - } - #define D3D11_FORMAT_SUPPORT2_UAV_TYPED_LOAD 0x40 /* for old toolchains */ - if ((usage & (SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_SIMULTANEOUS_READ_WRITE) && !(formatSupport2.OutFormatSupport2 & D3D11_FORMAT_SUPPORT2_UAV_TYPED_LOAD))) { - return false; - } - if ((usage & SDL_GPU_TEXTUREUSAGE_COLOR_TARGET) && !(formatSupport & D3D11_FORMAT_SUPPORT_RENDER_TARGET)) { - return false; - } - if ((usage & SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET) && !(formatSupport & D3D11_FORMAT_SUPPORT_DEPTH_STENCIL)) { - return false; - } - - return true; -} - -// Device Creation - -static bool D3D11_PrepareDriver(SDL_VideoDevice *this) -{ - SDL_SharedObject *d3d11_dll; - SDL_SharedObject *dxgi_dll; - PFN_D3D11_CREATE_DEVICE D3D11CreateDeviceFunc; - D3D_FEATURE_LEVEL levels[] = { D3D_FEATURE_LEVEL_11_1 }; - PFN_CREATE_DXGI_FACTORY1 CreateDxgiFactoryFunc; - HRESULT res; - - // Can we load D3D11? - - d3d11_dll = SDL_LoadObject(D3D11_DLL); - if (d3d11_dll == NULL) { - SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "D3D11: Could not find " D3D11_DLL); - return false; - } - - D3D11CreateDeviceFunc = (PFN_D3D11_CREATE_DEVICE)SDL_LoadFunction( - d3d11_dll, - D3D11_CREATE_DEVICE_FUNC); - if (D3D11CreateDeviceFunc == NULL) { - SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "D3D11: Could not find function " D3D11_CREATE_DEVICE_FUNC " in " D3D11_DLL); - SDL_UnloadObject(d3d11_dll); - return false; - } - - // Can we create a device? - - res = D3D11CreateDeviceFunc( - NULL, - D3D_DRIVER_TYPE_HARDWARE, - NULL, - D3D11_CREATE_DEVICE_BGRA_SUPPORT, - levels, - SDL_arraysize(levels), - D3D11_SDK_VERSION, - NULL, - NULL, - NULL); - - SDL_UnloadObject(d3d11_dll); - - if (FAILED(res)) { - SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "D3D11: Could not create D3D11Device with feature level 11_1"); - return false; - } - - // Can we load DXGI? - - dxgi_dll = SDL_LoadObject(DXGI_DLL); - if (dxgi_dll == NULL) { - SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "D3D11: Could not find " DXGI_DLL); - return false; - } - - CreateDxgiFactoryFunc = (PFN_CREATE_DXGI_FACTORY1)SDL_LoadFunction( - dxgi_dll, - CREATE_DXGI_FACTORY1_FUNC); - SDL_UnloadObject(dxgi_dll); // We're not going to call this function, so we can just unload now. - if (CreateDxgiFactoryFunc == NULL) { - SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "D3D11: Could not find function " CREATE_DXGI_FACTORY1_FUNC " in " DXGI_DLL); - return false; - } - - return true; -} - -static void D3D11_INTERNAL_TryInitializeDXGIDebug(D3D11Renderer *renderer) -{ - PFN_DXGI_GET_DEBUG_INTERFACE DXGIGetDebugInterfaceFunc; - HRESULT res; - - renderer->dxgidebug_dll = SDL_LoadObject(DXGIDEBUG_DLL); - if (renderer->dxgidebug_dll == NULL) { - SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "Could not find " DXGIDEBUG_DLL); - return; - } - - DXGIGetDebugInterfaceFunc = (PFN_DXGI_GET_DEBUG_INTERFACE)SDL_LoadFunction( - renderer->dxgidebug_dll, - DXGI_GET_DEBUG_INTERFACE_FUNC); - if (DXGIGetDebugInterfaceFunc == NULL) { - SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "Could not load function: " DXGI_GET_DEBUG_INTERFACE_FUNC); - return; - } - - res = DXGIGetDebugInterfaceFunc(&D3D_IID_IDXGIDebug, (void **)&renderer->dxgiDebug); - if (FAILED(res)) { - SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "Could not get IDXGIDebug interface"); - } - -#ifdef HAVE_IDXGIINFOQUEUE - res = DXGIGetDebugInterfaceFunc(&D3D_IID_IDXGIInfoQueue, (void **)&renderer->dxgiInfoQueue); - if (FAILED(res)) { - SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "Could not get IDXGIInfoQueue interface"); - } -#endif -} - -static void D3D11_INTERNAL_InitBlitPipelines( - D3D11Renderer *renderer) -{ - SDL_GPUShaderCreateInfo shaderCreateInfo; - SDL_GPUShader *fullscreenVertexShader; - SDL_GPUShader *blitFrom2DPixelShader; - SDL_GPUShader *blitFrom2DArrayPixelShader; - SDL_GPUShader *blitFrom3DPixelShader; - SDL_GPUShader *blitFromCubePixelShader; - SDL_GPUShader *blitFromCubeArrayPixelShader; - SDL_GPUGraphicsPipelineCreateInfo blitPipelineCreateInfo; - SDL_GPUGraphicsPipeline *blitPipeline; - SDL_GPUSamplerCreateInfo samplerCreateInfo; - SDL_GPUColorTargetDescription colorTargetDesc; - - // Fullscreen vertex shader - SDL_zero(shaderCreateInfo); - shaderCreateInfo.code = (Uint8 *)D3D11_FullscreenVert; - shaderCreateInfo.code_size = sizeof(D3D11_FullscreenVert); - shaderCreateInfo.stage = SDL_GPU_SHADERSTAGE_VERTEX; - shaderCreateInfo.format = SDL_GPU_SHADERFORMAT_DXBC; - shaderCreateInfo.entrypoint = "main"; - - fullscreenVertexShader = D3D11_CreateShader( - (SDL_GPURenderer *)renderer, - &shaderCreateInfo); - - if (fullscreenVertexShader == NULL) { - SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to compile vertex shader for blit!"); - } - - // BlitFrom2D pixel shader - shaderCreateInfo.code = (Uint8 *)D3D11_BlitFrom2D; - shaderCreateInfo.code_size = sizeof(D3D11_BlitFrom2D); - shaderCreateInfo.stage = SDL_GPU_SHADERSTAGE_FRAGMENT; - shaderCreateInfo.num_samplers = 1; - shaderCreateInfo.num_uniform_buffers = 1; - - blitFrom2DPixelShader = D3D11_CreateShader( - (SDL_GPURenderer *)renderer, - &shaderCreateInfo); - - if (blitFrom2DPixelShader == NULL) { - SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to compile BlitFrom2D pixel shader!"); - } - - // BlitFrom2DArray pixel shader - shaderCreateInfo.code = (Uint8 *)D3D11_BlitFrom2DArray; - shaderCreateInfo.code_size = sizeof(D3D11_BlitFrom2DArray); - - blitFrom2DArrayPixelShader = D3D11_CreateShader( - (SDL_GPURenderer *)renderer, - &shaderCreateInfo); - - if (blitFrom2DArrayPixelShader == NULL) { - SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to compile BlitFrom2DArray pixel shader!"); - } - - // BlitFrom3D pixel shader - shaderCreateInfo.code = (Uint8 *)D3D11_BlitFrom3D; - shaderCreateInfo.code_size = sizeof(D3D11_BlitFrom3D); - - blitFrom3DPixelShader = D3D11_CreateShader( - (SDL_GPURenderer *)renderer, - &shaderCreateInfo); - - if (blitFrom3DPixelShader == NULL) { - SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to compile BlitFrom3D pixel shader!"); - } - - // BlitFromCube pixel shader - shaderCreateInfo.code = (Uint8 *)D3D11_BlitFromCube; - shaderCreateInfo.code_size = sizeof(D3D11_BlitFromCube); - - blitFromCubePixelShader = D3D11_CreateShader( - (SDL_GPURenderer *)renderer, - &shaderCreateInfo); - - if (blitFromCubePixelShader == NULL) { - SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to compile BlitFromCube pixel shader!"); - } - - // BlitFromCubeArray pixel shader - shaderCreateInfo.code = (Uint8 *)D3D11_BlitFromCubeArray; - shaderCreateInfo.code_size = sizeof(D3D11_BlitFromCubeArray); - - blitFromCubeArrayPixelShader = D3D11_CreateShader( - (SDL_GPURenderer *)renderer, - &shaderCreateInfo); - - if (blitFromCubeArrayPixelShader == NULL) { - SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to compile BlitFromCubeArray pixel shader!"); - } - - // BlitFrom2D pipeline - SDL_zero(blitPipelineCreateInfo); - - SDL_zero(colorTargetDesc); - colorTargetDesc.blend_state.color_write_mask = 0xF; - colorTargetDesc.format = SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM; // format doesn't matter in d3d11 - - blitPipelineCreateInfo.target_info.color_target_descriptions = &colorTargetDesc; - blitPipelineCreateInfo.target_info.num_color_targets = 1; - blitPipelineCreateInfo.target_info.depth_stencil_format = SDL_GPU_TEXTUREFORMAT_D16_UNORM; // arbitrary - blitPipelineCreateInfo.target_info.has_depth_stencil_target = false; - - blitPipelineCreateInfo.vertex_shader = fullscreenVertexShader; - blitPipelineCreateInfo.fragment_shader = blitFrom2DPixelShader; - - blitPipelineCreateInfo.multisample_state.sample_count = SDL_GPU_SAMPLECOUNT_1; - blitPipelineCreateInfo.multisample_state.enable_mask = false; - - blitPipelineCreateInfo.primitive_type = SDL_GPU_PRIMITIVETYPE_TRIANGLELIST; - - blitPipeline = D3D11_CreateGraphicsPipeline( - (SDL_GPURenderer *)renderer, - &blitPipelineCreateInfo); - - if (blitPipeline == NULL) { - SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create BlitFrom2D pipeline!"); - } - - renderer->blitPipelines[SDL_GPU_TEXTURETYPE_2D].pipeline = blitPipeline; - renderer->blitPipelines[SDL_GPU_TEXTURETYPE_2D].type = SDL_GPU_TEXTURETYPE_2D; - renderer->blitPipelines[SDL_GPU_TEXTURETYPE_2D].format = SDL_GPU_TEXTUREFORMAT_INVALID; - - // BlitFrom2DArrayPipeline - blitPipelineCreateInfo.fragment_shader = blitFrom2DArrayPixelShader; - blitPipeline = D3D11_CreateGraphicsPipeline( - (SDL_GPURenderer *)renderer, - &blitPipelineCreateInfo); - - if (blitPipeline == NULL) { - SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create BlitFrom2DArray pipeline!"); - } - - renderer->blitPipelines[SDL_GPU_TEXTURETYPE_2D_ARRAY].pipeline = blitPipeline; - renderer->blitPipelines[SDL_GPU_TEXTURETYPE_2D_ARRAY].type = SDL_GPU_TEXTURETYPE_2D_ARRAY; - renderer->blitPipelines[SDL_GPU_TEXTURETYPE_2D_ARRAY].format = SDL_GPU_TEXTUREFORMAT_INVALID; - - // BlitFrom3DPipeline - blitPipelineCreateInfo.fragment_shader = blitFrom3DPixelShader; - blitPipeline = D3D11_CreateGraphicsPipeline( - (SDL_GPURenderer *)renderer, - &blitPipelineCreateInfo); - - if (blitPipeline == NULL) { - SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create BlitFrom3D pipeline!"); - } - - renderer->blitPipelines[SDL_GPU_TEXTURETYPE_3D].pipeline = blitPipeline; - renderer->blitPipelines[SDL_GPU_TEXTURETYPE_3D].type = SDL_GPU_TEXTURETYPE_3D; - renderer->blitPipelines[SDL_GPU_TEXTURETYPE_3D].format = SDL_GPU_TEXTUREFORMAT_INVALID; - - // BlitFromCubePipeline - blitPipelineCreateInfo.fragment_shader = blitFromCubePixelShader; - blitPipeline = D3D11_CreateGraphicsPipeline( - (SDL_GPURenderer *)renderer, - &blitPipelineCreateInfo); - - if (blitPipeline == NULL) { - SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create BlitFromCube pipeline!"); - } - - renderer->blitPipelines[SDL_GPU_TEXTURETYPE_CUBE].pipeline = blitPipeline; - renderer->blitPipelines[SDL_GPU_TEXTURETYPE_CUBE].type = SDL_GPU_TEXTURETYPE_CUBE; - renderer->blitPipelines[SDL_GPU_TEXTURETYPE_CUBE].format = SDL_GPU_TEXTUREFORMAT_INVALID; - - // BlitFromCubeArrayPipeline - blitPipelineCreateInfo.fragment_shader = blitFromCubeArrayPixelShader; - blitPipeline = D3D11_CreateGraphicsPipeline( - (SDL_GPURenderer *)renderer, - &blitPipelineCreateInfo); - - if (blitPipeline == NULL) { - SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create BlitFromCubeArray pipeline!"); - } - - renderer->blitPipelines[SDL_GPU_TEXTURETYPE_CUBE_ARRAY].pipeline = blitPipeline; - renderer->blitPipelines[SDL_GPU_TEXTURETYPE_CUBE_ARRAY].type = SDL_GPU_TEXTURETYPE_CUBE_ARRAY; - renderer->blitPipelines[SDL_GPU_TEXTURETYPE_CUBE_ARRAY].format = SDL_GPU_TEXTUREFORMAT_INVALID; - - // Create samplers - samplerCreateInfo.address_mode_u = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE; - samplerCreateInfo.address_mode_v = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE; - samplerCreateInfo.address_mode_w = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE; - samplerCreateInfo.enable_anisotropy = 0; - samplerCreateInfo.enable_compare = 0; - samplerCreateInfo.mag_filter = SDL_GPU_FILTER_NEAREST; - samplerCreateInfo.min_filter = SDL_GPU_FILTER_NEAREST; - samplerCreateInfo.mipmap_mode = SDL_GPU_SAMPLERMIPMAPMODE_NEAREST; - samplerCreateInfo.mip_lod_bias = 0.0f; - samplerCreateInfo.min_lod = 0; - samplerCreateInfo.max_lod = 1000; - - renderer->blitNearestSampler = D3D11_CreateSampler( - (SDL_GPURenderer *)renderer, - &samplerCreateInfo); - - if (renderer->blitNearestSampler == NULL) { - SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create blit nearest sampler!"); - } - - samplerCreateInfo.mag_filter = SDL_GPU_FILTER_LINEAR; - samplerCreateInfo.min_filter = SDL_GPU_FILTER_LINEAR; - samplerCreateInfo.mipmap_mode = SDL_GPU_SAMPLERMIPMAPMODE_LINEAR; - - renderer->blitLinearSampler = D3D11_CreateSampler( - (SDL_GPURenderer *)renderer, - &samplerCreateInfo); - - if (renderer->blitLinearSampler == NULL) { - SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create blit linear sampler!"); - } - - // Clean up - D3D11_ReleaseShader((SDL_GPURenderer *)renderer, fullscreenVertexShader); - D3D11_ReleaseShader((SDL_GPURenderer *)renderer, blitFrom2DPixelShader); - D3D11_ReleaseShader((SDL_GPURenderer *)renderer, blitFrom2DArrayPixelShader); - D3D11_ReleaseShader((SDL_GPURenderer *)renderer, blitFrom3DPixelShader); - D3D11_ReleaseShader((SDL_GPURenderer *)renderer, blitFromCubePixelShader); - D3D11_ReleaseShader((SDL_GPURenderer *)renderer, blitFromCubeArrayPixelShader); -} - -static void D3D11_INTERNAL_DestroyBlitPipelines( - SDL_GPURenderer *driverData) -{ - D3D11Renderer *renderer = (D3D11Renderer *)driverData; - D3D11_ReleaseSampler(driverData, renderer->blitLinearSampler); - D3D11_ReleaseSampler(driverData, renderer->blitNearestSampler); - for (int i = 0; i < SDL_arraysize(renderer->blitPipelines); i += 1) { - D3D11_ReleaseGraphicsPipeline(driverData, renderer->blitPipelines[i].pipeline); - } -} - -static SDL_GPUDevice *D3D11_CreateDevice(bool debugMode, bool preferLowPower, SDL_PropertiesID props) -{ - D3D11Renderer *renderer; - PFN_CREATE_DXGI_FACTORY1 CreateDxgiFactoryFunc; - PFN_D3D11_CREATE_DEVICE D3D11CreateDeviceFunc; - D3D_FEATURE_LEVEL levels[] = { D3D_FEATURE_LEVEL_11_1 }; - IDXGIFactory4 *factory4; - IDXGIFactory5 *factory5; - IDXGIFactory6 *factory6; - Uint32 flags; - DXGI_ADAPTER_DESC1 adapterDesc; - HRESULT res; - SDL_GPUDevice *result; - - // Allocate and zero out the renderer - renderer = (D3D11Renderer *)SDL_calloc(1, sizeof(D3D11Renderer)); - - // Load the DXGI library - renderer->dxgi_dll = SDL_LoadObject(DXGI_DLL); - if (renderer->dxgi_dll == NULL) { - SET_STRING_ERROR_AND_RETURN("Could not find " DXGI_DLL, NULL) - } - - // Load the CreateDXGIFactory1 function - CreateDxgiFactoryFunc = (PFN_CREATE_DXGI_FACTORY1)SDL_LoadFunction( - renderer->dxgi_dll, - CREATE_DXGI_FACTORY1_FUNC); - if (CreateDxgiFactoryFunc == NULL) { - SET_STRING_ERROR_AND_RETURN("Could not load function: " CREATE_DXGI_FACTORY1_FUNC, NULL) - } - - // Create the DXGI factory - res = CreateDxgiFactoryFunc( - &D3D_IID_IDXGIFactory1, - (void **)&renderer->factory); - CHECK_D3D11_ERROR_AND_RETURN("Could not create DXGIFactory", NULL); - - // Check for flip-model discard support (supported on Windows 10+) - res = IDXGIFactory1_QueryInterface( - renderer->factory, - &D3D_IID_IDXGIFactory4, - (void **)&factory4); - if (SUCCEEDED(res)) { - renderer->supportsFlipDiscard = 1; - IDXGIFactory4_Release(factory4); - } - - // Check for explicit tearing support - res = IDXGIFactory1_QueryInterface( - renderer->factory, - &D3D_IID_IDXGIFactory5, - (void **)&factory5); - if (SUCCEEDED(res)) { - res = IDXGIFactory5_CheckFeatureSupport( - factory5, - DXGI_FEATURE_PRESENT_ALLOW_TEARING, - &renderer->supportsTearing, - sizeof(renderer->supportsTearing)); - if (FAILED(res)) { - renderer->supportsTearing = FALSE; - } - IDXGIFactory5_Release(factory5); - } - - // Select the appropriate device for rendering - res = IDXGIAdapter1_QueryInterface( - renderer->factory, - &D3D_IID_IDXGIFactory6, - (void **)&factory6); - if (SUCCEEDED(res)) { - IDXGIFactory6_EnumAdapterByGpuPreference( - factory6, - 0, - preferLowPower ? DXGI_GPU_PREFERENCE_MINIMUM_POWER : DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE, - &D3D_IID_IDXGIAdapter1, - (void **)&renderer->adapter); - IDXGIFactory6_Release(factory6); - } else { - IDXGIFactory1_EnumAdapters1( - renderer->factory, - 0, - &renderer->adapter); - } - - // Get information about the selected adapter. Used for logging info. - IDXGIAdapter1_GetDesc1(renderer->adapter, &adapterDesc); - - // Initialize the DXGI debug layer, if applicable - if (debugMode) { - D3D11_INTERNAL_TryInitializeDXGIDebug(renderer); - } - - // Load the D3D library - renderer->d3d11_dll = SDL_LoadObject(D3D11_DLL); - if (renderer->d3d11_dll == NULL) { - SET_STRING_ERROR_AND_RETURN("Could not find " D3D11_DLL, NULL) - } - - // Load the CreateDevice function - D3D11CreateDeviceFunc = (PFN_D3D11_CREATE_DEVICE)SDL_LoadFunction( - renderer->d3d11_dll, - D3D11_CREATE_DEVICE_FUNC); - if (D3D11CreateDeviceFunc == NULL) { - SET_STRING_ERROR_AND_RETURN("Could not load function: " D3D11_CREATE_DEVICE_FUNC, NULL) - } - - // Set up device flags - flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; - if (debugMode) { - flags |= D3D11_CREATE_DEVICE_DEBUG; - } - - // Create the device - ID3D11Device *d3d11Device; -tryCreateDevice: - res = D3D11CreateDeviceFunc( - (IDXGIAdapter *)renderer->adapter, - D3D_DRIVER_TYPE_UNKNOWN, // Must be UNKNOWN if adapter is non-null according to spec - NULL, - flags, - levels, - SDL_arraysize(levels), - D3D11_SDK_VERSION, - &d3d11Device, - NULL, - &renderer->immediateContext); - if (FAILED(res) && debugMode) { - // If device creation failed, and we're in debug mode, remove the debug flag and try again. - SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "Creating device in debug mode failed with error " HRESULT_FMT ". Trying non-debug.", res); - flags &= ~D3D11_CREATE_DEVICE_DEBUG; - debugMode = 0; - goto tryCreateDevice; - } - - CHECK_D3D11_ERROR_AND_RETURN("Could not create D3D11 device", NULL); - - // The actual device we want is the ID3D11Device1 interface... - res = ID3D11Device_QueryInterface( - d3d11Device, - &D3D_IID_ID3D11Device1, - (void **)&renderer->device); - CHECK_D3D11_ERROR_AND_RETURN("Could not get ID3D11Device1 interface", NULL); - - // Release the old device interface, we don't need it anymore - ID3D11Device_Release(d3d11Device); - -#ifdef HAVE_IDXGIINFOQUEUE - // Set up the info queue - if (renderer->dxgiInfoQueue) { - DXGI_INFO_QUEUE_MESSAGE_SEVERITY sevList[] = { - DXGI_INFO_QUEUE_MESSAGE_SEVERITY_CORRUPTION, - DXGI_INFO_QUEUE_MESSAGE_SEVERITY_ERROR, - DXGI_INFO_QUEUE_MESSAGE_SEVERITY_WARNING, - // DXGI_INFO_QUEUE_MESSAGE_SEVERITY_INFO, // This can be a bit much, so toggle as needed for debugging. - DXGI_INFO_QUEUE_MESSAGE_SEVERITY_MESSAGE - }; - DXGI_INFO_QUEUE_FILTER filter = { 0 }; - filter.AllowList.NumSeverities = SDL_arraysize(sevList); - filter.AllowList.pSeverityList = sevList; - - IDXGIInfoQueue_PushStorageFilter( - renderer->dxgiInfoQueue, - D3D_IID_DXGI_DEBUG_ALL, - &filter); - } -#endif - - // Print driver info - SDL_LogInfo(SDL_LOG_CATEGORY_GPU, "SDL GPU Driver: D3D11"); - SDL_LogInfo(SDL_LOG_CATEGORY_GPU, "D3D11 Adapter: %S", adapterDesc.Description); - - // Create mutexes - renderer->contextLock = SDL_CreateMutex(); - renderer->acquireCommandBufferLock = SDL_CreateMutex(); - renderer->acquireUniformBufferLock = SDL_CreateMutex(); - renderer->fenceLock = SDL_CreateMutex(); - renderer->windowLock = SDL_CreateMutex(); - - // Initialize miscellaneous renderer members - renderer->debugMode = (flags & D3D11_CREATE_DEVICE_DEBUG); - - // Create command buffer pool - D3D11_INTERNAL_AllocateCommandBuffers(renderer, 2); - - // Create fence pool - renderer->availableFenceCapacity = 2; - renderer->availableFences = SDL_malloc( - sizeof(D3D11Fence *) * renderer->availableFenceCapacity); - - // Create uniform buffer pool - - renderer->uniformBufferPoolCapacity = 32; - renderer->uniformBufferPoolCount = 32; - renderer->uniformBufferPool = SDL_malloc( - renderer->uniformBufferPoolCapacity * sizeof(D3D11UniformBuffer *)); - - for (Uint32 i = 0; i < renderer->uniformBufferPoolCount; i += 1) { - renderer->uniformBufferPool[i] = D3D11_INTERNAL_CreateUniformBuffer( - renderer, - UNIFORM_BUFFER_SIZE); - } - - // Create deferred destroy arrays - renderer->transferBufferContainersToDestroyCapacity = 2; - renderer->transferBufferContainersToDestroyCount = 0; - renderer->transferBufferContainersToDestroy = SDL_malloc( - renderer->transferBufferContainersToDestroyCapacity * sizeof(D3D11TransferBufferContainer *)); - - renderer->bufferContainersToDestroyCapacity = 2; - renderer->bufferContainersToDestroyCount = 0; - renderer->bufferContainersToDestroy = SDL_malloc( - renderer->bufferContainersToDestroyCapacity * sizeof(D3D11BufferContainer *)); - - renderer->textureContainersToDestroyCapacity = 2; - renderer->textureContainersToDestroyCount = 0; - renderer->textureContainersToDestroy = SDL_malloc( - renderer->textureContainersToDestroyCapacity * sizeof(D3D11TextureContainer *)); - - // Create claimed window list - renderer->claimedWindowCapacity = 1; - renderer->claimedWindows = SDL_malloc( - sizeof(D3D11WindowData *) * renderer->claimedWindowCapacity); - - // Initialize null states - - SDL_zeroa(renderer->nullRTVs); - SDL_zeroa(renderer->nullSRVs); - SDL_zeroa(renderer->nullSamplers); - SDL_zeroa(renderer->nullUAVs); - - // Initialize built-in pipelines - D3D11_INTERNAL_InitBlitPipelines(renderer); - - // Create the SDL_GPU Device - result = (SDL_GPUDevice *)SDL_malloc(sizeof(SDL_GPUDevice)); - ASSIGN_DRIVER(D3D11) - result->driverData = (SDL_GPURenderer *)renderer; - - return result; -} - -SDL_GPUBootstrap D3D11Driver = { - "direct3d11", - SDL_GPU_SHADERFORMAT_DXBC, - D3D11_PrepareDriver, - D3D11_CreateDevice -}; - -#endif // SDL_GPU_D3D11 diff --git a/libs/SDL3/src/gpu/d3d11/compile_shaders.bat b/libs/SDL3/src/gpu/d3d11/compile_shaders.bat deleted file mode 100644 index 56eadcc2..00000000 --- a/libs/SDL3/src/gpu/d3d11/compile_shaders.bat +++ /dev/null @@ -1,8 +0,0 @@ -fxc /T vs_5_0 /E FullscreenVert /Fh D3D11_FullscreenVert.h ..\d3dcommon\D3D_Blit.hlsl -fxc /T ps_5_0 /E BlitFrom2D /Fh D3D11_BlitFrom2D.h ..\d3dcommon\D3D_Blit.hlsl -fxc /T ps_5_0 /E BlitFrom2DArray /Fh D3D11_BlitFrom2DArray.h ..\d3dcommon\D3D_Blit.hlsl -fxc /T ps_5_0 /E BlitFrom3D /Fh D3D11_BlitFrom3D.h ..\d3dcommon\D3D_Blit.hlsl -fxc /T ps_5_0 /E BlitFromCube /Fh D3D11_BlitFromCube.h ..\d3dcommon\D3D_Blit.hlsl -fxc /T ps_5_0 /E BlitFromCubeArray /Fh D3D11_BlitFromCubeArray.h ..\d3dcommon\D3D_Blit.hlsl -copy /b D3D11_FullscreenVert.h+D3D11_BlitFrom2D.h+D3D11_BlitFrom2DArray.h+D3D11_BlitFrom3D.h+D3D11_BlitFromCube.h+D3D11_BlitFromCubeArray.h D3D11_Blit.h -del D3D11_FullscreenVert.h D3D11_BlitFrom2D.h D3D11_BlitFrom2DArray.h D3D11_BlitFrom3D.h D3D11_BlitFromCube.h D3D11_BlitFromCubeArray.h diff --git a/libs/SDL3/src/gpu/d3dcommon/D3D_Blit.hlsl b/libs/SDL3/src/gpu/d3d12/D3D_Blit.hlsl similarity index 85% rename from libs/SDL3/src/gpu/d3dcommon/D3D_Blit.hlsl rename to libs/SDL3/src/gpu/d3d12/D3D_Blit.hlsl index c629c944..69748d4b 100644 --- a/libs/SDL3/src/gpu/d3dcommon/D3D_Blit.hlsl +++ b/libs/SDL3/src/gpu/d3d12/D3D_Blit.hlsl @@ -1,21 +1,15 @@ -#if D3D12 #define BlitRS \ "DescriptorTable ( Sampler(s0, space=2), visibility = SHADER_VISIBILITY_PIXEL ),"\ "DescriptorTable ( SRV(t0, space=2), visibility = SHADER_VISIBILITY_PIXEL ),"\ "CBV(b0, space=3, visibility = SHADER_VISIBILITY_PIXEL),"\ -#define REG(reg, space) register(reg, space) -#else -#define REG(reg, space) register(reg) -#endif - struct VertexToPixel { float2 tex : TEXCOORD0; float4 pos : SV_POSITION; }; -cbuffer SourceRegionBuffer : REG(b0, space3) +cbuffer SourceRegionBuffer : register(b0, space3) { float2 UVLeftTop; float2 UVDimensions; @@ -23,16 +17,14 @@ cbuffer SourceRegionBuffer : REG(b0, space3) float LayerOrDepth; }; -Texture2D SourceTexture2D : REG(t0, space2); -Texture2DArray SourceTexture2DArray : REG(t0, space2); -Texture3D SourceTexture3D : REG(t0, space2); -TextureCube SourceTextureCube : REG(t0, space2); -TextureCubeArray SourceTextureCubeArray : REG(t0, space2); -sampler SourceSampler : REG(s0, space2); +Texture2D SourceTexture2D : register(t0, space2); +Texture2DArray SourceTexture2DArray : register(t0, space2); +Texture3D SourceTexture3D : register(t0, space2); +TextureCube SourceTextureCube : register(t0, space2); +TextureCubeArray SourceTextureCubeArray : register(t0, space2); +sampler SourceSampler : register(s0, space2); -#if D3D12 [RootSignature(BlitRS)] -#endif VertexToPixel FullscreenVert(uint vI : SV_VERTEXID) { float2 inTex = float2((vI << 1) & 2, vI & 2); @@ -42,36 +34,28 @@ VertexToPixel FullscreenVert(uint vI : SV_VERTEXID) return Out; } -#if D3D12 [RootSignature(BlitRS)] -#endif float4 BlitFrom2D(VertexToPixel input) : SV_Target0 { float2 newCoord = UVLeftTop + UVDimensions * input.tex; return SourceTexture2D.SampleLevel(SourceSampler, newCoord, MipLevel); } -#if D3D12 [RootSignature(BlitRS)] -#endif float4 BlitFrom2DArray(VertexToPixel input) : SV_Target0 { float3 newCoord = float3(UVLeftTop + UVDimensions * input.tex, (uint)LayerOrDepth); return SourceTexture2DArray.SampleLevel(SourceSampler, newCoord, MipLevel); } -#if D3D12 [RootSignature(BlitRS)] -#endif float4 BlitFrom3D(VertexToPixel input) : SV_Target0 { float3 newCoord = float3(UVLeftTop + UVDimensions * input.tex, LayerOrDepth); return SourceTexture3D.SampleLevel(SourceSampler, newCoord, MipLevel); } -#if D3D12 [RootSignature(BlitRS)] -#endif float4 BlitFromCube(VertexToPixel input) : SV_Target0 { // Thanks, Wikipedia! https://en.wikipedia.org/wiki/Cube_mapping @@ -91,9 +75,7 @@ float4 BlitFromCube(VertexToPixel input) : SV_Target0 return SourceTextureCube.SampleLevel(SourceSampler, newCoord, MipLevel); } -#if D3D12 [RootSignature(BlitRS)] -#endif float4 BlitFromCubeArray(VertexToPixel input) : SV_Target0 { // Thanks, Wikipedia! https://en.wikipedia.org/wiki/Cube_mapping diff --git a/libs/SDL3/src/gpu/d3d12/SDL_gpu_d3d12.c b/libs/SDL3/src/gpu/d3d12/SDL_gpu_d3d12.c index 00c32931..dc02000e 100644 --- a/libs/SDL3/src/gpu/d3d12/SDL_gpu_d3d12.c +++ b/libs/SDL3/src/gpu/d3d12/SDL_gpu_d3d12.c @@ -55,26 +55,32 @@ // Macros -#define SET_ERROR(fmt, msg) \ - if (renderer->debug_mode) { \ - SDL_LogError(SDL_LOG_CATEGORY_GPU, fmt, msg); \ - } \ - SDL_SetError(fmt, msg); +#define SET_ERROR(fmt, msg) \ + do { \ + if (renderer->debug_mode) { \ + SDL_LogError(SDL_LOG_CATEGORY_GPU, fmt, msg); \ + } \ + SDL_SetError(fmt, msg); \ + } while (0) -#define SET_ERROR_AND_RETURN(fmt, msg, ret) \ - if (renderer->debug_mode) { \ - SDL_LogError(SDL_LOG_CATEGORY_GPU, fmt, msg); \ - } \ - SDL_SetError(fmt, msg); \ - return ret; \ +#define SET_ERROR_AND_RETURN(fmt, msg, ret) \ + do { \ + if (renderer->debug_mode) { \ + SDL_LogError(SDL_LOG_CATEGORY_GPU, fmt, msg); \ + } \ + SDL_SetError(fmt, msg); \ + return ret; \ + } while (0) #define SET_STRING_ERROR_AND_RETURN(msg, ret) SET_ERROR_AND_RETURN("%s", msg, ret) -#define CHECK_D3D12_ERROR_AND_RETURN(msg, ret) \ - if (FAILED(res)) { \ - D3D12_INTERNAL_SetError(renderer, msg, res); \ - return ret; \ - } +#define CHECK_D3D12_ERROR_AND_RETURN(msg, ret) \ + do { \ + if (FAILED(res)) { \ + D3D12_INTERNAL_SetError(renderer, msg, res); \ + return (ret); \ + } \ + } while (0) // Defines #if defined(_WIN32) @@ -105,14 +111,13 @@ #define WINDOW_PROPERTY_DATA "SDL_GPUD3D12WindowPropertyData" #define D3D_FEATURE_LEVEL_CHOICE D3D_FEATURE_LEVEL_11_1 #define D3D_FEATURE_LEVEL_CHOICE_STR "11_1" -// FIXME: just use sysgpu.h defines #define MAX_ROOT_SIGNATURE_PARAMETERS 64 -#define VIEW_GPU_DESCRIPTOR_COUNT 65536 -#define SAMPLER_GPU_DESCRIPTOR_COUNT 2048 -#define VIEW_SAMPLER_STAGING_DESCRIPTOR_COUNT 1000000 -#define TARGET_STAGING_DESCRIPTOR_COUNT 1000000 #define D3D12_FENCE_UNSIGNALED_VALUE 0 #define D3D12_FENCE_SIGNAL_VALUE 1 +// TODO: do these need to be tuned? +#define VIEW_GPU_DESCRIPTOR_COUNT 65536 +#define SAMPLER_GPU_DESCRIPTOR_COUNT 2048 +#define STAGING_HEAP_DESCRIPTOR_COUNT 1024 #define SDL_GPU_SHADERSTAGE_COMPUTE (SDL_GPUShaderStage)2 @@ -146,6 +151,8 @@ static const IID D3D_IID_IDXGIFactory6 = { 0xc1b6694f, 0xff09, 0x44a9, { 0xb0, 0 static const IID D3D_IID_IDXGIAdapter1 = { 0x29038f61, 0x3839, 0x4626, { 0x91, 0xfd, 0x08, 0x68, 0x79, 0x01, 0x1a, 0x05 } }; #if (defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)) static const IID D3D_IID_IDXGIDevice1 = { 0x77db970f, 0x6276, 0x48ba, { 0xba, 0x28, 0x07, 0x01, 0x43, 0xb4, 0x39, 0x2c } }; +#else +static const IID D3D_IID_IDXGIDevice = { 0x54ec77fa, 0x1377, 0x44e6, { 0x8c, 0x32, 0x88, 0xfd, 0x5f, 0x44, 0xc8, 0x4c } }; #endif static const IID D3D_IID_IDXGISwapChain3 = { 0x94d99bdb, 0xf1f8, 0x4ab0, { 0xb2, 0x36, 0x7d, 0xa0, 0x17, 0x0e, 0xda, 0xb1 } }; #ifdef HAVE_IDXGIINFOQUEUE @@ -168,6 +175,7 @@ static const IID D3D_IID_ID3D12CommandSignature = { 0xc36a797c, 0xec80, 0x4f0a, static const IID D3D_IID_ID3D12PipelineState = { 0x765a30f3, 0xf624, 0x4c6f, { 0xa8, 0x28, 0xac, 0xe9, 0x48, 0x62, 0x24, 0x45 } }; static const IID D3D_IID_ID3D12Debug = { 0x344488b7, 0x6846, 0x474b, { 0xb9, 0x89, 0xf0, 0x27, 0x44, 0x82, 0x45, 0xe0 } }; static const IID D3D_IID_ID3D12InfoQueue = { 0x0742a90b, 0xc387, 0x483f, { 0xb9, 0x46, 0x30, 0xa7, 0xe4, 0xe6, 0x14, 0x58 } }; +static const IID D3D_IID_ID3D12InfoQueue1 = { 0x2852dd88, 0xb484, 0x4c0c, { 0xb6, 0xb1, 0x67, 0x16, 0x85, 0x00, 0xe6, 0x00 } }; // Enums @@ -560,6 +568,14 @@ static D3D12_PRIMITIVE_TOPOLOGY SDLToD3D12_PrimitiveType[] = { D3D_PRIMITIVE_TOPOLOGY_POINTLIST // POINTLIST }; +static D3D12_PRIMITIVE_TOPOLOGY_TYPE SDLToD3D12_PrimitiveTopologyType[] = { + D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE, // TRIANGLELIST + D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE, // TRIANGLESTRIP + D3D12_PRIMITIVE_TOPOLOGY_TYPE_LINE, // LINELIST + D3D12_PRIMITIVE_TOPOLOGY_TYPE_LINE, // LINESTRIP + D3D12_PRIMITIVE_TOPOLOGY_TYPE_POINT // POINTLIST +}; + static D3D12_TEXTURE_ADDRESS_MODE SDLToD3D12_SamplerAddressMode[] = { D3D12_TEXTURE_ADDRESS_MODE_WRAP, // REPEAT D3D12_TEXTURE_ADDRESS_MODE_MIRROR, // MIRRORED_REPEAT @@ -598,6 +614,7 @@ typedef struct D3D12Buffer D3D12Buffer; typedef struct D3D12BufferContainer D3D12BufferContainer; typedef struct D3D12UniformBuffer D3D12UniformBuffer; typedef struct D3D12DescriptorHeap D3D12DescriptorHeap; +typedef struct D3D12StagingDescriptor D3D12StagingDescriptor; typedef struct D3D12TextureDownload D3D12TextureDownload; typedef struct D3D12Fence @@ -612,31 +629,43 @@ struct D3D12DescriptorHeap ID3D12DescriptorHeap *handle; D3D12_DESCRIPTOR_HEAP_TYPE heapType; D3D12_CPU_DESCRIPTOR_HANDLE descriptorHeapCPUStart; - D3D12_GPU_DESCRIPTOR_HANDLE descriptorHeapGPUStart; // only exists if staging is true + D3D12_GPU_DESCRIPTOR_HANDLE descriptorHeapGPUStart; // only used by GPU heaps Uint32 maxDescriptors; Uint32 descriptorSize; bool staging; - Uint32 currentDescriptorIndex; - - Uint32 *inactiveDescriptorIndices; // only exists if staging is true - Uint32 inactiveDescriptorCount; + Uint32 currentDescriptorIndex; // only used by GPU heaps }; -typedef struct D3D12DescriptorHeapPool +typedef struct D3D12GPUDescriptorHeapPool { Uint32 capacity; Uint32 count; D3D12DescriptorHeap **heaps; SDL_Mutex *lock; -} D3D12DescriptorHeapPool; +} D3D12GPUDescriptorHeapPool; -typedef struct D3D12CPUDescriptor +// The only thing we care about with staging descriptors is being able to grab a free descriptor. +typedef struct D3D12StagingDescriptorPool { + Uint32 heapCount; + D3D12DescriptorHeap **heaps; + + // Descriptor handles are owned by resources, so these can be thought of as descriptions of a free index within a heap. + Uint32 freeDescriptorCapacity; + Uint32 freeDescriptorCount; + D3D12StagingDescriptor *freeDescriptors; + + SDL_Mutex *lock; +} D3D12StagingDescriptorPool; + +struct D3D12StagingDescriptor +{ + D3D12StagingDescriptorPool *pool; D3D12DescriptorHeap *heap; D3D12_CPU_DESCRIPTOR_HANDLE cpuHandle; Uint32 cpuHandleIndex; -} D3D12CPUDescriptor; +}; typedef struct D3D12TextureContainer { @@ -664,10 +693,10 @@ typedef struct D3D12TextureSubresource Uint32 index; // One per depth slice - D3D12CPUDescriptor *rtvHandles; // NULL if not a color target + D3D12StagingDescriptor *rtvHandles; // NULL if not a color target - D3D12CPUDescriptor uavHandle; // NULL if not a compute storage write texture - D3D12CPUDescriptor dsvHandle; // NULL if not a depth stencil target + D3D12StagingDescriptor uavHandle; // NULL if not a compute storage write texture + D3D12StagingDescriptor dsvHandle; // NULL if not a depth stencil target } D3D12TextureSubresource; struct D3D12Texture @@ -679,7 +708,7 @@ struct D3D12Texture Uint32 subresourceCount; /* layerCount * num_levels */ ID3D12Resource *resource; - D3D12CPUDescriptor srvHandle; + D3D12StagingDescriptor srvHandle; SDL_AtomicInt referenceCount; }; @@ -687,7 +716,7 @@ struct D3D12Texture typedef struct D3D12Sampler { SDL_GPUSamplerCreateInfo createInfo; - D3D12CPUDescriptor handle; + D3D12StagingDescriptor handle; SDL_AtomicInt referenceCount; } D3D12Sampler; @@ -705,6 +734,8 @@ typedef struct D3D12WindowData Uint32 frameCounter; D3D12TextureContainer textureContainers[MAX_FRAMES_IN_FLIGHT]; + Uint32 swapchainTextureCount; + SDL_GPUFence *inFlightFences[MAX_FRAMES_IN_FLIGHT]; Uint32 width; Uint32 height; @@ -733,7 +764,7 @@ struct D3D12Renderer SDL_SharedObject *dxgidebug_dll; #endif ID3D12Debug *d3d12Debug; - bool supportsTearing; + BOOL supportsTearing; SDL_SharedObject *d3d12_dll; ID3D12Device *device; PFN_D3D12_SERIALIZE_ROOT_SIGNATURE D3D12SerializeRootSignature_func; @@ -747,6 +778,7 @@ struct D3D12Renderer // FIXME: these might not be necessary since we're not using custom heaps bool UMA; bool UMACacheCoherent; + Uint32 allowedFramesInFlight; // Indirect command signatures ID3D12CommandSignature *indirectDrawCommandSignature; @@ -790,8 +822,8 @@ struct D3D12Renderer Uint32 availableFenceCount; Uint32 availableFenceCapacity; - D3D12DescriptorHeap *stagingDescriptorHeaps[D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES]; - D3D12DescriptorHeapPool descriptorHeapPools[2]; + D3D12StagingDescriptorPool *stagingDescriptorPools[D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES]; + D3D12GPUDescriptorHeapPool gpuDescriptorHeapPools[2]; // Deferred resource releasing @@ -816,7 +848,6 @@ struct D3D12Renderer Uint32 computePipelinesToDestroyCapacity; // Locks - SDL_Mutex *stagingDescriptorHeapLock; SDL_Mutex *acquireCommandBufferLock; SDL_Mutex *acquireUniformBufferLock; SDL_Mutex *submitLock; @@ -1024,9 +1055,9 @@ struct D3D12Buffer Uint32 containerIndex; ID3D12Resource *handle; - D3D12CPUDescriptor uavDescriptor; - D3D12CPUDescriptor srvDescriptor; - D3D12CPUDescriptor cbvDescriptor; + D3D12StagingDescriptor uavDescriptor; + D3D12StagingDescriptor srvDescriptor; + D3D12StagingDescriptor cbvDescriptor; D3D12_GPU_VIRTUAL_ADDRESS virtualAddress; Uint8 *mapPointer; // NULL except for upload buffers and fast uniform buffers SDL_AtomicInt referenceCount; @@ -1163,23 +1194,18 @@ static void D3D12_INTERNAL_SetResourceName( // Release / Cleanup -// TODO: call this when releasing resources -static void D3D12_INTERNAL_ReleaseCpuDescriptorHandle( +static void D3D12_INTERNAL_ReleaseStagingDescriptorHandle( D3D12Renderer *renderer, - D3D12CPUDescriptor *cpuDescriptor) + D3D12StagingDescriptor *cpuDescriptor) { - D3D12DescriptorHeap *heap = cpuDescriptor->heap; + D3D12StagingDescriptorPool *pool = cpuDescriptor->pool; - if (heap != NULL) { - SDL_LockMutex(renderer->stagingDescriptorHeapLock); - heap->inactiveDescriptorIndices[heap->inactiveDescriptorCount] = cpuDescriptor->cpuHandleIndex; - heap->inactiveDescriptorCount += 1; - SDL_UnlockMutex(renderer->stagingDescriptorHeapLock); + if (pool != NULL) { + SDL_LockMutex(pool->lock); + SDL_memcpy(&pool->freeDescriptors[pool->freeDescriptorCount], cpuDescriptor, sizeof(D3D12StagingDescriptor)); + pool->freeDescriptorCount += 1; + SDL_UnlockMutex(pool->lock); } - - cpuDescriptor->heap = NULL; - cpuDescriptor->cpuHandle.ptr = 0; - cpuDescriptor->cpuHandleIndex = SDL_MAX_UINT32; } static void D3D12_INTERNAL_DestroyBuffer( @@ -1196,13 +1222,13 @@ static void D3D12_INTERNAL_DestroyBuffer( 0, NULL); } - D3D12_INTERNAL_ReleaseCpuDescriptorHandle( + D3D12_INTERNAL_ReleaseStagingDescriptorHandle( renderer, &buffer->srvDescriptor); - D3D12_INTERNAL_ReleaseCpuDescriptorHandle( + D3D12_INTERNAL_ReleaseStagingDescriptorHandle( renderer, &buffer->uavDescriptor); - D3D12_INTERNAL_ReleaseCpuDescriptorHandle( + D3D12_INTERNAL_ReleaseStagingDescriptorHandle( renderer, &buffer->cbvDescriptor); @@ -1223,7 +1249,7 @@ static void D3D12_INTERNAL_ReleaseBuffer( D3D12Buffer *, renderer->buffersToDestroyCount + 1, renderer->buffersToDestroyCapacity, - renderer->buffersToDestroyCapacity * 2) + renderer->buffersToDestroyCapacity * 2); renderer->buffersToDestroy[renderer->buffersToDestroyCount] = buffer; renderer->buffersToDestroyCount += 1; @@ -1264,24 +1290,24 @@ static void D3D12_INTERNAL_DestroyTexture( D3D12TextureSubresource *subresource = &texture->subresources[i]; if (subresource->rtvHandles) { for (Uint32 depthIndex = 0; depthIndex < subresource->depth; depthIndex += 1) { - D3D12_INTERNAL_ReleaseCpuDescriptorHandle( + D3D12_INTERNAL_ReleaseStagingDescriptorHandle( renderer, &subresource->rtvHandles[depthIndex]); } SDL_free(subresource->rtvHandles); } - D3D12_INTERNAL_ReleaseCpuDescriptorHandle( + D3D12_INTERNAL_ReleaseStagingDescriptorHandle( renderer, &subresource->uavHandle); - D3D12_INTERNAL_ReleaseCpuDescriptorHandle( + D3D12_INTERNAL_ReleaseStagingDescriptorHandle( renderer, &subresource->dsvHandle); } SDL_free(texture->subresources); - D3D12_INTERNAL_ReleaseCpuDescriptorHandle( + D3D12_INTERNAL_ReleaseStagingDescriptorHandle( renderer, &texture->srvHandle); @@ -1303,7 +1329,7 @@ static void D3D12_INTERNAL_ReleaseTexture( D3D12Texture *, renderer->texturesToDestroyCount + 1, renderer->texturesToDestroyCapacity, - renderer->texturesToDestroyCapacity * 2) + renderer->texturesToDestroyCapacity * 2); renderer->texturesToDestroy[renderer->texturesToDestroyCount] = texture; renderer->texturesToDestroyCount += 1; @@ -1337,7 +1363,7 @@ static void D3D12_INTERNAL_DestroySampler( D3D12Renderer *renderer, D3D12Sampler *sampler) { - D3D12_INTERNAL_ReleaseCpuDescriptorHandle( + D3D12_INTERNAL_ReleaseStagingDescriptorHandle( renderer, &sampler->handle); @@ -1433,13 +1459,25 @@ static void D3D12_INTERNAL_DestroyDescriptorHeap(D3D12DescriptorHeap *descriptor if (!descriptorHeap) { return; } - SDL_free(descriptorHeap->inactiveDescriptorIndices); if (descriptorHeap->handle) { ID3D12DescriptorHeap_Release(descriptorHeap->handle); } SDL_free(descriptorHeap); } +static void D3D12_INTERNAL_DestroyStagingDescriptorPool( + D3D12StagingDescriptorPool *pool) +{ + for (Uint32 i = 0; i < pool->heapCount; i += 1) { + D3D12_INTERNAL_DestroyDescriptorHeap(pool->heaps[i]); + } + SDL_free(pool->heaps); + SDL_free(pool->freeDescriptors); + SDL_DestroyMutex(pool->lock); + + SDL_free(pool); +} + static void D3D12_INTERNAL_DestroyCommandBuffer(D3D12CommandBuffer *commandBuffer) { if (!commandBuffer) { @@ -1488,25 +1526,25 @@ static void D3D12_INTERNAL_DestroyRenderer(D3D12Renderer *renderer) // Clean up descriptor heaps for (Uint32 i = 0; i < D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES; i += 1) { - if (renderer->stagingDescriptorHeaps[i]) { - D3D12_INTERNAL_DestroyDescriptorHeap(renderer->stagingDescriptorHeaps[i]); - renderer->stagingDescriptorHeaps[i] = NULL; + if (renderer->stagingDescriptorPools[i]) { + D3D12_INTERNAL_DestroyStagingDescriptorPool(renderer->stagingDescriptorPools[i]); + renderer->stagingDescriptorPools[i] = NULL; } } for (Uint32 i = 0; i < 2; i += 1) { - if (renderer->descriptorHeapPools[i].heaps) { - for (Uint32 j = 0; j < renderer->descriptorHeapPools[i].count; j += 1) { - if (renderer->descriptorHeapPools[i].heaps[j]) { - D3D12_INTERNAL_DestroyDescriptorHeap(renderer->descriptorHeapPools[i].heaps[j]); - renderer->descriptorHeapPools[i].heaps[j] = NULL; + if (renderer->gpuDescriptorHeapPools[i].heaps) { + for (Uint32 j = 0; j < renderer->gpuDescriptorHeapPools[i].count; j += 1) { + if (renderer->gpuDescriptorHeapPools[i].heaps[j]) { + D3D12_INTERNAL_DestroyDescriptorHeap(renderer->gpuDescriptorHeapPools[i].heaps[j]); + renderer->gpuDescriptorHeapPools[i].heaps[j] = NULL; } } - SDL_free(renderer->descriptorHeapPools[i].heaps); + SDL_free(renderer->gpuDescriptorHeapPools[i].heaps); } - if (renderer->descriptorHeapPools[i].lock) { - SDL_DestroyMutex(renderer->descriptorHeapPools[i].lock); - renderer->descriptorHeapPools[i].lock = NULL; + if (renderer->gpuDescriptorHeapPools[i].lock) { + SDL_DestroyMutex(renderer->gpuDescriptorHeapPools[i].lock); + renderer->gpuDescriptorHeapPools[i].lock = NULL; } } @@ -1597,7 +1635,6 @@ static void D3D12_INTERNAL_DestroyRenderer(D3D12Renderer *renderer) SDL_iconv_close(renderer->iconv); } - SDL_DestroyMutex(renderer->stagingDescriptorHeapLock); SDL_DestroyMutex(renderer->acquireCommandBufferLock); SDL_DestroyMutex(renderer->acquireUniformBufferLock); SDL_DestroyMutex(renderer->submitLock); @@ -1933,16 +1970,6 @@ static D3D12DescriptorHeap *D3D12_INTERNAL_CreateDescriptorHeap( } heap->currentDescriptorIndex = 0; - heap->inactiveDescriptorCount = 0; - heap->inactiveDescriptorIndices = NULL; - - if (staging) { - heap->inactiveDescriptorIndices = (Uint32 *)SDL_calloc(descriptorCount, sizeof(Uint32)); - if (!heap->inactiveDescriptorIndices) { - D3D12_INTERNAL_DestroyDescriptorHeap(heap); - return NULL; - } - } heapDesc.NumDescriptors = descriptorCount; heapDesc.Type = type; @@ -1974,13 +2001,82 @@ static D3D12DescriptorHeap *D3D12_INTERNAL_CreateDescriptorHeap( return heap; } -static D3D12DescriptorHeap *D3D12_INTERNAL_AcquireDescriptorHeapFromPool( +static D3D12StagingDescriptorPool *D3D12_INTERNAL_CreateStagingDescriptorPool( + D3D12Renderer *renderer, + D3D12_DESCRIPTOR_HEAP_TYPE heapType +) { + D3D12DescriptorHeap *heap = D3D12_INTERNAL_CreateDescriptorHeap( + renderer, + heapType, + STAGING_HEAP_DESCRIPTOR_COUNT, + true); + + if (!heap) { + return NULL; + } + + D3D12StagingDescriptorPool *pool = (D3D12StagingDescriptorPool*) SDL_calloc(1, sizeof(D3D12StagingDescriptorPool)); + + pool->heapCount = 1; + pool->heaps = (D3D12DescriptorHeap**) SDL_malloc(sizeof(D3D12DescriptorHeap*)); + pool->heaps[0] = heap; + + pool->freeDescriptorCapacity = STAGING_HEAP_DESCRIPTOR_COUNT; + pool->freeDescriptorCount = STAGING_HEAP_DESCRIPTOR_COUNT; + pool->freeDescriptors = (D3D12StagingDescriptor*) SDL_malloc(STAGING_HEAP_DESCRIPTOR_COUNT * sizeof(D3D12StagingDescriptor)); + + for (Uint32 i = 0; i < STAGING_HEAP_DESCRIPTOR_COUNT; i += 1) { + pool->freeDescriptors[i].pool = pool; + pool->freeDescriptors[i].heap = heap; + pool->freeDescriptors[i].cpuHandleIndex = i; + pool->freeDescriptors[i].cpuHandle.ptr = heap->descriptorHeapCPUStart.ptr + (i * heap->descriptorSize); + } + + pool->lock = SDL_CreateMutex(); + + return pool; +} + +/* If the pool is empty, we need to refill it! */ +static bool D3D12_INTERNAL_ExpandStagingDescriptorPool( + D3D12Renderer *renderer, + D3D12StagingDescriptorPool *pool +) { + D3D12DescriptorHeap *heap = D3D12_INTERNAL_CreateDescriptorHeap( + renderer, + pool->heaps[0]->heapType, + STAGING_HEAP_DESCRIPTOR_COUNT, + true); + + if (!heap) { + return false; + } + + pool->heapCount += 1; + pool->heaps = (D3D12DescriptorHeap**) SDL_realloc(pool->heaps, pool->heapCount * sizeof(D3D12DescriptorHeap*)); + pool->heaps[pool->heapCount - 1] = heap; + + pool->freeDescriptorCapacity += STAGING_HEAP_DESCRIPTOR_COUNT; + pool->freeDescriptorCount += STAGING_HEAP_DESCRIPTOR_COUNT; + pool->freeDescriptors = (D3D12StagingDescriptor*) SDL_realloc(pool->freeDescriptors, pool->freeDescriptorCapacity * sizeof(D3D12StagingDescriptor)); + + for (Uint32 i = 0; i < STAGING_HEAP_DESCRIPTOR_COUNT; i += 1) { + pool->freeDescriptors[i].pool = pool; + pool->freeDescriptors[i].heap = heap; + pool->freeDescriptors[i].cpuHandleIndex = i; + pool->freeDescriptors[i].cpuHandle.ptr = heap->descriptorHeapCPUStart.ptr + (i * heap->descriptorSize); + } + + return true; +} + +static D3D12DescriptorHeap *D3D12_INTERNAL_AcquireGPUDescriptorHeapFromPool( D3D12CommandBuffer *commandBuffer, D3D12_DESCRIPTOR_HEAP_TYPE descriptorHeapType) { D3D12DescriptorHeap *result; D3D12Renderer *renderer = commandBuffer->renderer; - D3D12DescriptorHeapPool *pool = &renderer->descriptorHeapPools[descriptorHeapType]; + D3D12GPUDescriptorHeapPool *pool = &renderer->gpuDescriptorHeapPools[descriptorHeapType]; SDL_LockMutex(pool->lock); if (pool->count > 0) { @@ -1998,11 +2094,15 @@ static D3D12DescriptorHeap *D3D12_INTERNAL_AcquireDescriptorHeapFromPool( return result; } -static void D3D12_INTERNAL_ReturnDescriptorHeapToPool( +static void D3D12_INTERNAL_ReturnGPUDescriptorHeapToPool( D3D12Renderer *renderer, D3D12DescriptorHeap *heap) { - D3D12DescriptorHeapPool *pool = &renderer->descriptorHeapPools[heap->heapType]; + if (heap == NULL) { + return; + } + + D3D12GPUDescriptorHeapPool *pool = &renderer->gpuDescriptorHeapPools[heap->heapType]; heap->currentDescriptorIndex = 0; @@ -2537,7 +2637,7 @@ static SDL_GPUComputePipeline *D3D12_CreateComputePipeline( if (rootSignature == NULL) { SDL_free(bytecode); - SET_STRING_ERROR_AND_RETURN("Could not create root signature!", NULL) + SET_STRING_ERROR_AND_RETURN("Could not create root signature!", NULL); } D3D12_COMPUTE_PIPELINE_STATE_DESC pipelineDesc; @@ -2723,36 +2823,31 @@ static bool D3D12_INTERNAL_ConvertVertexInputState(SDL_GPUVertexInputState verte return true; } -static void D3D12_INTERNAL_AssignCpuDescriptorHandle( +static bool D3D12_INTERNAL_AssignStagingDescriptorHandle( D3D12Renderer *renderer, D3D12_DESCRIPTOR_HEAP_TYPE heapType, - D3D12CPUDescriptor *cpuDescriptor) + D3D12StagingDescriptor *cpuDescriptor) { - D3D12DescriptorHeap *heap = renderer->stagingDescriptorHeaps[heapType]; - Uint32 descriptorIndex; + D3D12StagingDescriptor *descriptor; + D3D12StagingDescriptorPool *pool = renderer->stagingDescriptorPools[heapType]; - cpuDescriptor->heap = heap; + SDL_LockMutex(pool->lock); - SDL_LockMutex(renderer->stagingDescriptorHeapLock); - - if (heap->inactiveDescriptorCount > 0) { - descriptorIndex = heap->inactiveDescriptorIndices[heap->inactiveDescriptorCount - 1]; - heap->inactiveDescriptorCount -= 1; - } else if (heap->currentDescriptorIndex < heap->maxDescriptors) { - descriptorIndex = heap->currentDescriptorIndex; - heap->currentDescriptorIndex += 1; - } else { - cpuDescriptor->cpuHandleIndex = SDL_MAX_UINT32; - cpuDescriptor->cpuHandle.ptr = 0; - SDL_LogError(SDL_LOG_CATEGORY_GPU, "Out of CPU descriptor handles, many bad things are going to happen!"); - SDL_UnlockMutex(renderer->stagingDescriptorHeapLock); - return; + if (pool->freeDescriptorCount == 0) { + if (!D3D12_INTERNAL_ExpandStagingDescriptorPool(renderer, pool)) + { + SDL_UnlockMutex(pool->lock); + return false; + } } - SDL_UnlockMutex(renderer->stagingDescriptorHeapLock); + descriptor = &pool->freeDescriptors[pool->freeDescriptorCount - 1]; + SDL_memcpy(cpuDescriptor, descriptor, sizeof(D3D12StagingDescriptor)); + pool->freeDescriptorCount -= 1; - cpuDescriptor->cpuHandleIndex = descriptorIndex; - cpuDescriptor->cpuHandle.ptr = heap->descriptorHeapCPUStart.ptr + (descriptorIndex * heap->descriptorSize); + SDL_UnlockMutex(pool->lock); + + return true; } static SDL_GPUGraphicsPipeline *D3D12_CreateGraphicsPipeline( @@ -2777,7 +2872,7 @@ static SDL_GPUGraphicsPipeline *D3D12_CreateGraphicsPipeline( D3D12_INTERNAL_ConvertVertexInputState(createinfo->vertex_input_state, inputElementDescs, renderer->semantic); } - psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; + psoDesc.PrimitiveTopologyType = SDLToD3D12_PrimitiveTopologyType[createinfo->primitive_type]; if (!D3D12_INTERNAL_ConvertRasterizerState(createinfo->rasterizer_state, &psoDesc.RasterizerState)) { return NULL; @@ -2893,7 +2988,7 @@ static SDL_GPUSampler *D3D12_CreateSampler( samplerDesc.BorderColor[2] = 0; samplerDesc.BorderColor[3] = 0; - D3D12_INTERNAL_AssignCpuDescriptorHandle( + D3D12_INTERNAL_AssignStagingDescriptorHandle( renderer, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, &sampler->handle); @@ -3056,7 +3151,7 @@ static D3D12Texture *D3D12_INTERNAL_CreateTexture( (createinfo->usage & SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_READ)) { D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc; - D3D12_INTERNAL_AssignCpuDescriptorHandle( + D3D12_INTERNAL_AssignStagingDescriptorHandle( renderer, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, &texture->srvHandle); @@ -3132,12 +3227,12 @@ static D3D12Texture *D3D12_INTERNAL_CreateTexture( // Create RTV if needed if (createinfo->usage & SDL_GPU_TEXTUREUSAGE_COLOR_TARGET) { - texture->subresources[subresourceIndex].rtvHandles = (D3D12CPUDescriptor *)SDL_calloc(depth, sizeof(D3D12CPUDescriptor)); + texture->subresources[subresourceIndex].rtvHandles = (D3D12StagingDescriptor *)SDL_calloc(depth, sizeof(D3D12StagingDescriptor)); for (Uint32 depthIndex = 0; depthIndex < depth; depthIndex += 1) { D3D12_RENDER_TARGET_VIEW_DESC rtvDesc; - D3D12_INTERNAL_AssignCpuDescriptorHandle( + D3D12_INTERNAL_AssignStagingDescriptorHandle( renderer, D3D12_DESCRIPTOR_HEAP_TYPE_RTV, &texture->subresources[subresourceIndex].rtvHandles[depthIndex]); @@ -3175,7 +3270,7 @@ static D3D12Texture *D3D12_INTERNAL_CreateTexture( if (createinfo->usage & SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET) { D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc; - D3D12_INTERNAL_AssignCpuDescriptorHandle( + D3D12_INTERNAL_AssignStagingDescriptorHandle( renderer, D3D12_DESCRIPTOR_HEAP_TYPE_DSV, &texture->subresources[subresourceIndex].dsvHandle); @@ -3201,7 +3296,7 @@ static D3D12Texture *D3D12_INTERNAL_CreateTexture( if (needsUAV) { D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc; - D3D12_INTERNAL_AssignCpuDescriptorHandle( + D3D12_INTERNAL_AssignStagingDescriptorHandle( renderer, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, &texture->subresources[subresourceIndex].uavHandle); @@ -3347,7 +3442,7 @@ static D3D12Buffer *D3D12_INTERNAL_CreateBuffer( } heapFlags = D3D12_HEAP_FLAG_NONE; } else { - SET_STRING_ERROR_AND_RETURN("Unrecognized buffer type!", NULL) + SET_STRING_ERROR_AND_RETURN("Unrecognized buffer type!", NULL); } desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; @@ -3385,7 +3480,7 @@ static D3D12Buffer *D3D12_INTERNAL_CreateBuffer( buffer->cbvDescriptor.heap = NULL; if (usageFlags & SDL_GPU_BUFFERUSAGE_COMPUTE_STORAGE_WRITE) { - D3D12_INTERNAL_AssignCpuDescriptorHandle( + D3D12_INTERNAL_AssignStagingDescriptorHandle( renderer, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, &buffer->uavDescriptor); @@ -3410,7 +3505,7 @@ static D3D12Buffer *D3D12_INTERNAL_CreateBuffer( if ( (usageFlags & SDL_GPU_BUFFERUSAGE_GRAPHICS_STORAGE_READ) || (usageFlags & SDL_GPU_BUFFERUSAGE_COMPUTE_STORAGE_READ)) { - D3D12_INTERNAL_AssignCpuDescriptorHandle( + D3D12_INTERNAL_AssignStagingDescriptorHandle( renderer, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, &buffer->srvDescriptor); @@ -3433,7 +3528,7 @@ static D3D12Buffer *D3D12_INTERNAL_CreateBuffer( // FIXME: we may not need a CBV since we use root descriptors if (type == D3D12_BUFFER_TYPE_UNIFORM) { - D3D12_INTERNAL_AssignCpuDescriptorHandle( + D3D12_INTERNAL_AssignStagingDescriptorHandle( renderer, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, &buffer->cbvDescriptor); @@ -3618,7 +3713,8 @@ static bool D3D12_INTERNAL_StrToWStr( Uint32 *outSize) { size_t inlen, result; - size_t outlen = wstrSize; + size_t outBytesLeft = wstrSize; + *outSize = 0; if (renderer->iconv == NULL) { renderer->iconv = SDL_iconv_open("WCHAR_T", "UTF-8"); @@ -3632,9 +3728,8 @@ static bool D3D12_INTERNAL_StrToWStr( &str, &inlen, (char **)&wstr, - &outlen); + &outBytesLeft); - *outSize = (Uint32)outlen; // Check... switch (result) { @@ -3648,6 +3743,7 @@ static bool D3D12_INTERNAL_StrToWStr( break; } + *outSize = (Uint32)(wstrSize - outBytesLeft); return true; } @@ -3734,7 +3830,7 @@ static void D3D12_ReleaseSampler( D3D12Sampler *, renderer->samplersToDestroyCount + 1, renderer->samplersToDestroyCapacity, - renderer->samplersToDestroyCapacity * 2) + renderer->samplersToDestroyCapacity * 2); renderer->samplersToDestroy[renderer->samplersToDestroyCount] = d3d12Sampler; renderer->samplersToDestroyCount += 1; @@ -3794,7 +3890,7 @@ static void D3D12_ReleaseComputePipeline( D3D12ComputePipeline *, renderer->computePipelinesToDestroyCount + 1, renderer->computePipelinesToDestroyCapacity, - renderer->computePipelinesToDestroyCapacity * 2) + renderer->computePipelinesToDestroyCapacity * 2); renderer->computePipelinesToDestroy[renderer->computePipelinesToDestroyCount] = d3d12ComputePipeline; renderer->computePipelinesToDestroyCount += 1; @@ -3816,7 +3912,7 @@ static void D3D12_ReleaseGraphicsPipeline( D3D12GraphicsPipeline *, renderer->graphicsPipelinesToDestroyCount + 1, renderer->graphicsPipelinesToDestroyCapacity, - renderer->graphicsPipelinesToDestroyCapacity * 2) + renderer->graphicsPipelinesToDestroyCapacity * 2); renderer->graphicsPipelinesToDestroy[renderer->graphicsPipelinesToDestroyCount] = d3d12GraphicsPipeline; renderer->graphicsPipelinesToDestroyCount += 1; @@ -4652,6 +4748,27 @@ static void D3D12_PushFragmentUniformData( length); } +static void D3D12_INTERNAL_SetGPUDescriptorHeaps(D3D12CommandBuffer *commandBuffer) +{ + ID3D12DescriptorHeap *heaps[2]; + D3D12DescriptorHeap *viewHeap; + D3D12DescriptorHeap *samplerHeap; + + viewHeap = D3D12_INTERNAL_AcquireGPUDescriptorHeapFromPool(commandBuffer, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); + samplerHeap = D3D12_INTERNAL_AcquireGPUDescriptorHeapFromPool(commandBuffer, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER); + + commandBuffer->gpuDescriptorHeaps[0] = viewHeap; + commandBuffer->gpuDescriptorHeaps[1] = samplerHeap; + + heaps[0] = viewHeap->handle; + heaps[1] = samplerHeap->handle; + + ID3D12GraphicsCommandList_SetDescriptorHeaps( + commandBuffer->graphicsCommandList, + 2, + heaps); +} + static void D3D12_INTERNAL_WriteGPUDescriptors( D3D12CommandBuffer *commandBuffer, D3D12_DESCRIPTOR_HEAP_TYPE heapType, @@ -4659,9 +4776,16 @@ static void D3D12_INTERNAL_WriteGPUDescriptors( Uint32 resourceHandleCount, D3D12_GPU_DESCRIPTOR_HANDLE *gpuBaseDescriptor) { - D3D12DescriptorHeap *heap = commandBuffer->gpuDescriptorHeaps[heapType]; + D3D12DescriptorHeap *heap; D3D12_CPU_DESCRIPTOR_HANDLE gpuHeapCpuHandle; + /* Descriptor overflow, acquire new heaps */ + if (commandBuffer->gpuDescriptorHeaps[heapType]->currentDescriptorIndex >= commandBuffer->gpuDescriptorHeaps[heapType]->maxDescriptors) { + D3D12_INTERNAL_SetGPUDescriptorHeaps(commandBuffer); + } + + heap = commandBuffer->gpuDescriptorHeaps[heapType]; + // FIXME: need to error on overflow gpuHeapCpuHandle.ptr = heap->descriptorHeapCPUStart.ptr + (heap->currentDescriptorIndex * heap->descriptorSize); gpuBaseDescriptor->ptr = heap->descriptorHeapGPUStart.ptr + (heap->currentDescriptorIndex * heap->descriptorSize); @@ -4684,6 +4808,11 @@ static void D3D12_INTERNAL_BindGraphicsResources( { D3D12GraphicsPipeline *graphicsPipeline = commandBuffer->currentGraphicsPipeline; + /* Acquire GPU descriptor heaps if we haven't yet */ + if (commandBuffer->gpuDescriptorHeaps[0] == NULL) { + D3D12_INTERNAL_SetGPUDescriptorHeaps(commandBuffer); + } + D3D12_CPU_DESCRIPTOR_HANDLE cpuHandles[MAX_TEXTURE_SAMPLERS_PER_STAGE]; D3D12_GPU_DESCRIPTOR_HANDLE gpuDescriptorHandle; D3D12_VERTEX_BUFFER_VIEW vertexBufferViews[MAX_VERTEX_BUFFERS]; @@ -5112,6 +5241,12 @@ static void D3D12_BindComputePipeline( SDL_GPUComputePipeline *computePipeline) { D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer; + + /* Acquire GPU descriptor heaps if we haven't yet */ + if (d3d12CommandBuffer->gpuDescriptorHeaps[0] == NULL) { + D3D12_INTERNAL_SetGPUDescriptorHeaps(d3d12CommandBuffer); + } + D3D12ComputePipeline *pipeline = (D3D12ComputePipeline *)computePipeline; D3D12_CPU_DESCRIPTOR_HANDLE cpuHandles[MAX_TEXTURE_SAMPLERS_PER_STAGE]; D3D12_GPU_DESCRIPTOR_HANDLE gpuDescriptorHandle; @@ -5294,6 +5429,11 @@ static void D3D12_INTERNAL_BindComputeResources( { D3D12ComputePipeline *computePipeline = commandBuffer->currentComputePipeline; + /* Acquire GPU descriptor heaps if we haven't yet */ + if (commandBuffer->gpuDescriptorHeaps[0] == NULL) { + D3D12_INTERNAL_SetGPUDescriptorHeaps(commandBuffer); + } + D3D12_CPU_DESCRIPTOR_HANDLE cpuHandles[MAX_TEXTURE_SAMPLERS_PER_STAGE]; D3D12_GPU_DESCRIPTOR_HANDLE gpuDescriptorHandle; @@ -5898,7 +6038,7 @@ static void D3D12_DownloadFromTexture( * And just for some extra fun, D3D12 doesn't actually support depth pitch, so we have to realign that too! * * Since this is an async download we have to do all these fixups after the command is finished, - * so we'll cache the metadata similar to D3D11 and map and copy it when the command buffer is cleaned. + * so we'll cache the metadata and map and copy it when the command buffer is cleaned. */ if (pixelsPerRow == 0) { @@ -6184,7 +6324,7 @@ static bool D3D12_SupportsSwapchainComposition( D3D12WindowData *windowData = D3D12_INTERNAL_FetchWindowData(window); if (windowData == NULL) { - SET_STRING_ERROR_AND_RETURN("Must claim window before querying swapchain composition support!", false) + SET_STRING_ERROR_AND_RETURN("Must claim window before querying swapchain composition support!", false); } // Check the color space support if necessary @@ -6242,6 +6382,9 @@ static bool D3D12_INTERNAL_CreateSwapchain( SDL_SyncWindow(windowData->window); SDL_GetWindowSizeInPixels(windowData->window, &width, &height); + // Min swapchain image count is 2 + windowData->swapchainTextureCount = SDL_clamp(renderer->allowedFramesInFlight, 2, 3); + // Create the swapchain textures SDL_zero(createInfo); createInfo.type = SDL_GPU_TEXTURETYPE_2D; @@ -6252,7 +6395,7 @@ static bool D3D12_INTERNAL_CreateSwapchain( createInfo.layer_count_or_depth = 1; createInfo.num_levels = 1; - for (Uint32 i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) { + for (Uint32 i = 0; i < windowData->swapchainTextureCount; i += 1) { texture = D3D12_INTERNAL_CreateTexture(renderer, &createInfo, true); texture->container = &windowData->textureContainers[i]; windowData->textureContainers[i].activeTexture = texture; @@ -6296,7 +6439,7 @@ static void D3D12_INTERNAL_DestroySwapchain( D3D12WindowData *windowData) { renderer->commandQueue->PresentX(0, NULL, NULL); - for (Uint32 i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) { + for (Uint32 i = 0; i < windowData->swapchainTextureCount; i += 1) { D3D12_INTERNAL_DestroyTexture( renderer, windowData->textureContainers[i].activeTexture); @@ -6314,7 +6457,7 @@ static bool D3D12_INTERNAL_ResizeSwapchain( renderer->commandQueue->PresentX(0, NULL, NULL); // Clean up the previous swapchain textures - for (Uint32 i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) { + for (Uint32 i = 0; i < windowData->swapchainTextureCount; i += 1) { D3D12_INTERNAL_DestroyTexture( renderer, windowData->textureContainers[i].activeTexture); @@ -6367,7 +6510,7 @@ static bool D3D12_INTERNAL_InitializeSwapchainTexture( ID3D12Resource_Release(swapchainTexture); return false; } - pTexture->subresources[0].rtvHandles = SDL_calloc(1, sizeof(D3D12CPUDescriptor)); + pTexture->subresources[0].rtvHandles = SDL_calloc(1, sizeof(D3D12StagingDescriptor)); pTexture->subresources[0].uavHandle.heap = NULL; pTexture->subresources[0].dsvHandle.heap = NULL; pTexture->subresources[0].parent = pTexture; @@ -6405,7 +6548,7 @@ static bool D3D12_INTERNAL_InitializeSwapchainTexture( pTexture->containerIndex = 0; // Create the SRV for the swapchain - D3D12_INTERNAL_AssignCpuDescriptorHandle( + D3D12_INTERNAL_AssignStagingDescriptorHandle( renderer, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, &pTexture->srvHandle); @@ -6425,7 +6568,7 @@ static bool D3D12_INTERNAL_InitializeSwapchainTexture( pTexture->srvHandle.cpuHandle); // Create the RTV for the swapchain - D3D12_INTERNAL_AssignCpuDescriptorHandle( + D3D12_INTERNAL_AssignStagingDescriptorHandle( renderer, D3D12_DESCRIPTOR_HEAP_TYPE_RTV, &pTexture->subresources[0].rtvHandles[0]); @@ -6454,11 +6597,11 @@ static bool D3D12_INTERNAL_ResizeSwapchain( D3D12_Wait((SDL_GPURenderer *)renderer); // Release views and clean up - for (Uint32 i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) { - D3D12_INTERNAL_ReleaseCpuDescriptorHandle( + for (Uint32 i = 0; i < windowData->swapchainTextureCount; i += 1) { + D3D12_INTERNAL_ReleaseStagingDescriptorHandle( renderer, &windowData->textureContainers[i].activeTexture->srvHandle); - D3D12_INTERNAL_ReleaseCpuDescriptorHandle( + D3D12_INTERNAL_ReleaseStagingDescriptorHandle( renderer, &windowData->textureContainers[i].activeTexture->subresources[0].rtvHandles[0]); @@ -6476,10 +6619,10 @@ static bool D3D12_INTERNAL_ResizeSwapchain( 0, // use client window height DXGI_FORMAT_UNKNOWN, // Keep the old format renderer->supportsTearing ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0); - CHECK_D3D12_ERROR_AND_RETURN("Could not resize swapchain buffers", false) + CHECK_D3D12_ERROR_AND_RETURN("Could not resize swapchain buffers", false); // Create texture object for the swapchain - for (Uint32 i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) { + for (Uint32 i = 0; i < windowData->swapchainTextureCount; i += 1) { if (!D3D12_INTERNAL_InitializeSwapchainTexture( renderer, windowData->swapchain, @@ -6492,7 +6635,7 @@ static bool D3D12_INTERNAL_ResizeSwapchain( DXGI_SWAP_CHAIN_DESC1 swapchainDesc; IDXGISwapChain3_GetDesc1(windowData->swapchain, &swapchainDesc); - CHECK_D3D12_ERROR_AND_RETURN("Failed to retrieve swapchain descriptor!", false) + CHECK_D3D12_ERROR_AND_RETURN("Failed to retrieve swapchain descriptor!", false); windowData->width = swapchainDesc.Width; windowData->height = swapchainDesc.Height; @@ -6505,11 +6648,11 @@ static void D3D12_INTERNAL_DestroySwapchain( D3D12WindowData *windowData) { // Release views and clean up - for (Uint32 i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) { - D3D12_INTERNAL_ReleaseCpuDescriptorHandle( + for (Uint32 i = 0; i < windowData->swapchainTextureCount; i += 1) { + D3D12_INTERNAL_ReleaseStagingDescriptorHandle( renderer, &windowData->textureContainers[i].activeTexture->srvHandle); - D3D12_INTERNAL_ReleaseCpuDescriptorHandle( + D3D12_INTERNAL_ReleaseStagingDescriptorHandle( renderer, &windowData->textureContainers[i].activeTexture->subresources[0].rtvHandles[0]); @@ -6547,6 +6690,9 @@ static bool D3D12_INTERNAL_CreateSwapchain( swapchainFormat = SwapchainCompositionToTextureFormat[swapchainComposition]; + // Min swapchain image count is 2 + windowData->swapchainTextureCount = SDL_clamp(renderer->allowedFramesInFlight, 2, 3); + // Initialize the swapchain buffer descriptor swapchainDesc.Width = 0; // use client window width swapchainDesc.Height = 0; // use client window height @@ -6554,7 +6700,7 @@ static bool D3D12_INTERNAL_CreateSwapchain( swapchainDesc.SampleDesc.Count = 1; swapchainDesc.SampleDesc.Quality = 0; swapchainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; - swapchainDesc.BufferCount = MAX_FRAMES_IN_FLIGHT; + swapchainDesc.BufferCount = windowData->swapchainTextureCount; swapchainDesc.Scaling = DXGI_SCALING_STRETCH; swapchainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; swapchainDesc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED; @@ -6616,7 +6762,7 @@ static bool D3D12_INTERNAL_CreateSwapchain( (void **)&pParent); if (FAILED(res)) { SDL_LogWarn( - SDL_LOG_CATEGORY_APPLICATION, + SDL_LOG_CATEGORY_GPU, "Could not get swapchain parent! Error Code: " HRESULT_FMT, res); } else { @@ -6627,7 +6773,7 @@ static bool D3D12_INTERNAL_CreateSwapchain( DXGI_MWA_NO_WINDOW_CHANGES); if (FAILED(res)) { SDL_LogWarn( - SDL_LOG_CATEGORY_APPLICATION, + SDL_LOG_CATEGORY_GPU, "MakeWindowAssociation failed! Error Code: " HRESULT_FMT, res); } @@ -6637,7 +6783,7 @@ static bool D3D12_INTERNAL_CreateSwapchain( } IDXGISwapChain3_GetDesc1(swapchain3, &swapchainDesc); - CHECK_D3D12_ERROR_AND_RETURN("Failed to retrieve swapchain descriptor!", false) + CHECK_D3D12_ERROR_AND_RETURN("Failed to retrieve swapchain descriptor!", false); // Initialize the swapchain data windowData->swapchain = swapchain3; @@ -6668,7 +6814,7 @@ static bool D3D12_INTERNAL_CreateSwapchain( /* If a you are using a FLIP model format you can't create the swapchain as DXGI_FORMAT_B8G8R8A8_UNORM_SRGB. * You have to create the swapchain as DXGI_FORMAT_B8G8R8A8_UNORM and then set the render target view's format to DXGI_FORMAT_B8G8R8A8_UNORM_SRGB */ - for (Uint32 i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) { + for (Uint32 i = 0; i < windowData->swapchainTextureCount; i += 1) { if (!D3D12_INTERNAL_InitializeSwapchainTexture( renderer, swapchain3, @@ -6717,11 +6863,10 @@ static bool D3D12_ClaimWindow( return true; } else { SDL_free(windowData); - SET_STRING_ERROR_AND_RETURN("Could not create swapchain, failed to claim window!", false) + SET_STRING_ERROR_AND_RETURN("Could not create swapchain, failed to claim window!", false); } } else { - SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Window already claimed!"); - return false; + SET_STRING_ERROR_AND_RETURN("Window already claimed", false); } } @@ -6733,8 +6878,7 @@ static void D3D12_ReleaseWindow( D3D12WindowData *windowData = D3D12_INTERNAL_FetchWindowData(window); if (windowData == NULL) { - SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Window already unclaimed!"); - return; + SET_STRING_ERROR_AND_RETURN("Window already unclaimed!", ); } D3D12_Wait(driverData); @@ -6775,15 +6919,15 @@ static bool D3D12_SetSwapchainParameters( D3D12WindowData *windowData = D3D12_INTERNAL_FetchWindowData(window); if (windowData == NULL) { - SET_STRING_ERROR_AND_RETURN("Cannot set swapchain parameters on unclaimed window!", false) + SET_STRING_ERROR_AND_RETURN("Cannot set swapchain parameters on unclaimed window!", false); } if (!D3D12_SupportsSwapchainComposition(driverData, window, swapchainComposition)) { - SET_STRING_ERROR_AND_RETURN("Swapchain composition not supported!", false) + SET_STRING_ERROR_AND_RETURN("Swapchain composition not supported!", false); } if (!D3D12_SupportsPresentMode(driverData, window, presentMode)) { - SET_STRING_ERROR_AND_RETURN("Present mode not supported!", false) + SET_STRING_ERROR_AND_RETURN("Present mode not supported!", false); } if ( @@ -6806,6 +6950,42 @@ static bool D3D12_SetSwapchainParameters( return true; } +static bool D3D12_SetAllowedFramesInFlight( + SDL_GPURenderer *driverData, + Uint32 allowedFramesInFlight) +{ + D3D12Renderer *renderer = (D3D12Renderer *)driverData; + + if (!D3D12_Wait(driverData)) { + return false; + } + + // Destroy all swapchains + for (Uint32 i = 0; i < renderer->claimedWindowCount; i += 1) { + D3D12WindowData *windowData = renderer->claimedWindows[i]; + + D3D12_INTERNAL_DestroySwapchain(renderer, windowData); + } + + // Set the frames in flight value + renderer->allowedFramesInFlight = allowedFramesInFlight; + + // Recreate all swapchains + for (Uint32 i = 0; i < renderer->claimedWindowCount; i += 1) { + D3D12WindowData *windowData = renderer->claimedWindows[i]; + + if (!D3D12_INTERNAL_CreateSwapchain( + renderer, + windowData, + windowData->swapchainComposition, + windowData->present_mode)) { + return false; + } + } + + return true; +} + static SDL_GPUTextureFormat D3D12_GetSwapchainTextureFormat( SDL_GPURenderer *driverData, SDL_Window *window) @@ -6814,7 +6994,7 @@ static SDL_GPUTextureFormat D3D12_GetSwapchainTextureFormat( D3D12WindowData *windowData = D3D12_INTERNAL_FetchWindowData(window); if (windowData == NULL) { - SET_STRING_ERROR_AND_RETURN("Cannot get swapchain format, window has not been claimed!", SDL_GPU_TEXTUREFORMAT_INVALID) + SET_STRING_ERROR_AND_RETURN("Cannot get swapchain format, window has not been claimed!", SDL_GPU_TEXTUREFORMAT_INVALID); } return windowData->textureContainers[windowData->frameCounter].header.info.format; @@ -6849,7 +7029,7 @@ static D3D12Fence *D3D12_INTERNAL_AcquireFence( return NULL; } fence->handle = handle; - fence->event = CreateEventEx(NULL, 0, 0, EVENT_ALL_ACCESS); + fence->event = CreateEvent(NULL, FALSE, FALSE, NULL); SDL_SetAtomicInt(&fence->referenceCount, 0); } else { fence = renderer->availableFences[renderer->availableFenceCount - 1]; @@ -6873,7 +7053,7 @@ static bool D3D12_INTERNAL_AllocateCommandBuffer( commandBuffer = (D3D12CommandBuffer *)SDL_calloc(1, sizeof(D3D12CommandBuffer)); if (!commandBuffer) { - SET_STRING_ERROR_AND_RETURN("Failed to create ID3D12CommandList. Out of Memory", false) + SET_STRING_ERROR_AND_RETURN("Failed to create ID3D12CommandList. Out of Memory", false); } res = ID3D12Device_CreateCommandAllocator( @@ -6959,7 +7139,7 @@ static bool D3D12_INTERNAL_AllocateCommandBuffer( (!commandBuffer->usedUniformBuffers) || (!commandBuffer->textureDownloads)) { D3D12_INTERNAL_DestroyCommandBuffer(commandBuffer); - SET_STRING_ERROR_AND_RETURN("Failed to create ID3D12CommandList. Out of Memory", false) + SET_STRING_ERROR_AND_RETURN("Failed to create ID3D12CommandList. Out of Memory", false); } D3D12CommandBuffer **resizedAvailableCommandBuffers = (D3D12CommandBuffer **)SDL_realloc( @@ -6968,7 +7148,7 @@ static bool D3D12_INTERNAL_AllocateCommandBuffer( if (!resizedAvailableCommandBuffers) { D3D12_INTERNAL_DestroyCommandBuffer(commandBuffer); - SET_STRING_ERROR_AND_RETURN("Failed to create ID3D12CommandList. Out of Memory", false) + SET_STRING_ERROR_AND_RETURN("Failed to create ID3D12CommandList. Out of Memory", false); } // Add to inactive command buffer array renderer->availableCommandBufferCapacity += 1; @@ -7013,31 +7193,6 @@ static SDL_GPUCommandBuffer *D3D12_AcquireCommandBuffer( return NULL; } - // Set the descriptor heaps! - commandBuffer->gpuDescriptorHeaps[D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV] = - D3D12_INTERNAL_AcquireDescriptorHeapFromPool(commandBuffer, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); - - if (!commandBuffer->gpuDescriptorHeaps[D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV]) { - D3D12_INTERNAL_DestroyCommandBuffer(commandBuffer); - return NULL; - } - - commandBuffer->gpuDescriptorHeaps[D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER] = - D3D12_INTERNAL_AcquireDescriptorHeapFromPool(commandBuffer, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER); - - if (!commandBuffer->gpuDescriptorHeaps[D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER]) { - D3D12_INTERNAL_DestroyCommandBuffer(commandBuffer); - return NULL; - } - - heaps[0] = commandBuffer->gpuDescriptorHeaps[D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV]->handle; - heaps[1] = commandBuffer->gpuDescriptorHeaps[D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER]->handle; - - ID3D12GraphicsCommandList_SetDescriptorHeaps( - commandBuffer->graphicsCommandList, - 2, - heaps); - // Set the bind state commandBuffer->currentGraphicsPipeline = NULL; @@ -7074,7 +7229,32 @@ static SDL_GPUCommandBuffer *D3D12_AcquireCommandBuffer( return (SDL_GPUCommandBuffer *)commandBuffer; } -static bool D3D12_AcquireSwapchainTexture( +static bool D3D12_WaitForSwapchain( + SDL_GPURenderer *driverData, + SDL_Window *window) +{ + D3D12Renderer *renderer = (D3D12Renderer *)driverData; + D3D12WindowData *windowData = D3D12_INTERNAL_FetchWindowData(window); + + if (windowData == NULL) { + SET_STRING_ERROR_AND_RETURN("Cannot wait for a swapchain from an unclaimed window!", false); + } + + if (windowData->inFlightFences[windowData->frameCounter] != NULL) { + if (!D3D12_WaitForFences( + driverData, + true, + &windowData->inFlightFences[windowData->frameCounter], + 1)) { + return false; + } + } + + return true; +} + +static bool D3D12_INTERNAL_AcquireSwapchainTexture( + bool block, SDL_GPUCommandBuffer *commandBuffer, SDL_Window *window, SDL_GPUTexture **swapchainTexture, @@ -7097,7 +7277,7 @@ static bool D3D12_AcquireSwapchainTexture( windowData = D3D12_INTERNAL_FetchWindowData(window); if (windowData == NULL) { - SET_STRING_ERROR_AND_RETURN("Cannot acquire swapchain texture from an unclaimed window!", false) + SET_STRING_ERROR_AND_RETURN("Cannot acquire swapchain texture from an unclaimed window!", false); } if (windowData->needsSwapchainRecreate) { @@ -7114,7 +7294,7 @@ static bool D3D12_AcquireSwapchainTexture( } if (windowData->inFlightFences[windowData->frameCounter] != NULL) { - if (windowData->present_mode == SDL_GPU_PRESENTMODE_VSYNC) { + if (block) { // In VSYNC mode, block until the least recent presented frame is done if (!D3D12_WaitForFences( (SDL_GPURenderer *)renderer, @@ -7124,13 +7304,11 @@ static bool D3D12_AcquireSwapchainTexture( return false; } } else { + // If we are not blocking and the least recent fence is not signaled, + // return true to indicate that there is no error but rendering should be skipped. if (!D3D12_QueryFence( (SDL_GPURenderer *)renderer, windowData->inFlightFences[windowData->frameCounter])) { - /* - * In MAILBOX or IMMEDIATE mode, if the least recent fence is not signaled, - * return true to indicate that there is no error but rendering should be skipped - */ return true; } } @@ -7188,6 +7366,38 @@ static bool D3D12_AcquireSwapchainTexture( return true; } +static bool D3D12_AcquireSwapchainTexture( + SDL_GPUCommandBuffer *command_buffer, + SDL_Window *window, + SDL_GPUTexture **swapchain_texture, + Uint32 *swapchain_texture_width, + Uint32 *swapchain_texture_height +) { + return D3D12_INTERNAL_AcquireSwapchainTexture( + false, + command_buffer, + window, + swapchain_texture, + swapchain_texture_width, + swapchain_texture_height); +} + +static bool D3D12_WaitAndAcquireSwapchainTexture( + SDL_GPUCommandBuffer *command_buffer, + SDL_Window *window, + SDL_GPUTexture **swapchain_texture, + Uint32 *swapchain_texture_width, + Uint32 *swapchain_texture_height +) { + return D3D12_INTERNAL_AcquireSwapchainTexture( + true, + command_buffer, + window, + swapchain_texture, + swapchain_texture_width, + swapchain_texture_height); +} + static void D3D12_INTERNAL_PerformPendingDestroys(D3D12Renderer *renderer) { SDL_LockMutex(renderer->disposeLock); @@ -7263,7 +7473,7 @@ static bool D3D12_INTERNAL_CopyTextureDownload( NULL, (void **)&sourcePtr); - CHECK_D3D12_ERROR_AND_RETURN("Failed to map temporary buffer", false) + CHECK_D3D12_ERROR_AND_RETURN("Failed to map temporary buffer", false); res = ID3D12Resource_Map( download->destinationBuffer->handle, @@ -7271,7 +7481,7 @@ static bool D3D12_INTERNAL_CopyTextureDownload( NULL, (void **)&destPtr); - CHECK_D3D12_ERROR_AND_RETURN("Failed to map destination buffer", false) + CHECK_D3D12_ERROR_AND_RETURN("Failed to map destination buffer", false); for (Uint32 sliceIndex = 0; sliceIndex < download->depth; sliceIndex += 1) { for (Uint32 rowIndex = 0; rowIndex < download->height; rowIndex += 1) { @@ -7320,22 +7530,25 @@ static bool D3D12_INTERNAL_CleanCommandBuffer( } res = ID3D12CommandAllocator_Reset(commandBuffer->commandAllocator); - CHECK_D3D12_ERROR_AND_RETURN("Could not reset command allocator", false) + CHECK_D3D12_ERROR_AND_RETURN("Could not reset command allocator", false); res = ID3D12GraphicsCommandList_Reset( commandBuffer->graphicsCommandList, commandBuffer->commandAllocator, NULL); - CHECK_D3D12_ERROR_AND_RETURN("Could not reset command list", false) + CHECK_D3D12_ERROR_AND_RETURN("Could not reset command list", false); // Return descriptor heaps to pool - D3D12_INTERNAL_ReturnDescriptorHeapToPool( + D3D12_INTERNAL_ReturnGPUDescriptorHeapToPool( renderer, commandBuffer->gpuDescriptorHeaps[D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV]); - D3D12_INTERNAL_ReturnDescriptorHeapToPool( + D3D12_INTERNAL_ReturnGPUDescriptorHeapToPool( renderer, commandBuffer->gpuDescriptorHeaps[D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER]); + commandBuffer->gpuDescriptorHeaps[D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV] = NULL; + commandBuffer->gpuDescriptorHeaps[D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER] = NULL; + // Uniform buffers are now available SDL_LockMutex(renderer->acquireUniformBufferLock); @@ -7476,7 +7689,7 @@ static bool D3D12_Submit( (void **)&commandLists[0]); if (FAILED(res)) { SDL_UnlockMutex(renderer->submitLock); - CHECK_D3D12_ERROR_AND_RETURN("Failed to convert command list!", false) + CHECK_D3D12_ERROR_AND_RETURN("Failed to convert command list!", false); } // Submit the command list to the queue @@ -7566,7 +7779,7 @@ static bool D3D12_Submit( windowData->inFlightFences[windowData->frameCounter] = (SDL_GPUFence*)d3d12CommandBuffer->inFlightFence; (void)SDL_AtomicIncRef(&d3d12CommandBuffer->inFlightFence->referenceCount); - windowData->frameCounter = (windowData->frameCounter + 1) % MAX_FRAMES_IN_FLIGHT; + windowData->frameCounter = (windowData->frameCounter + 1) % renderer->allowedFramesInFlight; } // Check for cleanups @@ -7645,12 +7858,12 @@ static bool D3D12_Wait( fence->handle, D3D12_FENCE_SIGNAL_VALUE, fence->event); - CHECK_D3D12_ERROR_AND_RETURN("Setting fence event failed", false) + CHECK_D3D12_ERROR_AND_RETURN("Setting fence event failed", false); DWORD waitResult = WaitForSingleObject(fence->event, INFINITE); if (waitResult == WAIT_FAILED) { SDL_UnlockMutex(renderer->submitLock); - SET_STRING_ERROR_AND_RETURN("Wait failed", false) // TODO: is there a better way to report this? + SET_STRING_ERROR_AND_RETURN("Wait failed", false); // TODO: is there a better way to report this? } } } @@ -7693,7 +7906,7 @@ static bool D3D12_WaitForFences( fence->handle, D3D12_FENCE_SIGNAL_VALUE, fence->event); - CHECK_D3D12_ERROR_AND_RETURN("Setting fence event failed", false) + CHECK_D3D12_ERROR_AND_RETURN("Setting fence event failed", false); events[i] = fence->event; } @@ -7706,7 +7919,7 @@ static bool D3D12_WaitForFences( if (waitResult == WAIT_FAILED) { SDL_UnlockMutex(renderer->submitLock); - SET_STRING_ERROR_AND_RETURN("Wait failed", false) // TODO: is there a better way to report this? + SET_STRING_ERROR_AND_RETURN("Wait failed", false); // TODO: is there a better way to report this? } bool result = true; @@ -7940,7 +8153,7 @@ static void D3D12_INTERNAL_InitBlitResources( samplerCreateInfo.min_lod = 0; samplerCreateInfo.max_lod = 1000; samplerCreateInfo.max_anisotropy = 1.0f; - samplerCreateInfo.compare_op = SDL_GPU_COMPAREOP_ALWAYS; + samplerCreateInfo.compare_op = SDL_GPU_COMPAREOP_NEVER; renderer->blitNearestSampler = D3D12_CreateSampler( (SDL_GPURenderer *)renderer, @@ -7983,7 +8196,7 @@ static bool D3D12_PrepareDriver(SDL_VideoDevice *_this) d3d12Dll = SDL_LoadObject(D3D12_DLL); if (d3d12Dll == NULL) { - SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "D3D12: Could not find " D3D12_DLL); + SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "D3D12: Could not find " D3D12_DLL); return false; } @@ -7991,7 +8204,7 @@ static bool D3D12_PrepareDriver(SDL_VideoDevice *_this) d3d12Dll, D3D12_CREATE_DEVICE_FUNC); if (D3D12CreateDeviceFunc == NULL) { - SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "D3D12: Could not find function " D3D12_CREATE_DEVICE_FUNC " in " D3D12_DLL); + SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "D3D12: Could not find function " D3D12_CREATE_DEVICE_FUNC " in " D3D12_DLL); SDL_UnloadObject(d3d12Dll); return false; } @@ -8000,7 +8213,7 @@ static bool D3D12_PrepareDriver(SDL_VideoDevice *_this) dxgiDll = SDL_LoadObject(DXGI_DLL); if (dxgiDll == NULL) { - SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "D3D12: Could not find " DXGI_DLL); + SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "D3D12: Could not find " DXGI_DLL); return false; } @@ -8008,7 +8221,7 @@ static bool D3D12_PrepareDriver(SDL_VideoDevice *_this) dxgiDll, CREATE_DXGI_FACTORY1_FUNC); if (CreateDXGIFactoryFunc == NULL) { - SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "D3D12: Could not find function " CREATE_DXGI_FACTORY1_FUNC " in " DXGI_DLL); + SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "D3D12: Could not find function " CREATE_DXGI_FACTORY1_FUNC " in " DXGI_DLL); SDL_UnloadObject(dxgiDll); return false; } @@ -8020,7 +8233,7 @@ static bool D3D12_PrepareDriver(SDL_VideoDevice *_this) &D3D_IID_IDXGIFactory1, (void **)&factory); if (FAILED(res)) { - SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "D3D12: Could not create DXGIFactory"); + SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "D3D12: Could not create DXGIFactory"); SDL_UnloadObject(d3d12Dll); SDL_UnloadObject(dxgiDll); return false; @@ -8033,7 +8246,7 @@ static bool D3D12_PrepareDriver(SDL_VideoDevice *_this) (void **)&factory4); if (FAILED(res)) { IDXGIFactory1_Release(factory); - SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "D3D12: Failed to find DXGI1.4 support, required for DX12"); + SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "D3D12: Failed to find DXGI1.4 support, required for DX12"); SDL_UnloadObject(d3d12Dll); SDL_UnloadObject(dxgiDll); return false; @@ -8059,7 +8272,7 @@ static bool D3D12_PrepareDriver(SDL_VideoDevice *_this) &adapter); } if (FAILED(res)) { - SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "D3D12: Failed to find adapter for D3D12Device"); + SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "D3D12: Failed to find adapter for D3D12Device"); IDXGIFactory1_Release(factory); SDL_UnloadObject(d3d12Dll); SDL_UnloadObject(dxgiDll); @@ -8082,7 +8295,7 @@ static bool D3D12_PrepareDriver(SDL_VideoDevice *_this) SDL_UnloadObject(dxgiDll); if (FAILED(res)) { - SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "D3D12: Could not create D3D12Device with feature level " D3D_FEATURE_LEVEL_CHOICE_STR); + SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "D3D12: Could not create D3D12Device with feature level " D3D_FEATURE_LEVEL_CHOICE_STR); return false; } @@ -8098,7 +8311,7 @@ static void D3D12_INTERNAL_TryInitializeDXGIDebug(D3D12Renderer *renderer) renderer->dxgidebug_dll = SDL_LoadObject(DXGIDEBUG_DLL); if (renderer->dxgidebug_dll == NULL) { - SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Could not find " DXGIDEBUG_DLL); + SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "Could not find " DXGIDEBUG_DLL); return; } @@ -8106,18 +8319,18 @@ static void D3D12_INTERNAL_TryInitializeDXGIDebug(D3D12Renderer *renderer) renderer->dxgidebug_dll, DXGI_GET_DEBUG_INTERFACE_FUNC); if (DXGIGetDebugInterfaceFunc == NULL) { - SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Could not load function: " DXGI_GET_DEBUG_INTERFACE_FUNC); + SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "Could not load function: " DXGI_GET_DEBUG_INTERFACE_FUNC); return; } res = DXGIGetDebugInterfaceFunc(&D3D_IID_IDXGIDebug, (void **)&renderer->dxgiDebug); if (FAILED(res)) { - SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Could not get IDXGIDebug interface"); + SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "Could not get IDXGIDebug interface"); } res = DXGIGetDebugInterfaceFunc(&D3D_IID_IDXGIInfoQueue, (void **)&renderer->dxgiInfoQueue); if (FAILED(res)) { - SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Could not get IDXGIInfoQueue interface"); + SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "Could not get IDXGIInfoQueue interface"); } } #endif @@ -8137,7 +8350,7 @@ static void D3D12_INTERNAL_TryInitializeD3D12Debug(D3D12Renderer *renderer) res = D3D12GetDebugInterfaceFunc(D3D_GUID(D3D_IID_ID3D12Debug), (void **)&renderer->d3d12Debug); if (FAILED(res)) { - SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Could not get ID3D12Debug interface"); + SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "Could not get ID3D12Debug interface"); return; } @@ -8167,11 +8380,6 @@ static bool D3D12_INTERNAL_TryInitializeD3D12DebugInfoQueue(D3D12Renderer *rende infoQueue, &filter); - ID3D12InfoQueue_SetBreakOnSeverity( - infoQueue, - D3D12_MESSAGE_SEVERITY_ERROR, - true); - ID3D12InfoQueue_SetBreakOnSeverity( infoQueue, D3D12_MESSAGE_SEVERITY_CORRUPTION, @@ -8181,6 +8389,118 @@ static bool D3D12_INTERNAL_TryInitializeD3D12DebugInfoQueue(D3D12Renderer *rende return true; } + +static void WINAPI D3D12_INTERNAL_OnD3D12DebugInfoMsg( + D3D12_MESSAGE_CATEGORY category, + D3D12_MESSAGE_SEVERITY severity, + D3D12_MESSAGE_ID id, + LPCSTR description, + void *context) +{ + char *catStr; + char *sevStr; + + switch (category) { + case D3D12_MESSAGE_CATEGORY_APPLICATION_DEFINED: + catStr = "APPLICATION_DEFINED"; + break; + case D3D12_MESSAGE_CATEGORY_MISCELLANEOUS: + catStr = "MISCELLANEOUS"; + break; + case D3D12_MESSAGE_CATEGORY_INITIALIZATION: + catStr = "INITIALIZATION"; + break; + case D3D12_MESSAGE_CATEGORY_CLEANUP: + catStr = "CLEANUP"; + break; + case D3D12_MESSAGE_CATEGORY_COMPILATION: + catStr = "COMPILATION"; + break; + case D3D12_MESSAGE_CATEGORY_STATE_CREATION: + catStr = "STATE_CREATION"; + break; + case D3D12_MESSAGE_CATEGORY_STATE_SETTING: + catStr = "STATE_SETTING"; + break; + case D3D12_MESSAGE_CATEGORY_STATE_GETTING: + catStr = "STATE_GETTING"; + break; + case D3D12_MESSAGE_CATEGORY_RESOURCE_MANIPULATION: + catStr = "RESOURCE_MANIPULATION"; + break; + case D3D12_MESSAGE_CATEGORY_EXECUTION: + catStr = "EXECUTION"; + break; + case D3D12_MESSAGE_CATEGORY_SHADER: + catStr = "SHADER"; + break; + default: + catStr = "UNKNOWN"; + break; + } + + switch (severity) { + case D3D12_MESSAGE_SEVERITY_CORRUPTION: + sevStr = "CORRUPTION"; + break; + case D3D12_MESSAGE_SEVERITY_ERROR: + sevStr = "ERROR"; + break; + case D3D12_MESSAGE_SEVERITY_WARNING: + sevStr = "WARNING"; + break; + case D3D12_MESSAGE_SEVERITY_INFO: + sevStr = "INFO"; + break; + case D3D12_MESSAGE_SEVERITY_MESSAGE: + sevStr = "MESSAGE"; + break; + default: + sevStr = "UNKNOWN"; + break; + } + + if (severity <= D3D12_MESSAGE_SEVERITY_ERROR) { + SDL_LogError( + SDL_LOG_CATEGORY_GPU, + "D3D12 ERROR: %s [%s %s #%d]", + description, + catStr, + sevStr, + id); + } else { + SDL_LogWarn( + SDL_LOG_CATEGORY_GPU, + "D3D12 WARNING: %s [%s %s #%d]", + description, + catStr, + sevStr, + id); + } +} + +static void D3D12_INTERNAL_TryInitializeD3D12DebugInfoLogger(D3D12Renderer *renderer) +{ + ID3D12InfoQueue1 *infoQueue = NULL; + HRESULT res; + + res = ID3D12Device_QueryInterface( + renderer->device, + D3D_GUID(D3D_IID_ID3D12InfoQueue1), + (void **)&infoQueue); + if (FAILED(res)) { + return; + } + + ID3D12InfoQueue1_RegisterMessageCallback( + infoQueue, + D3D12_INTERNAL_OnD3D12DebugInfoMsg, + D3D12_MESSAGE_CALLBACK_FLAG_NONE, + NULL, + NULL); + + ID3D12InfoQueue1_Release(infoQueue); +} #endif static SDL_GPUDevice *D3D12_CreateDevice(bool debugMode, bool preferLowPower, SDL_PropertiesID props) @@ -8198,6 +8518,7 @@ static SDL_GPUDevice *D3D12_CreateDevice(bool debugMode, bool preferLowPower, SD IDXGIFactory5 *factory5; IDXGIFactory6 *factory6; DXGI_ADAPTER_DESC1 adapterDesc; + LARGE_INTEGER umdVersion; PFN_D3D12_CREATE_DEVICE D3D12CreateDeviceFunc; #endif D3D12_FEATURE_DATA_ARCHITECTURE architecture; @@ -8226,7 +8547,7 @@ static SDL_GPUDevice *D3D12_CreateDevice(bool debugMode, bool preferLowPower, SD CREATE_DXGI_FACTORY1_FUNC); if (CreateDXGIFactoryFunc == NULL) { D3D12_INTERNAL_DestroyRenderer(renderer); - SET_STRING_ERROR_AND_RETURN("Could not load function: " CREATE_DXGI_FACTORY1_FUNC, NULL) + SET_STRING_ERROR_AND_RETURN("Could not load function: " CREATE_DXGI_FACTORY1_FUNC, NULL); } // Create the DXGI factory @@ -8297,9 +8618,21 @@ static SDL_GPUDevice *D3D12_CreateDevice(bool debugMode, bool preferLowPower, SD D3D12_INTERNAL_DestroyRenderer(renderer); CHECK_D3D12_ERROR_AND_RETURN("Could not get adapter description", NULL); } + res = IDXGIAdapter1_CheckInterfaceSupport(renderer->adapter, D3D_GUID(D3D_IID_IDXGIDevice), &umdVersion); + if (FAILED(res)) { + D3D12_INTERNAL_DestroyRenderer(renderer); + CHECK_D3D12_ERROR_AND_RETURN("Could not get adapter driver version", NULL); + } SDL_LogInfo(SDL_LOG_CATEGORY_GPU, "SDL_GPU Driver: D3D12"); SDL_LogInfo(SDL_LOG_CATEGORY_GPU, "D3D12 Adapter: %S", adapterDesc.Description); + SDL_LogInfo( + SDL_LOG_CATEGORY_GPU, + "D3D12 Driver: %d.%d.%d.%d", + HIWORD(umdVersion.HighPart), + LOWORD(umdVersion.HighPart), + HIWORD(umdVersion.LowPart), + LOWORD(umdVersion.LowPart)); #endif // Load the D3D library @@ -8316,7 +8649,7 @@ static SDL_GPUDevice *D3D12_CreateDevice(bool debugMode, bool preferLowPower, SD "D3D12XboxCreateDevice"); if (D3D12XboxCreateDeviceFunc == NULL) { D3D12_INTERNAL_DestroyRenderer(renderer); - SET_STRING_ERROR_AND_RETURN("Could not load function: D3D12XboxCreateDevice", NULL) + SET_STRING_ERROR_AND_RETURN("Could not load function: D3D12XboxCreateDevice", NULL); } #else D3D12CreateDeviceFunc = (PFN_D3D12_CREATE_DEVICE)SDL_LoadFunction( @@ -8324,7 +8657,7 @@ static SDL_GPUDevice *D3D12_CreateDevice(bool debugMode, bool preferLowPower, SD D3D12_CREATE_DEVICE_FUNC); if (D3D12CreateDeviceFunc == NULL) { D3D12_INTERNAL_DestroyRenderer(renderer); - SET_STRING_ERROR_AND_RETURN("Could not load function: " D3D12_CREATE_DEVICE_FUNC, NULL) + SET_STRING_ERROR_AND_RETURN("Could not load function: " D3D12_CREATE_DEVICE_FUNC, NULL); } #endif @@ -8333,7 +8666,7 @@ static SDL_GPUDevice *D3D12_CreateDevice(bool debugMode, bool preferLowPower, SD D3D12_SERIALIZE_ROOT_SIGNATURE_FUNC); if (renderer->D3D12SerializeRootSignature_func == NULL) { D3D12_INTERNAL_DestroyRenderer(renderer); - SET_STRING_ERROR_AND_RETURN("Could not load function: " D3D12_SERIALIZE_ROOT_SIGNATURE_FUNC, NULL) + SET_STRING_ERROR_AND_RETURN("Could not load function: " D3D12_SERIALIZE_ROOT_SIGNATURE_FUNC, NULL); } // Initialize the D3D12 debug layer, if applicable @@ -8369,26 +8702,6 @@ static SDL_GPUDevice *D3D12_CreateDevice(bool debugMode, bool preferLowPower, SD CHECK_D3D12_ERROR_AND_RETURN("Could not create D3D12Device", NULL); } - res = renderer->device->SetFrameIntervalX( - NULL, - D3D12XBOX_FRAME_INTERVAL_60_HZ, - MAX_FRAMES_IN_FLIGHT - 1, - D3D12XBOX_FRAME_INTERVAL_FLAG_NONE); - if (FAILED(res)) { - D3D12_INTERNAL_DestroyRenderer(renderer); - CHECK_D3D12_ERROR_AND_RETURN("Could not get set frame interval", NULL); - } - - res = renderer->device->ScheduleFrameEventX( - D3D12XBOX_FRAME_EVENT_ORIGIN, - 0, - NULL, - D3D12XBOX_SCHEDULE_FRAME_EVENT_FLAG_NONE); - if (FAILED(res)) { - D3D12_INTERNAL_DestroyRenderer(renderer); - CHECK_D3D12_ERROR_AND_RETURN("Could not schedule frame events", NULL); - } - s_Device = renderer->device; } #else @@ -8408,6 +8721,7 @@ static SDL_GPUDevice *D3D12_CreateDevice(bool debugMode, bool preferLowPower, SD if (!D3D12_INTERNAL_TryInitializeD3D12DebugInfoQueue(renderer)) { return NULL; } + D3D12_INTERNAL_TryInitializeD3D12DebugInfoLogger(renderer); } #endif @@ -8489,7 +8803,7 @@ static SDL_GPUDevice *D3D12_CreateDevice(bool debugMode, bool preferLowPower, SD (void **)&renderer->indirectDrawCommandSignature); if (FAILED(res)) { D3D12_INTERNAL_DestroyRenderer(renderer); - CHECK_D3D12_ERROR_AND_RETURN("Could not create indirect draw command signature", NULL) + CHECK_D3D12_ERROR_AND_RETURN("Could not create indirect draw command signature", NULL); } indirectArgumentDesc.Type = D3D12_INDIRECT_ARGUMENT_TYPE_DRAW_INDEXED; @@ -8504,7 +8818,7 @@ static SDL_GPUDevice *D3D12_CreateDevice(bool debugMode, bool preferLowPower, SD (void **)&renderer->indirectIndexedDrawCommandSignature); if (FAILED(res)) { D3D12_INTERNAL_DestroyRenderer(renderer); - CHECK_D3D12_ERROR_AND_RETURN("Could not create indirect indexed draw command signature", NULL) + CHECK_D3D12_ERROR_AND_RETURN("Could not create indirect indexed draw command signature", NULL); } indirectArgumentDesc.Type = D3D12_INDIRECT_ARGUMENT_TYPE_DISPATCH; @@ -8519,7 +8833,7 @@ static SDL_GPUDevice *D3D12_CreateDevice(bool debugMode, bool preferLowPower, SD (void **)&renderer->indirectDispatchCommandSignature); if (FAILED(res)) { D3D12_INTERNAL_DestroyRenderer(renderer); - CHECK_D3D12_ERROR_AND_RETURN("Could not create indirect dispatch command signature", NULL) + CHECK_D3D12_ERROR_AND_RETURN("Could not create indirect dispatch command signature", NULL); } // Initialize pools @@ -8560,14 +8874,13 @@ static SDL_GPUDevice *D3D12_CreateDevice(bool debugMode, bool preferLowPower, SD return NULL; } - // Initialize CPU descriptor heaps + // Initialize staging descriptor pools for (Uint32 i = 0; i < D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES; i += 1) { - renderer->stagingDescriptorHeaps[i] = D3D12_INTERNAL_CreateDescriptorHeap( + renderer->stagingDescriptorPools[i] = D3D12_INTERNAL_CreateStagingDescriptorPool( renderer, - (D3D12_DESCRIPTOR_HEAP_TYPE)i, - (i <= D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER) ? VIEW_SAMPLER_STAGING_DESCRIPTOR_COUNT : TARGET_STAGING_DESCRIPTOR_COUNT, - true); - if (renderer->stagingDescriptorHeaps[i] == NULL) { + (D3D12_DESCRIPTOR_HEAP_TYPE)i); + + if (renderer->stagingDescriptorPools[i] == NULL) { D3D12_INTERNAL_DestroyRenderer(renderer); return NULL; } @@ -8575,20 +8888,20 @@ static SDL_GPUDevice *D3D12_CreateDevice(bool debugMode, bool preferLowPower, SD // Initialize GPU descriptor heaps for (Uint32 i = 0; i < 2; i += 1) { - renderer->descriptorHeapPools[i].lock = SDL_CreateMutex(); - renderer->descriptorHeapPools[i].capacity = 4; - renderer->descriptorHeapPools[i].count = 4; - renderer->descriptorHeapPools[i].heaps = (D3D12DescriptorHeap **)SDL_calloc( - renderer->descriptorHeapPools[i].capacity, sizeof(D3D12DescriptorHeap *)); + renderer->gpuDescriptorHeapPools[i].lock = SDL_CreateMutex(); + renderer->gpuDescriptorHeapPools[i].capacity = 4; + renderer->gpuDescriptorHeapPools[i].count = 4; + renderer->gpuDescriptorHeapPools[i].heaps = (D3D12DescriptorHeap **)SDL_calloc( + renderer->gpuDescriptorHeapPools[i].capacity, sizeof(D3D12DescriptorHeap *)); - for (Uint32 j = 0; j < renderer->descriptorHeapPools[i].capacity; j += 1) { - renderer->descriptorHeapPools[i].heaps[j] = D3D12_INTERNAL_CreateDescriptorHeap( + for (Uint32 j = 0; j < renderer->gpuDescriptorHeapPools[i].capacity; j += 1) { + renderer->gpuDescriptorHeapPools[i].heaps[j] = D3D12_INTERNAL_CreateDescriptorHeap( renderer, (D3D12_DESCRIPTOR_HEAP_TYPE)i, i == D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV ? VIEW_GPU_DESCRIPTOR_COUNT : SAMPLER_GPU_DESCRIPTOR_COUNT, false); - if (renderer->descriptorHeapPools[i].heaps[j] == NULL) { + if (renderer->gpuDescriptorHeapPools[i].heaps[j] == NULL) { D3D12_INTERNAL_DestroyRenderer(renderer); return NULL; } @@ -8643,7 +8956,6 @@ static SDL_GPUDevice *D3D12_CreateDevice(bool debugMode, bool preferLowPower, SD } // Locks - renderer->stagingDescriptorHeapLock = SDL_CreateMutex(); renderer->acquireCommandBufferLock = SDL_CreateMutex(); renderer->acquireUniformBufferLock = SDL_CreateMutex(); renderer->submitLock = SDL_CreateMutex(); @@ -8652,12 +8964,35 @@ static SDL_GPUDevice *D3D12_CreateDevice(bool debugMode, bool preferLowPower, SD renderer->disposeLock = SDL_CreateMutex(); renderer->debug_mode = debugMode; + renderer->allowedFramesInFlight = 2; renderer->semantic = SDL_GetStringProperty(props, SDL_PROP_GPU_DEVICE_CREATE_D3D12_SEMANTIC_NAME_STRING, "TEXCOORD"); // Blit resources D3D12_INTERNAL_InitBlitResources(renderer); +#if (defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)) + res = renderer->device->SetFrameIntervalX( + NULL, + D3D12XBOX_FRAME_INTERVAL_60_HZ, + renderer->allowedFramesInFlight - 1, + D3D12XBOX_FRAME_INTERVAL_FLAG_NONE); + if (FAILED(res)) { + D3D12_INTERNAL_DestroyRenderer(renderer); + CHECK_D3D12_ERROR_AND_RETURN("Could not get set frame interval", NULL); + } + + res = renderer->device->ScheduleFrameEventX( + D3D12XBOX_FRAME_EVENT_ORIGIN, + 0, + NULL, + D3D12XBOX_SCHEDULE_FRAME_EVENT_FLAG_NONE); + if (FAILED(res)) { + D3D12_INTERNAL_DestroyRenderer(renderer); + CHECK_D3D12_ERROR_AND_RETURN("Could not schedule frame events", NULL); + } +#endif + // Create the SDL_GPU Device result = (SDL_GPUDevice *)SDL_calloc(1, sizeof(SDL_GPUDevice)); @@ -8676,7 +9011,7 @@ static SDL_GPUDevice *D3D12_CreateDevice(bool debugMode, bool preferLowPower, SD SDL_GPUBootstrap D3D12Driver = { "direct3d12", - SDL_GPU_SHADERFORMAT_DXIL, + SDL_GPU_SHADERFORMAT_DXIL | SDL_GPU_SHADERFORMAT_DXBC, D3D12_PrepareDriver, D3D12_CreateDevice }; @@ -8724,7 +9059,7 @@ void SDL_GDKResumeGPU(SDL_GPUDevice *device) res = renderer->device->SetFrameIntervalX( NULL, D3D12XBOX_FRAME_INTERVAL_60_HZ, - MAX_FRAMES_IN_FLIGHT - 1, + renderer->allowedFramesInFlight - 1, D3D12XBOX_FRAME_INTERVAL_FLAG_NONE); if (FAILED(res)) { SDL_LogError(SDL_LOG_CATEGORY_GPU, "Could not set frame interval: %X", res); diff --git a/libs/SDL3/src/gpu/d3d12/compile_shaders.bat b/libs/SDL3/src/gpu/d3d12/compile_shaders.bat index 6733fabd..7aa78ef7 100644 --- a/libs/SDL3/src/gpu/d3d12/compile_shaders.bat +++ b/libs/SDL3/src/gpu/d3d12/compile_shaders.bat @@ -8,11 +8,11 @@ echo Suffix %SUFFIX% cd "%~dp0" -%DXC% -E FullscreenVert -T vs_6_0 -Fh D3D12_FullscreenVert.h ..\d3dcommon\D3D_Blit.hlsl /D D3D12=1 -%DXC% -E BlitFrom2D -T ps_6_0 -Fh D3D12_BlitFrom2D.h ..\d3dcommon\D3D_Blit.hlsl /D D3D12=1 -%DXC% -E BlitFrom2DArray -T ps_6_0 -Fh D3D12_BlitFrom2DArray.h ..\d3dcommon\D3D_Blit.hlsl /D D3D12=1 -%DXC% -E BlitFrom3D -T ps_6_0 -Fh D3D12_BlitFrom3D.h ..\d3dcommon\D3D_Blit.hlsl /D D3D12=1 -%DXC% -E BlitFromCube -T ps_6_0 -Fh D3D12_BlitFromCube.h ..\d3dcommon\D3D_Blit.hlsl /D D3D12=1 -%DXC% -E BlitFromCubeArray -T ps_6_0 -Fh D3D12_BlitFromCubeArray.h ..\d3dcommon\D3D_Blit.hlsl /D D3D12=1 +%DXC% -E FullscreenVert -T vs_6_0 -Fh D3D12_FullscreenVert.h D3D_Blit.hlsl +%DXC% -E BlitFrom2D -T ps_6_0 -Fh D3D12_BlitFrom2D.h D3D_Blit.hlsl +%DXC% -E BlitFrom2DArray -T ps_6_0 -Fh D3D12_BlitFrom2DArray.h D3D_Blit.hlsl +%DXC% -E BlitFrom3D -T ps_6_0 -Fh D3D12_BlitFrom3D.h D3D_Blit.hlsl +%DXC% -E BlitFromCube -T ps_6_0 -Fh D3D12_BlitFromCube.h D3D_Blit.hlsl +%DXC% -E BlitFromCubeArray -T ps_6_0 -Fh D3D12_BlitFromCubeArray.h D3D_Blit.hlsl copy /b D3D12_FullscreenVert.h+D3D12_BlitFrom2D.h+D3D12_BlitFrom2DArray.h+D3D12_BlitFrom3D.h+D3D12_BlitFromCube.h+D3D12_BlitFromCubeArray.h D3D12_Blit%SUFFIX% del D3D12_FullscreenVert.h D3D12_BlitFrom2D.h D3D12_BlitFrom2DArray.h D3D12_BlitFrom3D.h D3D12_BlitFromCube.h D3D12_BlitFromCubeArray.h diff --git a/libs/SDL3/src/gpu/metal/SDL_gpu_metal.m b/libs/SDL3/src/gpu/metal/SDL_gpu_metal.m index 6c496a5c..d2a970a1 100644 --- a/libs/SDL3/src/gpu/metal/SDL_gpu_metal.m +++ b/libs/SDL3/src/gpu/metal/SDL_gpu_metal.m @@ -34,31 +34,35 @@ #define WINDOW_PROPERTY_DATA "SDL_GPUMetalWindowPropertyData" #define SDL_GPU_SHADERSTAGE_COMPUTE 2 -#define TRACK_RESOURCE(resource, type, array, count, capacity) \ - Uint32 i; \ - \ - for (i = 0; i < commandBuffer->count; i += 1) { \ - if (commandBuffer->array[i] == resource) { \ - return; \ - } \ - } \ - \ - if (commandBuffer->count == commandBuffer->capacity) { \ - commandBuffer->capacity += 1; \ - commandBuffer->array = SDL_realloc( \ - commandBuffer->array, \ - commandBuffer->capacity * sizeof(type)); \ - } \ - commandBuffer->array[commandBuffer->count] = resource; \ - commandBuffer->count += 1; \ - SDL_AtomicIncRef(&resource->referenceCount); +#define TRACK_RESOURCE(resource, type, array, count, capacity) \ + do { \ + Uint32 i; \ + \ + for (i = 0; i < commandBuffer->count; i += 1) { \ + if (commandBuffer->array[i] == (resource)) { \ + return; \ + } \ + } \ + \ + if (commandBuffer->count == commandBuffer->capacity) { \ + commandBuffer->capacity += 1; \ + commandBuffer->array = SDL_realloc( \ + commandBuffer->array, \ + commandBuffer->capacity * sizeof(type)); \ + } \ + commandBuffer->array[commandBuffer->count] = (resource); \ + commandBuffer->count += 1; \ + SDL_AtomicIncRef(&(resource)->referenceCount); \ + } while (0) -#define SET_ERROR_AND_RETURN(fmt, msg, ret) \ - if (renderer->debugMode) { \ - SDL_LogError(SDL_LOG_CATEGORY_GPU, fmt, msg); \ - } \ - SDL_SetError(fmt, msg); \ - return ret; \ +#define SET_ERROR_AND_RETURN(fmt, msg, ret) \ + do { \ + if (renderer->debugMode) { \ + SDL_LogError(SDL_LOG_CATEGORY_GPU, fmt, msg); \ + } \ + SDL_SetError(fmt, msg); \ + return ret; \ + } while (0) #define SET_STRING_ERROR_AND_RETURN(msg, ret) SET_ERROR_AND_RETURN("%s", msg, ret) @@ -641,6 +645,7 @@ struct MetalRenderer id queue; bool debugMode; + Uint32 allowedFramesInFlight; MetalWindowData **claimedWindows; Uint32 claimedWindowCount; @@ -3623,8 +3628,7 @@ static bool METAL_ClaimWindow( SET_STRING_ERROR_AND_RETURN("Could not create swapchain, failed to claim window", false); } } else { - SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "Window already claimed!"); - return false; + SET_ERROR_AND_RETURN("%s", "Window already claimed!", false); } } } @@ -3667,7 +3671,34 @@ static void METAL_ReleaseWindow( } } -static bool METAL_AcquireSwapchainTexture( +static bool METAL_WaitForSwapchain( + SDL_GPURenderer *driverData, + SDL_Window *window) +{ + @autoreleasepool { + MetalRenderer *renderer = (MetalRenderer *)driverData; + MetalWindowData *windowData = METAL_INTERNAL_FetchWindowData(window); + + if (windowData == NULL) { + SET_STRING_ERROR_AND_RETURN("Cannot wait for a swapchain from an unclaimed window!", false); + } + + if (windowData->inFlightFences[windowData->frameCounter] != NULL) { + if (!METAL_WaitForFences( + driverData, + true, + &windowData->inFlightFences[windowData->frameCounter], + 1)) { + return false; + } + } + + return true; + } +} + +static bool METAL_INTERNAL_AcquireSwapchainTexture( + bool block, SDL_GPUCommandBuffer *commandBuffer, SDL_Window *window, SDL_GPUTexture **texture, @@ -3705,8 +3736,8 @@ static bool METAL_AcquireSwapchainTexture( } if (windowData->inFlightFences[windowData->frameCounter] != NULL) { - if (windowData->presentMode == SDL_GPU_PRESENTMODE_VSYNC) { - // In VSYNC mode, block until the least recent presented frame is done + if (block) { + // If we are blocking, just wait for the fence! if (!METAL_WaitForFences( (SDL_GPURenderer *)renderer, true, @@ -3715,13 +3746,11 @@ static bool METAL_AcquireSwapchainTexture( return false; } } else { + // If we are not blocking and the least recent fence is not signaled, + // return true to indicate that there is no error but rendering should be skipped. if (!METAL_QueryFence( (SDL_GPURenderer *)metalCommandBuffer->renderer, windowData->inFlightFences[windowData->frameCounter])) { - /* - * In IMMEDIATE mode, if the least recent fence is not signaled, - * return true to indicate that there is no error but rendering should be skipped - */ return true; } } @@ -3753,6 +3782,38 @@ static bool METAL_AcquireSwapchainTexture( } } +static bool METAL_AcquireSwapchainTexture( + SDL_GPUCommandBuffer *command_buffer, + SDL_Window *window, + SDL_GPUTexture **swapchain_texture, + Uint32 *swapchain_texture_width, + Uint32 *swapchain_texture_height +) { + return METAL_INTERNAL_AcquireSwapchainTexture( + false, + command_buffer, + window, + swapchain_texture, + swapchain_texture_width, + swapchain_texture_height); +} + +static bool METAL_WaitAndAcquireSwapchainTexture( + SDL_GPUCommandBuffer *command_buffer, + SDL_Window *window, + SDL_GPUTexture **swapchain_texture, + Uint32 *swapchain_texture_width, + Uint32 *swapchain_texture_height +) { + return METAL_INTERNAL_AcquireSwapchainTexture( + true, + command_buffer, + window, + swapchain_texture, + swapchain_texture_width, + swapchain_texture_height); +} + static SDL_GPUTextureFormat METAL_GetSwapchainTextureFormat( SDL_GPURenderer *driverData, SDL_Window *window) @@ -3817,6 +3878,22 @@ static bool METAL_SetSwapchainParameters( } } +static bool METAL_SetAllowedFramesInFlight( + SDL_GPURenderer *driverData, + Uint32 allowedFramesInFlight) +{ + @autoreleasepool { + MetalRenderer *renderer = (MetalRenderer *)driverData; + + if (!METAL_Wait(driverData)) { + return false; + } + + renderer->allowedFramesInFlight = allowedFramesInFlight; + return true; + } +} + // Submission static bool METAL_Submit( @@ -3843,7 +3920,7 @@ static bool METAL_Submit( (void)SDL_AtomicIncRef(&metalCommandBuffer->fence->referenceCount); - windowData->frameCounter = (windowData->frameCounter + 1) % MAX_FRAMES_IN_FLIGHT; + windowData->frameCounter = (windowData->frameCounter + 1) % renderer->allowedFramesInFlight; } // Notify the fence when the command buffer has completed @@ -4103,7 +4180,7 @@ static bool METAL_SupportsTextureFormat( static bool METAL_PrepareDriver(SDL_VideoDevice *this) { - if (@available(macOS 10.13, iOS 13.0, tvOS 13.0, *)) { + if (@available(macOS 10.14, iOS 13.0, tvOS 13.0, *)) { return (this->Metal_CreateView != NULL); } return false; @@ -4265,6 +4342,16 @@ static SDL_GPUDevice *METAL_CreateDevice(bool debugMode, bool preferLowPower, SD @autoreleasepool { MetalRenderer *renderer; id device = NULL; + bool hasHardwareSupport = false; + + if (debugMode) { + /* Due to a Metal driver quirk, once a MTLDevice has been created + * with this environment variable set, the Metal validation layers + * will remain enabled for the rest of the application's lifespan, + * even if the device is destroyed and recreated. + */ + SDL_setenv_unsafe("MTL_DEBUG_LAYER", "1", 0); + } // Create the Metal device and command queue #ifdef SDL_PLATFORM_MACOS @@ -4286,6 +4373,23 @@ static SDL_GPUDevice *METAL_CreateDevice(bool debugMode, bool preferLowPower, SD } } +#ifdef SDL_PLATFORM_MACOS + if (@available(macOS 10.15, *)) { + hasHardwareSupport = [device supportsFamily:MTLGPUFamilyMac2]; + } else if (@available(macOS 10.14, *)) { + hasHardwareSupport = [device supportsFeatureSet:MTLFeatureSet_macOS_GPUFamily2_v1]; + } +#else + if (@available(iOS 13.0, tvOS 13.0, *)) { + hasHardwareSupport = [device supportsFamily:MTLGPUFamilyApple3]; + } +#endif + + if (!hasHardwareSupport) { + SDL_SetError("Device does not meet the hardware requirements for SDL_GPU Metal"); + return NULL; + } + // Allocate and zero out the renderer renderer = (MetalRenderer *)SDL_calloc(1, sizeof(MetalRenderer)); @@ -4301,6 +4405,7 @@ static SDL_GPUDevice *METAL_CreateDevice(bool debugMode, bool preferLowPower, SD // Remember debug mode renderer->debugMode = debugMode; + renderer->allowedFramesInFlight = 2; // Set up colorspace array SwapchainCompositionToColorSpace[0] = kCGColorSpaceSRGB; diff --git a/libs/SDL3/src/gpu/vulkan/SDL_gpu_vulkan.c b/libs/SDL3/src/gpu/vulkan/SDL_gpu_vulkan.c index 29951c60..0e6f505a 100644 --- a/libs/SDL3/src/gpu/vulkan/SDL_gpu_vulkan.c +++ b/libs/SDL3/src/gpu/vulkan/SDL_gpu_vulkan.c @@ -86,31 +86,27 @@ typedef struct VulkanExtensions #define NULL_RENDER_PASS (SDL_GPURenderPass *)0 #define EXPAND_ELEMENTS_IF_NEEDED(arr, initialValue, type) \ - if (arr->count == arr->capacity) { \ - if (arr->capacity == 0) { \ - arr->capacity = initialValue; \ - } else { \ - arr->capacity *= 2; \ + do { \ + if (arr->count == arr->capacity) { \ + if (arr->capacity == 0) { \ + arr->capacity = initialValue; \ + } else { \ + arr->capacity *= 2; \ + } \ + arr->elements = (type *)SDL_realloc( \ + arr->elements, \ + arr->capacity * sizeof(type)); \ } \ - arr->elements = (type *)SDL_realloc( \ - arr->elements, \ - arr->capacity * sizeof(type)); \ - } - -#define EXPAND_ARRAY_IF_NEEDED(arr, elementType, newCount, capacity, newCapacity) \ - if (newCount >= capacity) { \ - capacity = newCapacity; \ - arr = (elementType *)SDL_realloc( \ - arr, \ - sizeof(elementType) * capacity); \ - } + } while (0) #define MOVE_ARRAY_CONTENTS_AND_RESET(i, dstArr, dstCount, srcArr, srcCount) \ - for (i = 0; i < srcCount; i += 1) { \ - dstArr[i] = srcArr[i]; \ - } \ - dstCount = srcCount; \ - srcCount = 0; + do { \ + for ((i) = 0; (i) < (srcCount); (i) += 1) { \ + (dstArr)[i] = (srcArr)[i]; \ + } \ + (dstCount) = (srcCount); \ + (srcCount) = 0; \ + while (0) // Conversions @@ -1123,6 +1119,8 @@ struct VulkanRenderer bool debugMode; bool preferLowPower; + Uint32 allowedFramesInFlight; + VulkanExtensions supports; bool supportsDebugUtils; bool supportsColorspace; @@ -1249,29 +1247,34 @@ static inline const char *VkErrorMessages(VkResult code) ERR_TO_STR(VK_ERROR_SURFACE_LOST_KHR) ERR_TO_STR(VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT) ERR_TO_STR(VK_SUBOPTIMAL_KHR) + ERR_TO_STR(VK_ERROR_NATIVE_WINDOW_IN_USE_KHR) default: return "Unhandled VkResult!"; } #undef ERR_TO_STR } -#define SET_ERROR_AND_RETURN(fmt, msg, ret) \ - if (renderer->debugMode) { \ - SDL_LogError(SDL_LOG_CATEGORY_GPU, fmt, msg); \ - } \ - SDL_SetError(fmt, msg); \ - return ret; \ +#define SET_ERROR_AND_RETURN(fmt, msg, ret) \ + do { \ + if (renderer->debugMode) { \ + SDL_LogError(SDL_LOG_CATEGORY_GPU, fmt, msg); \ + } \ + SDL_SetError((fmt), (msg)); \ + return ret; \ + } while (0) #define SET_STRING_ERROR_AND_RETURN(msg, ret) SET_ERROR_AND_RETURN("%s", msg, ret) -#define CHECK_VULKAN_ERROR_AND_RETURN(res, fn, ret) \ - if (res != VK_SUCCESS) { \ - if (renderer->debugMode) { \ - SDL_LogError(SDL_LOG_CATEGORY_GPU, "%s %s", #fn, VkErrorMessages(res)); \ - } \ - SDL_SetError("%s %s", #fn, VkErrorMessages(res)); \ - return ret; \ - } +#define CHECK_VULKAN_ERROR_AND_RETURN(res, fn, ret) \ + do { \ + if ((res) != VK_SUCCESS) { \ + if (renderer->debugMode) { \ + SDL_LogError(SDL_LOG_CATEGORY_GPU, "%s %s", #fn, VkErrorMessages(res)); \ + } \ + SDL_SetError("%s %s", #fn, VkErrorMessages(res)); \ + return (ret); \ + } \ + } while (0) // Utility @@ -1851,7 +1854,7 @@ static Uint8 VULKAN_INTERNAL_AllocateMemory( VK_WHOLE_SIZE, 0, (void **)&allocation->mapPointer); - CHECK_VULKAN_ERROR_AND_RETURN(result, vkMapMemory, 0) + CHECK_VULKAN_ERROR_AND_RETURN(result, vkMapMemory, 0); } else { allocation->mapPointer = NULL; } @@ -1884,7 +1887,7 @@ static Uint8 VULKAN_INTERNAL_BindBufferMemory( SDL_UnlockMutex(usedRegion->allocation->memoryLock); - CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkBindBufferMemory, 0) + CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkBindBufferMemory, 0); return 1; } @@ -1907,7 +1910,7 @@ static Uint8 VULKAN_INTERNAL_BindImageMemory( SDL_UnlockMutex(usedRegion->allocation->memoryLock); - CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkBindImageMemory, 0) + CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkBindImageMemory, 0); return 1; } @@ -2894,7 +2897,7 @@ static void VULKAN_INTERNAL_ReleaseFramebuffer( VulkanFramebuffer *, renderer->framebuffersToDestroyCount + 1, renderer->framebuffersToDestroyCapacity, - renderer->framebuffersToDestroyCapacity * 2) + renderer->framebuffersToDestroyCapacity * 2); renderer->framebuffersToDestroy[renderer->framebuffersToDestroyCount] = framebuffer; renderer->framebuffersToDestroyCount += 1; @@ -3603,7 +3606,7 @@ static bool VULKAN_INTERNAL_AllocateDescriptorSets( SDL_stack_free(descriptorSetLayouts); - CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkAllocateDescriptorSets, false) + CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkAllocateDescriptorSets, false); return true; } @@ -3676,7 +3679,7 @@ static bool VULKAN_INTERNAL_AllocateDescriptorsFromPool( NULL, &pool); - CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateDescriptorPool, false) + CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateDescriptorPool, false); descriptorSetPool->poolCount += 1; descriptorSetPool->descriptorPools = SDL_realloc( @@ -3815,7 +3818,7 @@ static DescriptorSetLayout *VULKAN_INTERNAL_FetchDescriptorSetLayout( NULL, &descriptorSetLayout); - CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateDescriptorSetLayout, NULL) + CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateDescriptorSetLayout, NULL); layout = SDL_malloc(sizeof(DescriptorSetLayout)); layout->descriptorSetLayout = descriptorSetLayout; @@ -3943,7 +3946,7 @@ static VulkanGraphicsPipelineResourceLayout *VULKAN_INTERNAL_FetchGraphicsPipeli if (vulkanResult != VK_SUCCESS) { VULKAN_INTERNAL_DestroyGraphicsPipelineResourceLayout(renderer, pipelineResourceLayout); - CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreatePipelineLayout, NULL) + CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreatePipelineLayout, NULL); } GraphicsPipelineResourceLayoutHashTableKey *allocedKey = SDL_malloc(sizeof(GraphicsPipelineResourceLayoutHashTableKey)); @@ -4044,7 +4047,7 @@ static VulkanComputePipelineResourceLayout *VULKAN_INTERNAL_FetchComputePipeline if (vulkanResult != VK_SUCCESS) { VULKAN_INTERNAL_DestroyComputePipelineResourceLayout(renderer, pipelineResourceLayout); - CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreatePipelineLayout, NULL) + CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreatePipelineLayout, NULL); } ComputePipelineResourceLayoutHashTableKey *allocedKey = SDL_malloc(sizeof(ComputePipelineResourceLayoutHashTableKey)); @@ -4126,7 +4129,7 @@ static VulkanBuffer *VULKAN_INTERNAL_CreateBuffer( if (vulkanResult != VK_SUCCESS) { SDL_free(buffer); - CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateBuffer, NULL) + CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateBuffer, NULL); } bindResult = VULKAN_INTERNAL_BindMemoryForBuffer( @@ -4249,7 +4252,7 @@ static bool VULKAN_INTERNAL_CreateRenderTargetView( if (vulkanResult != VK_SUCCESS) { *pView = (VkImageView)VK_NULL_HANDLE; - CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateImageView, false) + CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateImageView, false); } return true; @@ -4289,7 +4292,7 @@ static bool VULKAN_INTERNAL_CreateSubresourceView( if (vulkanResult != VK_SUCCESS) { *pView = (VkImageView)VK_NULL_HANDLE; - CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateImageView, false) + CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateImageView, false); } return true; @@ -4317,8 +4320,7 @@ static bool VULKAN_INTERNAL_QuerySwapchainSupport( outputDetails->presentModesLength = 0; if (!supportsPresent) { - SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "This surface does not support presenting!"); - return false; + SET_STRING_ERROR_AND_RETURN("This surface does not support presenting!", false); } // Run the device surface queries @@ -4326,7 +4328,7 @@ static bool VULKAN_INTERNAL_QuerySwapchainSupport( physicalDevice, surface, &outputDetails->capabilities); - CHECK_VULKAN_ERROR_AND_RETURN(result, vkGetPhysicalDeviceSurfaceCapabilitiesKHR, false) + CHECK_VULKAN_ERROR_AND_RETURN(result, vkGetPhysicalDeviceSurfaceCapabilitiesKHR, false); if (!(outputDetails->capabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR)) { SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "Opaque presentation unsupported! Expect weird transparency bugs!"); @@ -4337,13 +4339,13 @@ static bool VULKAN_INTERNAL_QuerySwapchainSupport( surface, &outputDetails->formatsLength, NULL); - CHECK_VULKAN_ERROR_AND_RETURN(result, vkGetPhysicalDeviceSurfaceFormatsKHR, false) + CHECK_VULKAN_ERROR_AND_RETURN(result, vkGetPhysicalDeviceSurfaceFormatsKHR, false); result = renderer->vkGetPhysicalDeviceSurfacePresentModesKHR( physicalDevice, surface, &outputDetails->presentModesLength, NULL); - CHECK_VULKAN_ERROR_AND_RETURN(result, vkGetPhysicalDeviceSurfacePresentModesKHR, false) + CHECK_VULKAN_ERROR_AND_RETURN(result, vkGetPhysicalDeviceSurfacePresentModesKHR, false); // Generate the arrays, if applicable @@ -4352,8 +4354,8 @@ static bool VULKAN_INTERNAL_QuerySwapchainSupport( outputDetails->formats = (VkSurfaceFormatKHR *)SDL_malloc( sizeof(VkSurfaceFormatKHR) * outputDetails->formatsLength); - if (!outputDetails->formats) { - return 0; + if (!outputDetails->formats) { // OOM + return false; } result = renderer->vkGetPhysicalDeviceSurfaceFormatsKHR( @@ -4363,7 +4365,7 @@ static bool VULKAN_INTERNAL_QuerySwapchainSupport( outputDetails->formats); if (result != VK_SUCCESS) { SDL_free(outputDetails->formats); - CHECK_VULKAN_ERROR_AND_RETURN(result, vkGetPhysicalDeviceSurfaceFormatsKHR, false) + CHECK_VULKAN_ERROR_AND_RETURN(result, vkGetPhysicalDeviceSurfaceFormatsKHR, false); } } @@ -4372,7 +4374,7 @@ static bool VULKAN_INTERNAL_QuerySwapchainSupport( outputDetails->presentModes = (VkPresentModeKHR *)SDL_malloc( sizeof(VkPresentModeKHR) * outputDetails->presentModesLength); - if (!outputDetails->presentModes) { + if (!outputDetails->presentModes) { // OOM SDL_free(outputDetails->formats); return false; } @@ -4385,7 +4387,7 @@ static bool VULKAN_INTERNAL_QuerySwapchainSupport( if (result != VK_SUCCESS) { SDL_free(outputDetails->formats); SDL_free(outputDetails->presentModes); - CHECK_VULKAN_ERROR_AND_RETURN(result, vkGetPhysicalDeviceSurfacePresentModesKHR, false) + CHECK_VULKAN_ERROR_AND_RETURN(result, vkGetPhysicalDeviceSurfacePresentModesKHR, false); } } @@ -4459,10 +4461,6 @@ static Uint32 VULKAN_INTERNAL_CreateSwapchain( renderer->instance, NULL, // FIXME: VAllocationCallbacks &windowData->surface)) { - SDL_LogError( - SDL_LOG_CATEGORY_GPU, - "Vulkan_CreateSurface failed: %s", - SDL_GetError()); return false; } @@ -4554,7 +4552,7 @@ static Uint32 VULKAN_INTERNAL_CreateSwapchain( return VULKAN_INTERNAL_TRY_AGAIN; } - windowData->imageCount = MAX_FRAMES_IN_FLIGHT; + Uint32 requestedImageCount = renderer->allowedFramesInFlight; #ifdef SDL_PLATFORM_APPLE windowData->width = swapchainSupportDetails.capabilities.currentExtent.width; @@ -4570,12 +4568,12 @@ static Uint32 VULKAN_INTERNAL_CreateSwapchain( #endif if (swapchainSupportDetails.capabilities.maxImageCount > 0 && - windowData->imageCount > swapchainSupportDetails.capabilities.maxImageCount) { - windowData->imageCount = swapchainSupportDetails.capabilities.maxImageCount; + requestedImageCount > swapchainSupportDetails.capabilities.maxImageCount) { + requestedImageCount = swapchainSupportDetails.capabilities.maxImageCount; } - if (windowData->imageCount < swapchainSupportDetails.capabilities.minImageCount) { - windowData->imageCount = swapchainSupportDetails.capabilities.minImageCount; + if (requestedImageCount < swapchainSupportDetails.capabilities.minImageCount) { + requestedImageCount = swapchainSupportDetails.capabilities.minImageCount; } if (windowData->presentMode == SDL_GPU_PRESENTMODE_MAILBOX) { @@ -4585,14 +4583,14 @@ static Uint32 VULKAN_INTERNAL_CreateSwapchain( * images, it's not real mailbox support, so let it fail hard. * -flibit */ - windowData->imageCount = SDL_max(windowData->imageCount, 3); + requestedImageCount = SDL_max(requestedImageCount, 3); } swapchainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; swapchainCreateInfo.pNext = NULL; swapchainCreateInfo.flags = 0; swapchainCreateInfo.surface = windowData->surface; - swapchainCreateInfo.minImageCount = windowData->imageCount; + swapchainCreateInfo.minImageCount = requestedImageCount; swapchainCreateInfo.imageFormat = windowData->format; swapchainCreateInfo.imageColorSpace = windowData->colorSpace; swapchainCreateInfo.imageExtent.width = windowData->width; @@ -4629,7 +4627,7 @@ static Uint32 VULKAN_INTERNAL_CreateSwapchain( windowData->surface, NULL); windowData->surface = VK_NULL_HANDLE; - CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateSwapchainKHR, false) + CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateSwapchainKHR, false); } vulkanResult = renderer->vkGetSwapchainImagesKHR( @@ -4637,12 +4635,12 @@ static Uint32 VULKAN_INTERNAL_CreateSwapchain( windowData->swapchain, &windowData->imageCount, NULL); - CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkGetSwapchainImagesKHR, false) + CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkGetSwapchainImagesKHR, false); windowData->textureContainers = SDL_malloc( sizeof(VulkanTextureContainer) * windowData->imageCount); - if (!windowData->textureContainers) { + if (!windowData->textureContainers) { // OOM renderer->vkDestroySurfaceKHR( renderer->instance, windowData->surface, @@ -4663,7 +4661,7 @@ static Uint32 VULKAN_INTERNAL_CreateSwapchain( windowData->swapchain, &windowData->imageCount, swapchainImages); - CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkGetSwapchainImagesKHR, false) + CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkGetSwapchainImagesKHR, false); for (i = 0; i < windowData->imageCount; i += 1) { @@ -4747,7 +4745,7 @@ static Uint32 VULKAN_INTERNAL_CreateSwapchain( NULL); windowData->surface = VK_NULL_HANDLE; windowData->swapchain = VK_NULL_HANDLE; - CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateSemaphore, false) + CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateSemaphore, false); } renderer->vkCreateSemaphore( @@ -4767,7 +4765,7 @@ static Uint32 VULKAN_INTERNAL_CreateSwapchain( NULL); windowData->surface = VK_NULL_HANDLE; windowData->swapchain = VK_NULL_HANDLE; - CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateSemaphore, false) + CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateSemaphore, false); } windowData->inFlightFences[i] = NULL; @@ -4796,7 +4794,7 @@ static bool VULKAN_INTERNAL_BeginCommandBuffer( commandBuffer->commandBuffer, &beginInfo); - CHECK_VULKAN_ERROR_AND_RETURN(result, vkBeginCommandBuffer, false) + CHECK_VULKAN_ERROR_AND_RETURN(result, vkBeginCommandBuffer, false); return true; } @@ -4808,7 +4806,7 @@ static bool VULKAN_INTERNAL_EndCommandBuffer( VkResult result = renderer->vkEndCommandBuffer( commandBuffer->commandBuffer); - CHECK_VULKAN_ERROR_AND_RETURN(result, vkEndCommandBuffer, false) + CHECK_VULKAN_ERROR_AND_RETURN(result, vkEndCommandBuffer, false); return true; } @@ -4942,7 +4940,7 @@ static void VULKAN_INTERNAL_ReturnDescriptorSetCacheToPool( DescriptorSetCache *, renderer->descriptorSetCachePoolCount + 1, renderer->descriptorSetCachePoolCapacity, - renderer->descriptorSetCachePoolCapacity * 2) + renderer->descriptorSetCachePoolCapacity * 2); renderer->descriptorSetCachePool[renderer->descriptorSetCachePoolCount] = descriptorSetCache; renderer->descriptorSetCachePoolCount += 1; @@ -5625,7 +5623,7 @@ static VulkanTexture *VULKAN_INTERNAL_CreateTexture( if (vulkanResult != VK_SUCCESS) { VULKAN_INTERNAL_DestroyTexture(renderer, texture); - CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateImage, NULL) + CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateImage, NULL); } bindResult = VULKAN_INTERNAL_BindMemoryForImage( @@ -5640,7 +5638,7 @@ static VulkanTexture *VULKAN_INTERNAL_CreateTexture( NULL); VULKAN_INTERNAL_DestroyTexture(renderer, texture); - SET_STRING_ERROR_AND_RETURN("Unable to bind memory for texture!", NULL) + SET_STRING_ERROR_AND_RETURN("Unable to bind memory for texture!", NULL); } texture->usedRegion->vulkanTexture = texture; // lol @@ -5679,7 +5677,7 @@ static VulkanTexture *VULKAN_INTERNAL_CreateTexture( if (vulkanResult != VK_SUCCESS) { VULKAN_INTERNAL_DestroyTexture(renderer, texture); - CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, "vkCreateImageView", NULL) + CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, "vkCreateImageView", NULL); } } @@ -6140,7 +6138,7 @@ static VkRenderPass VULKAN_INTERNAL_CreateTransientRenderPass( NULL, &renderPass); - CHECK_VULKAN_ERROR_AND_RETURN(result, vkCreateRenderPass, VK_NULL_HANDLE) + CHECK_VULKAN_ERROR_AND_RETURN(result, vkCreateRenderPass, VK_NULL_HANDLE); return renderPass; } @@ -6471,7 +6469,7 @@ static SDL_GPUGraphicsPipeline *VULKAN_CreateGraphicsPipeline( if (vulkanResult != VK_SUCCESS) { SDL_free(graphicsPipeline); - CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateGraphicsPipelines, NULL) + CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateGraphicsPipelines, NULL); } SDL_SetAtomicInt(&graphicsPipeline->referenceCount, 0); @@ -6491,7 +6489,7 @@ static SDL_GPUComputePipeline *VULKAN_CreateComputePipeline( VulkanComputePipeline *vulkanComputePipeline; if (createinfo->format != SDL_GPU_SHADERFORMAT_SPIRV) { - SET_STRING_ERROR_AND_RETURN("Incompatible shader format for Vulkan!", NULL) + SET_STRING_ERROR_AND_RETURN("Incompatible shader format for Vulkan!", NULL); } vulkanComputePipeline = SDL_malloc(sizeof(VulkanComputePipeline)); @@ -6509,7 +6507,7 @@ static SDL_GPUComputePipeline *VULKAN_CreateComputePipeline( if (vulkanResult != VK_SUCCESS) { SDL_free(vulkanComputePipeline); - CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateShaderModule, NULL) + CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateShaderModule, NULL); } pipelineShaderStageCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; @@ -6551,7 +6549,7 @@ static SDL_GPUComputePipeline *VULKAN_CreateComputePipeline( if (vulkanResult != VK_SUCCESS) { VULKAN_INTERNAL_DestroyComputePipeline(renderer, vulkanComputePipeline); - CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateComputePipeline, NULL) + CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateComputePipeline, NULL); return NULL; } @@ -6596,7 +6594,7 @@ static SDL_GPUSampler *VULKAN_CreateSampler( if (vulkanResult != VK_SUCCESS) { SDL_free(vulkanSampler); - CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateSampler, NULL) + CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateSampler, NULL); } SDL_SetAtomicInt(&vulkanSampler->referenceCount, 0); @@ -6629,7 +6627,7 @@ static SDL_GPUShader *VULKAN_CreateShader( if (vulkanResult != VK_SUCCESS) { SDL_free(vulkanShader); - CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateShaderModule, NULL) + CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateShaderModule, NULL); } entryPointNameLength = SDL_strlen(createinfo->entrypoint) + 1; @@ -6754,7 +6752,7 @@ static void VULKAN_INTERNAL_ReleaseTexture( VulkanTexture *, renderer->texturesToDestroyCount + 1, renderer->texturesToDestroyCapacity, - renderer->texturesToDestroyCapacity * 2) + renderer->texturesToDestroyCapacity * 2); renderer->texturesToDestroy[renderer->texturesToDestroyCount] = vulkanTexture; renderer->texturesToDestroyCount += 1; @@ -6802,7 +6800,7 @@ static void VULKAN_ReleaseSampler( VulkanSampler *, renderer->samplersToDestroyCount + 1, renderer->samplersToDestroyCapacity, - renderer->samplersToDestroyCapacity * 2) + renderer->samplersToDestroyCapacity * 2); renderer->samplersToDestroy[renderer->samplersToDestroyCount] = vulkanSampler; renderer->samplersToDestroyCount += 1; @@ -6825,7 +6823,7 @@ static void VULKAN_INTERNAL_ReleaseBuffer( VulkanBuffer *, renderer->buffersToDestroyCount + 1, renderer->buffersToDestroyCapacity, - renderer->buffersToDestroyCapacity * 2) + renderer->buffersToDestroyCapacity * 2); renderer->buffersToDestroy[renderer->buffersToDestroyCount] = vulkanBuffer; renderer->buffersToDestroyCount += 1; @@ -6895,7 +6893,7 @@ static void VULKAN_ReleaseShader( VulkanShader *, renderer->shadersToDestroyCount + 1, renderer->shadersToDestroyCapacity, - renderer->shadersToDestroyCapacity * 2) + renderer->shadersToDestroyCapacity * 2); renderer->shadersToDestroy[renderer->shadersToDestroyCount] = vulkanShader; renderer->shadersToDestroyCount += 1; @@ -6917,7 +6915,7 @@ static void VULKAN_ReleaseComputePipeline( VulkanComputePipeline *, renderer->computePipelinesToDestroyCount + 1, renderer->computePipelinesToDestroyCapacity, - renderer->computePipelinesToDestroyCapacity * 2) + renderer->computePipelinesToDestroyCapacity * 2); renderer->computePipelinesToDestroy[renderer->computePipelinesToDestroyCount] = vulkanComputePipeline; renderer->computePipelinesToDestroyCount += 1; @@ -6939,7 +6937,7 @@ static void VULKAN_ReleaseGraphicsPipeline( VulkanGraphicsPipeline *, renderer->graphicsPipelinesToDestroyCount + 1, renderer->graphicsPipelinesToDestroyCapacity, - renderer->graphicsPipelinesToDestroyCapacity * 2) + renderer->graphicsPipelinesToDestroyCapacity * 2); renderer->graphicsPipelinesToDestroy[renderer->graphicsPipelinesToDestroyCount] = vulkanGraphicsPipeline; renderer->graphicsPipelinesToDestroyCount += 1; @@ -7182,7 +7180,7 @@ static VulkanFramebuffer *VULKAN_INTERNAL_FetchFramebuffer( SDL_UnlockMutex(renderer->framebufferFetchLock); } else { SDL_free(vulkanFramebuffer); - CHECK_VULKAN_ERROR_AND_RETURN(result, vkCreateFramebuffer, NULL) + CHECK_VULKAN_ERROR_AND_RETURN(result, vkCreateFramebuffer, NULL); } return vulkanFramebuffer; @@ -9107,7 +9105,7 @@ static bool VULKAN_INTERNAL_AllocateCommandBuffer( &allocateInfo, &commandBufferHandle); - CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkAllocateCommandBuffers, false) + CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkAllocateCommandBuffers, false); commandBuffer = SDL_malloc(sizeof(VulkanCommandBuffer)); commandBuffer->renderer = renderer; @@ -9235,7 +9233,7 @@ static VulkanCommandPool *VULKAN_INTERNAL_FetchCommandPool( if (vulkanResult != VK_SUCCESS) { SDL_free(vulkanCommandPool); - CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateCommandPool, NULL) + CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateCommandPool, NULL); return NULL; } @@ -9377,7 +9375,7 @@ static SDL_GPUCommandBuffer *VULKAN_AcquireCommandBuffer( commandBuffer->commandBuffer, VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT); - CHECK_VULKAN_ERROR_AND_RETURN(result, vkResetCommandBuffer, NULL) + CHECK_VULKAN_ERROR_AND_RETURN(result, vkResetCommandBuffer, NULL); if (!VULKAN_INTERNAL_BeginCommandBuffer(renderer, commandBuffer)) { return NULL; @@ -9402,7 +9400,7 @@ static bool VULKAN_QueryFence( } else if (result == VK_NOT_READY) { return false; } else { - SET_ERROR_AND_RETURN("vkGetFenceStatus: %s", VkErrorMessages(result), false) + SET_ERROR_AND_RETURN("vkGetFenceStatus: %s", VkErrorMessages(result), false); } } @@ -9469,7 +9467,7 @@ static bool VULKAN_SupportsSwapchainComposition( bool result = false; if (windowData == NULL) { - SET_STRING_ERROR_AND_RETURN("Must claim window before querying swapchain composition support!", false) + SET_STRING_ERROR_AND_RETURN("Must claim window before querying swapchain composition support!", false); } surface = windowData->surface; @@ -9514,7 +9512,7 @@ static bool VULKAN_SupportsPresentMode( bool result = false; if (windowData == NULL) { - SET_STRING_ERROR_AND_RETURN("Must claim window before querying present mode support!", false) + SET_STRING_ERROR_AND_RETURN("Must claim window before querying present mode support!", false); } surface = windowData->surface; @@ -9587,8 +9585,7 @@ static bool VULKAN_ClaimWindow( return false; } } else { - SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "Window already claimed!"); - return false; + SET_STRING_ERROR_AND_RETURN("Window already claimed!", false); } } @@ -9658,7 +9655,32 @@ static Uint32 VULKAN_INTERNAL_RecreateSwapchain( return VULKAN_INTERNAL_CreateSwapchain(renderer, windowData); } -static bool VULKAN_AcquireSwapchainTexture( +static bool VULKAN_WaitForSwapchain( + SDL_GPURenderer *driverData, + SDL_Window *window) +{ + VulkanRenderer *renderer = (VulkanRenderer *)driverData; + WindowData *windowData = VULKAN_INTERNAL_FetchWindowData(window); + + if (windowData == NULL) { + SET_STRING_ERROR_AND_RETURN("Cannot wait for a swapchain from an unclaimed window!", false); + } + + if (windowData->inFlightFences[windowData->frameCounter] != NULL) { + if (!VULKAN_WaitForFences( + driverData, + true, + &windowData->inFlightFences[windowData->frameCounter], + 1)) { + return false; + } + } + + return true; +} + +static bool VULKAN_INTERNAL_AcquireSwapchainTexture( + bool block, SDL_GPUCommandBuffer *commandBuffer, SDL_Window *window, SDL_GPUTexture **swapchainTexture, @@ -9683,7 +9705,7 @@ static bool VULKAN_AcquireSwapchainTexture( windowData = VULKAN_INTERNAL_FetchWindowData(window); if (windowData == NULL) { - SET_STRING_ERROR_AND_RETURN("Cannot acquire a swapchain texture from an unclaimed window!", false) + SET_STRING_ERROR_AND_RETURN("Cannot acquire a swapchain texture from an unclaimed window!", false); } // If window data marked as needing swapchain recreate, try to recreate @@ -9711,8 +9733,8 @@ static bool VULKAN_AcquireSwapchainTexture( } if (windowData->inFlightFences[windowData->frameCounter] != NULL) { - if (windowData->presentMode == SDL_GPU_PRESENTMODE_VSYNC) { - // In VSYNC mode, block until the least recent presented frame is done + if (block) { + // If we are blocking, just wait for the fence! if (!VULKAN_WaitForFences( (SDL_GPURenderer *)renderer, true, @@ -9721,13 +9743,11 @@ static bool VULKAN_AcquireSwapchainTexture( return false; } } else { + // If we are not blocking and the least recent fence is not signaled, + // return true to indicate that there is no error but rendering should be skipped. if (!VULKAN_QueryFence( (SDL_GPURenderer *)renderer, windowData->inFlightFences[windowData->frameCounter])) { - /* - * In MAILBOX or IMMEDIATE mode, if the least recent fence is not signaled, - * return true to indicate that there is no error but rendering should be skipped - */ return true; } } @@ -9846,6 +9866,38 @@ static bool VULKAN_AcquireSwapchainTexture( return true; } +static bool VULKAN_AcquireSwapchainTexture( + SDL_GPUCommandBuffer *command_buffer, + SDL_Window *window, + SDL_GPUTexture **swapchain_texture, + Uint32 *swapchain_texture_width, + Uint32 *swapchain_texture_height +) { + return VULKAN_INTERNAL_AcquireSwapchainTexture( + false, + command_buffer, + window, + swapchain_texture, + swapchain_texture_width, + swapchain_texture_height); +} + +static bool VULKAN_WaitAndAcquireSwapchainTexture( + SDL_GPUCommandBuffer *command_buffer, + SDL_Window *window, + SDL_GPUTexture **swapchain_texture, + Uint32 *swapchain_texture_width, + Uint32 *swapchain_texture_height +) { + return VULKAN_INTERNAL_AcquireSwapchainTexture( + true, + command_buffer, + window, + swapchain_texture, + swapchain_texture_width, + swapchain_texture_height); +} + static SDL_GPUTextureFormat VULKAN_GetSwapchainTextureFormat( SDL_GPURenderer *driverData, SDL_Window *window) @@ -9854,7 +9906,7 @@ static SDL_GPUTextureFormat VULKAN_GetSwapchainTextureFormat( WindowData *windowData = VULKAN_INTERNAL_FetchWindowData(window); if (windowData == NULL) { - SET_STRING_ERROR_AND_RETURN("Cannot get swapchain format, window has not been claimed!", SDL_GPU_TEXTUREFORMAT_INVALID) + SET_STRING_ERROR_AND_RETURN("Cannot get swapchain format, window has not been claimed!", SDL_GPU_TEXTUREFORMAT_INVALID); } return SwapchainCompositionToSDLFormat( @@ -9872,15 +9924,15 @@ static bool VULKAN_SetSwapchainParameters( WindowData *windowData = VULKAN_INTERNAL_FetchWindowData(window); if (windowData == NULL) { - SET_STRING_ERROR_AND_RETURN("Cannot set swapchain parameters on unclaimed window!", false) + SET_STRING_ERROR_AND_RETURN("Cannot set swapchain parameters on unclaimed window!", false); } if (!VULKAN_SupportsSwapchainComposition(driverData, window, swapchainComposition)) { - SET_STRING_ERROR_AND_RETURN("Swapchain composition not supported!", false) + SET_STRING_ERROR_AND_RETURN("Swapchain composition not supported!", false); } if (!VULKAN_SupportsPresentMode(driverData, window, presentMode)) { - SET_STRING_ERROR_AND_RETURN("Present mode not supported!", false) + SET_STRING_ERROR_AND_RETURN("Present mode not supported!", false); } windowData->presentMode = presentMode; @@ -9898,6 +9950,29 @@ static bool VULKAN_SetSwapchainParameters( return true; } +static bool VULKAN_SetAllowedFramesInFlight( + SDL_GPURenderer *driverData, + Uint32 allowedFramesInFlight) +{ + VulkanRenderer *renderer = (VulkanRenderer *)driverData; + + renderer->allowedFramesInFlight = allowedFramesInFlight; + + for (Uint32 i = 0; i < renderer->claimedWindowCount; i += 1) { + WindowData *windowData = renderer->claimedWindows[i]; + + Uint32 recreateResult = VULKAN_INTERNAL_RecreateSwapchain(renderer, windowData); + if (!recreateResult) { + return false; + } else if (recreateResult == VULKAN_INTERNAL_TRY_AGAIN) { + // Edge case, swapchain extent is (0, 0) but this is not an error + windowData->needsSwapchainRecreate = true; + } + } + + return true; +} + // Submission structure static VulkanFenceHandle *VULKAN_INTERNAL_AcquireFenceFromPool( @@ -9920,7 +9995,7 @@ static VulkanFenceHandle *VULKAN_INTERNAL_AcquireFenceFromPool( NULL, &fence); - CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateFence, NULL) + CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateFence, NULL); handle = SDL_malloc(sizeof(VulkanFenceHandle)); handle->fence = fence; @@ -9940,7 +10015,7 @@ static VulkanFenceHandle *VULKAN_INTERNAL_AcquireFenceFromPool( SDL_UnlockMutex(renderer->fencePool.lock); - CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkResetFences, NULL) + CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkResetFences, NULL); return handle; } @@ -10156,7 +10231,7 @@ static bool VULKAN_WaitForFences( waitAll, SDL_MAX_UINT64); - CHECK_VULKAN_ERROR_AND_RETURN(result, vkWaitForFences, false) + CHECK_VULKAN_ERROR_AND_RETURN(result, vkWaitForFences, false); SDL_stack_free(vkFences); @@ -10192,7 +10267,7 @@ static bool VULKAN_Wait( result = renderer->vkDeviceWaitIdle(renderer->logicalDevice); - CHECK_VULKAN_ERROR_AND_RETURN(result, vkDeviceWaitIdle, false) + CHECK_VULKAN_ERROR_AND_RETURN(result, vkDeviceWaitIdle, false); SDL_LockMutex(renderer->submitLock); @@ -10219,6 +10294,22 @@ static SDL_GPUFence *VULKAN_SubmitAndAcquireFence( return (SDL_GPUFence *)vulkanCommandBuffer->inFlightFence; } +static void VULKAN_INTERNAL_ReleaseCommandBuffer(VulkanCommandBuffer *vulkanCommandBuffer) +{ + VulkanRenderer *renderer = vulkanCommandBuffer->renderer; + + if (renderer->submittedCommandBufferCount + 1 >= renderer->submittedCommandBufferCapacity) { + renderer->submittedCommandBufferCapacity = renderer->submittedCommandBufferCount + 1; + + renderer->submittedCommandBuffers = SDL_realloc( + renderer->submittedCommandBuffers, + sizeof(VulkanCommandBuffer *) * renderer->submittedCommandBufferCapacity); + } + + renderer->submittedCommandBuffers[renderer->submittedCommandBufferCount] = vulkanCommandBuffer; + renderer->submittedCommandBufferCount += 1; +} + static bool VULKAN_Submit( SDL_GPUCommandBuffer *commandBuffer) { @@ -10289,22 +10380,9 @@ static bool VULKAN_Submit( if (vulkanResult != VK_SUCCESS) { SDL_UnlockMutex(renderer->submitLock); - CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkQueueSubmit, false) + CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkQueueSubmit, false); } - // Mark command buffers as submitted - - if (renderer->submittedCommandBufferCount + 1 >= renderer->submittedCommandBufferCapacity) { - renderer->submittedCommandBufferCapacity = renderer->submittedCommandBufferCount + 1; - - renderer->submittedCommandBuffers = SDL_realloc( - renderer->submittedCommandBuffers, - sizeof(VulkanCommandBuffer *) * renderer->submittedCommandBufferCapacity); - } - - renderer->submittedCommandBuffers[renderer->submittedCommandBufferCount] = vulkanCommandBuffer; - renderer->submittedCommandBufferCount += 1; - // Present, if applicable bool result = true; @@ -10336,12 +10414,16 @@ static bool VULKAN_Submit( presentData->windowData->needsSwapchainRecreate = true; } } else { - CHECK_VULKAN_ERROR_AND_RETURN(presentResult, vkQueuePresentKHR, false) + if (presentResult != VK_SUCCESS) { + VULKAN_INTERNAL_ReleaseCommandBuffer(vulkanCommandBuffer); + SDL_UnlockMutex(renderer->submitLock); + } + + CHECK_VULKAN_ERROR_AND_RETURN(presentResult, vkQueuePresentKHR, false); } presentData->windowData->frameCounter = - (presentData->windowData->frameCounter + 1) % MAX_FRAMES_IN_FLIGHT; - + (presentData->windowData->frameCounter + 1) % renderer->allowedFramesInFlight; } // Check if we can perform any cleanups @@ -10391,6 +10473,10 @@ static bool VULKAN_Submit( result = VULKAN_INTERNAL_DefragmentMemory(renderer); } + // Mark command buffer as submitted + // This must happen after defrag, because it will try to acquire new command buffers. + VULKAN_INTERNAL_ReleaseCommandBuffer(vulkanCommandBuffer); + SDL_UnlockMutex(renderer->submitLock); return result; @@ -10409,7 +10495,7 @@ static bool VULKAN_Cancel( result = renderer->vkResetCommandBuffer( vulkanCommandBuffer->commandBuffer, VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT); - CHECK_VULKAN_ERROR_AND_RETURN(result, vkResetCommandBuffer, false) + CHECK_VULKAN_ERROR_AND_RETURN(result, vkResetCommandBuffer, false); vulkanCommandBuffer->autoReleaseFence = false; SDL_LockMutex(renderer->submitLock); @@ -10907,7 +10993,7 @@ static Uint8 VULKAN_INTERNAL_CreateInstance(VulkanRenderer *renderer) &renderer->supportsDebugUtils, &renderer->supportsColorspace)) { SDL_stack_free((char *)instanceExtensionNames); - SET_STRING_ERROR_AND_RETURN("Required Vulkan instance extensions not supported", false) + SET_STRING_ERROR_AND_RETURN("Required Vulkan instance extensions not supported", false); } if (renderer->supportsDebugUtils) { @@ -10952,7 +11038,7 @@ static Uint8 VULKAN_INTERNAL_CreateInstance(VulkanRenderer *renderer) SDL_stack_free((char *)instanceExtensionNames); if (vulkanResult != VK_SUCCESS) { - CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateInstance, 0) + CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateInstance, 0); } return 1; @@ -11002,7 +11088,8 @@ static Uint8 VULKAN_INTERNAL_IsDeviceSuitable( if (!deviceFeatures.independentBlend || !deviceFeatures.imageCubeArray || !deviceFeatures.depthClamp || - !deviceFeatures.shaderClipDistance) { + !deviceFeatures.shaderClipDistance || + !deviceFeatures.drawIndirectFirstInstance) { return 0; } @@ -11105,7 +11192,7 @@ static Uint8 VULKAN_INTERNAL_DeterminePhysicalDevice(VulkanRenderer *renderer) renderer->instance, &physicalDeviceCount, NULL); - CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkEnumeratePhysicalDevices, 0) + CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkEnumeratePhysicalDevices, 0); if (physicalDeviceCount == 0) { SDL_LogInfo(SDL_LOG_CATEGORY_GPU, "Failed to find any GPUs with Vulkan support"); @@ -11248,6 +11335,7 @@ static Uint8 VULKAN_INTERNAL_CreateLogicalDevice( desiredDeviceFeatures.imageCubeArray = VK_TRUE; desiredDeviceFeatures.depthClamp = VK_TRUE; desiredDeviceFeatures.shaderClipDistance = VK_TRUE; + desiredDeviceFeatures.drawIndirectFirstInstance = VK_TRUE; if (haveDeviceFeatures.fillModeNonSolid) { desiredDeviceFeatures.fillModeNonSolid = VK_TRUE; @@ -11304,7 +11392,7 @@ static Uint8 VULKAN_INTERNAL_CreateLogicalDevice( NULL, &renderer->logicalDevice); SDL_stack_free((void *)deviceExtensions); - CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateDevice, 0) + CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateDevice, 0); // Load vkDevice entry points @@ -11424,11 +11512,12 @@ static SDL_GPUDevice *VULKAN_CreateDevice(bool debugMode, bool preferLowPower, S SDL_memset(renderer, '\0', sizeof(VulkanRenderer)); renderer->debugMode = debugMode; renderer->preferLowPower = preferLowPower; + renderer->allowedFramesInFlight = 2; if (!VULKAN_INTERNAL_PrepareVulkan(renderer)) { SDL_free(renderer); SDL_Vulkan_UnloadLibrary(); - SET_STRING_ERROR_AND_RETURN("Failed to initialize Vulkan!", NULL) + SET_STRING_ERROR_AND_RETURN("Failed to initialize Vulkan!", NULL); } SDL_LogInfo(SDL_LOG_CATEGORY_GPU, "SDL_GPU Driver: Vulkan"); @@ -11456,7 +11545,7 @@ static SDL_GPUDevice *VULKAN_CreateDevice(bool debugMode, bool preferLowPower, S renderer)) { SDL_free(renderer); SDL_Vulkan_UnloadLibrary(); - SET_STRING_ERROR_AND_RETURN("Failed to create logical device!", NULL) + SET_STRING_ERROR_AND_RETURN("Failed to create logical device!", NULL); } // FIXME: just move this into this function @@ -11539,7 +11628,7 @@ static SDL_GPUDevice *VULKAN_CreateDevice(bool debugMode, bool preferLowPower, S VULKAN_INTERNAL_CommandPoolHashFunction, VULKAN_INTERNAL_CommandPoolHashKeyMatch, VULKAN_INTERNAL_CommandPoolHashNuke, - false); + false, false); renderer->renderPassHashTable = SDL_CreateHashTable( (void *)renderer, @@ -11547,7 +11636,7 @@ static SDL_GPUDevice *VULKAN_CreateDevice(bool debugMode, bool preferLowPower, S VULKAN_INTERNAL_RenderPassHashFunction, VULKAN_INTERNAL_RenderPassHashKeyMatch, VULKAN_INTERNAL_RenderPassHashNuke, - false); + false, false); renderer->framebufferHashTable = SDL_CreateHashTable( (void *)renderer, @@ -11555,7 +11644,7 @@ static SDL_GPUDevice *VULKAN_CreateDevice(bool debugMode, bool preferLowPower, S VULKAN_INTERNAL_FramebufferHashFunction, VULKAN_INTERNAL_FramebufferHashKeyMatch, VULKAN_INTERNAL_FramebufferHashNuke, - false); + false, false); renderer->graphicsPipelineResourceLayoutHashTable = SDL_CreateHashTable( (void *)renderer, @@ -11563,7 +11652,7 @@ static SDL_GPUDevice *VULKAN_CreateDevice(bool debugMode, bool preferLowPower, S VULKAN_INTERNAL_GraphicsPipelineResourceLayoutHashFunction, VULKAN_INTERNAL_GraphicsPipelineResourceLayoutHashKeyMatch, VULKAN_INTERNAL_GraphicsPipelineResourceLayoutHashNuke, - false); + false, false); renderer->computePipelineResourceLayoutHashTable = SDL_CreateHashTable( (void *)renderer, @@ -11571,7 +11660,7 @@ static SDL_GPUDevice *VULKAN_CreateDevice(bool debugMode, bool preferLowPower, S VULKAN_INTERNAL_ComputePipelineResourceLayoutHashFunction, VULKAN_INTERNAL_ComputePipelineResourceLayoutHashKeyMatch, VULKAN_INTERNAL_ComputePipelineResourceLayoutHashNuke, - false); + false, false); renderer->descriptorSetLayoutHashTable = SDL_CreateHashTable( (void *)renderer, @@ -11579,7 +11668,7 @@ static SDL_GPUDevice *VULKAN_CreateDevice(bool debugMode, bool preferLowPower, S VULKAN_INTERNAL_DescriptorSetLayoutHashFunction, VULKAN_INTERNAL_DescriptorSetLayoutHashKeyMatch, VULKAN_INTERNAL_DescriptorSetLayoutHashNuke, - false); + false, false); // Initialize fence pool diff --git a/libs/SDL3/src/hidapi/SDL_hidapi.c b/libs/SDL3/src/hidapi/SDL_hidapi.c index 81bebf0c..6c027f91 100644 --- a/libs/SDL3/src/hidapi/SDL_hidapi.c +++ b/libs/SDL3/src/hidapi/SDL_hidapi.c @@ -1242,7 +1242,7 @@ int SDL_hid_init(void) return -1; } -#ifdef SDL_PLATFORM_MACOS +#if defined(SDL_PLATFORM_MACOS) && !defined(SDL_HIDAPI_DISABLED) hid_darwin_set_open_exclusive(0); #endif diff --git a/libs/SDL3/src/hidapi/android/hid.cpp b/libs/SDL3/src/hidapi/android/hid.cpp index 13f31292..1e43aa73 100644 --- a/libs/SDL3/src/hidapi/android/hid.cpp +++ b/libs/SDL3/src/hidapi/android/hid.cpp @@ -748,6 +748,8 @@ public: m_nReportResponseError = -ECONNRESET; pthread_cond_broadcast( &m_cv ); + m_bOpenResult = false; + if ( bDeleteDevice ) { delete m_pDevice; @@ -808,7 +810,7 @@ extern "C" JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceReleaseCallback)(JNIEnv *env, jobject thiz); extern "C" -JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceConnected)(JNIEnv *env, jobject thiz, int nDeviceID, jstring sIdentifier, int nVendorId, int nProductId, jstring sSerialNumber, int nReleaseNumber, jstring sManufacturer, jstring sProduct, int nInterface, int nInterfaceClass, int nInterfaceSubclass, int nInterfaceProtocol ); +JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceConnected)(JNIEnv *env, jobject thiz, int nDeviceID, jstring sIdentifier, int nVendorId, int nProductId, jstring sSerialNumber, int nReleaseNumber, jstring sManufacturer, jstring sProduct, int nInterface, int nInterfaceClass, int nInterfaceSubclass, int nInterfaceProtocol, bool bBluetooth ); extern "C" JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceOpenPending)(JNIEnv *env, jobject thiz, int nDeviceID); @@ -888,7 +890,7 @@ JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceReleaseCallbac } extern "C" -JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceConnected)(JNIEnv *env, jobject thiz, int nDeviceID, jstring sIdentifier, int nVendorId, int nProductId, jstring sSerialNumber, int nReleaseNumber, jstring sManufacturer, jstring sProduct, int nInterface, int nInterfaceClass, int nInterfaceSubclass, int nInterfaceProtocol ) +JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceConnected)(JNIEnv *env, jobject thiz, int nDeviceID, jstring sIdentifier, int nVendorId, int nProductId, jstring sSerialNumber, int nReleaseNumber, jstring sManufacturer, jstring sProduct, int nInterface, int nInterfaceClass, int nInterfaceSubclass, int nInterfaceProtocol, bool bBluetooth ) { LOGV( "HIDDeviceConnected() id=%d VID/PID = %.4x/%.4x, interface %d\n", nDeviceID, nVendorId, nProductId, nInterface ); @@ -905,6 +907,14 @@ JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceConnected)(JNI pInfo->interface_class = nInterfaceClass; pInfo->interface_subclass = nInterfaceSubclass; pInfo->interface_protocol = nInterfaceProtocol; + if ( bBluetooth ) + { + pInfo->bus_type = HID_API_BUS_BLUETOOTH; + } + else + { + pInfo->bus_type = HID_API_BUS_USB; + } hid_device_ref pDevice( new CHIDDevice( nDeviceID, pInfo ) ); @@ -1386,7 +1396,7 @@ extern "C" JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceReleaseCallback)(JNIEnv *env, jobject thiz); extern "C" -JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceConnected)(JNIEnv *env, jobject thiz, int nDeviceID, jstring sIdentifier, int nVendorId, int nProductId, jstring sSerialNumber, int nReleaseNumber, jstring sManufacturer, jstring sProduct, int nInterface, int nInterfaceClass, int nInterfaceSubclass, int nInterfaceProtocol ); +JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceConnected)(JNIEnv *env, jobject thiz, int nDeviceID, jstring sIdentifier, int nVendorId, int nProductId, jstring sSerialNumber, int nReleaseNumber, jstring sManufacturer, jstring sProduct, int nInterface, int nInterfaceClass, int nInterfaceSubclass, int nInterfaceProtocol, bool bBluetooth ); extern "C" JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceOpenPending)(JNIEnv *env, jobject thiz, int nDeviceID); @@ -1417,7 +1427,7 @@ JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceReleaseCallbac } extern "C" -JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceConnected)(JNIEnv *env, jobject thiz, int nDeviceID, jstring sIdentifier, int nVendorId, int nProductId, jstring sSerialNumber, int nReleaseNumber, jstring sManufacturer, jstring sProduct, int nInterface, int nInterfaceClass, int nInterfaceSubclass, int nInterfaceProtocol ) +JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceConnected)(JNIEnv *env, jobject thiz, int nDeviceID, jstring sIdentifier, int nVendorId, int nProductId, jstring sSerialNumber, int nReleaseNumber, jstring sManufacturer, jstring sProduct, int nInterface, int nInterfaceClass, int nInterfaceSubclass, int nInterfaceProtocol, bool bBluetooth ) { LOGV("Stub HIDDeviceConnected() id=%d VID/PID = %.4x/%.4x, interface %d\n", nDeviceID, nVendorId, nProductId, nInterface); } @@ -1458,7 +1468,7 @@ extern "C" JNINativeMethod HIDDeviceManager_tab[8] = { { "HIDDeviceRegisterCallback", "()V", (void*)HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceRegisterCallback) }, { "HIDDeviceReleaseCallback", "()V", (void*)HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceReleaseCallback) }, - { "HIDDeviceConnected", "(ILjava/lang/String;IILjava/lang/String;ILjava/lang/String;Ljava/lang/String;IIII)V", (void*)HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceConnected) }, + { "HIDDeviceConnected", "(ILjava/lang/String;IILjava/lang/String;ILjava/lang/String;Ljava/lang/String;IIIIZ)V", (void*)HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceConnected) }, { "HIDDeviceOpenPending", "(I)V", (void*)HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceOpenPending) }, { "HIDDeviceOpenResult", "(IZ)V", (void*)HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceOpenResult) }, { "HIDDeviceDisconnected", "(I)V", (void*)HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceDisconnected) }, diff --git a/libs/SDL3/src/hidapi/ios/hid.m b/libs/SDL3/src/hidapi/ios/hid.m index bfa375c1..d0a8b54d 100644 --- a/libs/SDL3/src/hidapi/ios/hid.m +++ b/libs/SDL3/src/hidapi/ios/hid.m @@ -853,6 +853,7 @@ static struct hid_device_info *create_device_info_for_hid_device(HIDBLEDevice *d device_info->product_id = D0G_BLE2_PID; device_info->product_string = wcsdup( L"Steam Controller" ); device_info->manufacturer_string = wcsdup( L"Valve Corporation" ); + device_info->bus_type = HID_API_BUS_BLUETOOTH; return device_info; } diff --git a/libs/SDL3/src/hidapi/linux/hid.c b/libs/SDL3/src/hidapi/linux/hid.c index 6c17fac3..dbbb3277 100644 --- a/libs/SDL3/src/hidapi/linux/hid.c +++ b/libs/SDL3/src/hidapi/linux/hid.c @@ -900,6 +900,32 @@ static struct hid_device_info * create_device_info_for_device(struct udev_device } } +#ifdef HIDAPI_IGNORE_DEVICE + { + struct hid_device_info *prev_dev = NULL; + + cur_dev = root; + while (cur_dev) { + if (HIDAPI_IGNORE_DEVICE(cur_dev->bus_type, cur_dev->vendor_id, cur_dev->product_id, cur_dev->usage_page, cur_dev->usage)) { + struct hid_device_info *tmp = cur_dev; + + cur_dev = tmp->next; + if (prev_dev) { + prev_dev->next = cur_dev; + } else { + root = cur_dev; + } + tmp->next = NULL; + + hid_free_enumeration(tmp); + } else { + prev_dev = cur_dev; + cur_dev = cur_dev->next; + } + } + } +#endif + end: free(serial_number_utf8); free(product_name_utf8); @@ -1030,23 +1056,6 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, continue; } -#ifdef HIDAPI_IGNORE_DEVICE - /* See if there are any devices we should skip in enumeration */ - if (!parse_hid_vid_pid_from_sysfs(sysfs_path, &bus_type, &dev_vid, &dev_pid)) - continue; - - struct hidraw_report_descriptor report_desc; - unsigned short page = 0, usage = 0; - if (get_hid_report_descriptor_from_sysfs(sysfs_path, &report_desc) >= 0) { - struct hid_usage_iterator usage_iterator; - memset(&usage_iterator, 0, sizeof(usage_iterator)); - get_next_hid_usage(report_desc.value, report_desc.size, &usage_iterator, &page, &usage); - } - if (HIDAPI_IGNORE_DEVICE(bus_type, dev_vid, dev_pid, page, usage)) { - continue; - } -#endif - raw_dev = udev_device_new_from_syspath(udev, sysfs_path); if (!raw_dev) continue; diff --git a/libs/SDL3/src/joystick/SDL_gamepad.c b/libs/SDL3/src/joystick/SDL_gamepad.c index 3c5e7edd..7d75a5a9 100644 --- a/libs/SDL3/src/joystick/SDL_gamepad.c +++ b/libs/SDL3/src/joystick/SDL_gamepad.c @@ -2582,7 +2582,7 @@ bool SDL_IsGamepad(SDL_JoystickID instance_id) } if (!s_gamepadInstanceIDs) { - s_gamepadInstanceIDs = SDL_CreateHashTable(NULL, 4, SDL_HashID, SDL_KeyMatchID, NULL, false); + s_gamepadInstanceIDs = SDL_CreateHashTable(NULL, 4, SDL_HashID, SDL_KeyMatchID, NULL, false, false); } SDL_InsertIntoHashTable(s_gamepadInstanceIDs, (void *)(uintptr_t)instance_id, (void *)(uintptr_t)result); } @@ -2620,6 +2620,17 @@ bool SDL_ShouldIgnoreGamepad(Uint16 vendor_id, Uint16 product_id, Uint16 version return true; } +#ifdef SDL_PLATFORM_WIN32 + if (SDL_GetHintBoolean("SDL_GAMECONTROLLER_ALLOW_STEAM_VIRTUAL_GAMEPAD", false) && + SDL_GetHintBoolean("STEAM_COMPAT_PROTON", false)) { + // We are launched by Steam and running under Proton + // We can't tell whether this controller is a Steam Virtual Gamepad, + // so assume that Proton is doing the appropriate filtering of controllers + // and anything we see here is fine to use. + return false; + } +#endif // SDL_PLATFORM_WIN32 + if (SDL_IsJoystickSteamVirtualGamepad(vendor_id, product_id, version)) { return !SDL_GetHintBoolean("SDL_GAMECONTROLLER_ALLOW_STEAM_VIRTUAL_GAMEPAD", false); } @@ -2741,7 +2752,7 @@ bool SDL_GamepadHasAxis(SDL_Gamepad *gamepad, SDL_GamepadAxis axis) CHECK_GAMEPAD_MAGIC(gamepad, false); for (i = 0; i < gamepad->num_bindings; ++i) { - SDL_GamepadBinding *binding = &gamepad->bindings[i]; + const SDL_GamepadBinding *binding = &gamepad->bindings[i]; if (binding->output_type == SDL_GAMEPAD_BINDTYPE_AXIS && binding->output.axis.axis == axis) { result = true; break; @@ -2767,7 +2778,7 @@ Sint16 SDL_GetGamepadAxis(SDL_Gamepad *gamepad, SDL_GamepadAxis axis) CHECK_GAMEPAD_MAGIC(gamepad, 0); for (i = 0; i < gamepad->num_bindings; ++i) { - SDL_GamepadBinding *binding = &gamepad->bindings[i]; + const SDL_GamepadBinding *binding = &gamepad->bindings[i]; if (binding->output_type == SDL_GAMEPAD_BINDTYPE_AXIS && binding->output.axis.axis == axis) { int value = 0; bool valid_input_range; @@ -2831,7 +2842,7 @@ bool SDL_GamepadHasButton(SDL_Gamepad *gamepad, SDL_GamepadButton button) CHECK_GAMEPAD_MAGIC(gamepad, false); for (i = 0; i < gamepad->num_bindings; ++i) { - SDL_GamepadBinding *binding = &gamepad->bindings[i]; + const SDL_GamepadBinding *binding = &gamepad->bindings[i]; if (binding->output_type == SDL_GAMEPAD_BINDTYPE_BUTTON && binding->output.button == button) { result = true; break; @@ -2857,7 +2868,7 @@ bool SDL_GetGamepadButton(SDL_Gamepad *gamepad, SDL_GamepadButton button) CHECK_GAMEPAD_MAGIC(gamepad, false); for (i = 0; i < gamepad->num_bindings; ++i) { - SDL_GamepadBinding *binding = &gamepad->bindings[i]; + const SDL_GamepadBinding *binding = &gamepad->bindings[i]; if (binding->output_type == SDL_GAMEPAD_BINDTYPE_BUTTON && binding->output.button == button) { if (binding->input_type == SDL_GAMEPAD_BINDTYPE_AXIS) { bool valid_input_range; @@ -2867,23 +2878,19 @@ bool SDL_GetGamepadButton(SDL_Gamepad *gamepad, SDL_GamepadButton button) if (binding->input.axis.axis_min < binding->input.axis.axis_max) { valid_input_range = (value >= binding->input.axis.axis_min && value <= binding->input.axis.axis_max); if (valid_input_range) { - result = (value >= threshold); - break; + result |= (value >= threshold); } } else { valid_input_range = (value >= binding->input.axis.axis_max && value <= binding->input.axis.axis_min); if (valid_input_range) { - result = (value <= threshold); - break; + result |= (value <= threshold); } } } else if (binding->input_type == SDL_GAMEPAD_BINDTYPE_BUTTON) { - result = SDL_GetJoystickButton(gamepad->joystick, binding->input.button); - break; + result |= SDL_GetJoystickButton(gamepad->joystick, binding->input.button); } else if (binding->input_type == SDL_GAMEPAD_BINDTYPE_HAT) { int hat_mask = SDL_GetJoystickHat(gamepad->joystick, binding->input.hat.hat); - result = ((hat_mask & binding->input.hat.hat_mask) != 0); - break; + result |= ((hat_mask & binding->input.hat.hat_mask) != 0); } } } diff --git a/libs/SDL3/src/joystick/SDL_gamepad_db.h b/libs/SDL3/src/joystick/SDL_gamepad_db.h index 4280539f..91abb65f 100644 --- a/libs/SDL3/src/joystick/SDL_gamepad_db.h +++ b/libs/SDL3/src/joystick/SDL_gamepad_db.h @@ -48,8 +48,8 @@ static const char *s_GamepadMappings[] = { "03000000fa2d00000100000000000000,3DRUDDER,leftx:a0,lefty:a1,rightx:a5,righty:a2,", "03000000c82d00000090000000000000,8BitDo FC30 Pro,a:b1,b:b0,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:a3,righty:a4,start:b11,x:b4,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,", "03000000c82d00001038000000000000,8BitDo FC30 Pro,a:b1,b:b0,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:a3,righty:a4,start:b11,x:b4,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,", - "03000000c82d00000650000000000000,8BitDo M30 Gamepad,a:b1,b:b0,back:b10,guide:b2,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,start:b11,x:b4,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,", - "03000000c82d00005106000000000000,8BitDo M30 Gamepad,a:b1,b:b0,back:b10,guide:b2,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,start:b11,x:b4,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,", + "03000000c82d00000650000000000000,8BitDo M30 Gamepad,a:b0,b:b1,back:b10,guide:b2,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,", + "03000000c82d00005106000000000000,8BitDo M30 Gamepad,+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b10,guide:b2,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,", "03000000c82d00001590000000000000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,", "03000000c82d00006528000000000000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,", "030000003512000012ab000000000000,8BitDo NES30 Gamepad,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,", @@ -64,7 +64,9 @@ static const char *s_GamepadMappings[] = { "03000000c82d00006228000000000000,8BitDo SN30 Gamepad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,", "03000000c82d00000260000000000000,8BitDo SN30 Pro+,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,", "03000000c82d00000261000000000000,8BitDo SN30 Pro+,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,", - "03000000c82d00000160000000000000,8BitDo SN30 Pro,a:b1,b:b0,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:a3,righty:a4,start:b11,x:b4,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,", + "03000000c82d00000160000000000000,8BitDo SN30 Pro,crc:769e,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,", /* Firmware v2.00, USB */ + "03000000c82d00000160000000000000,8BitDo SN30 Pro,crc:fa59,a:b1,b:b0,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:a3,righty:a4,start:b11,x:b4,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,", /* Firmware v1.26, USB */ + "03000000c82d00000161000000000000,8BitDo SN30 Pro,crc:190b,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,", /* Bluetooth, firmware v1.26 uses b2 for guide, firmware v2.00 uses b12 for guide */ "030000003512000020ab000000000000,8BitDo SNES30 Gamepad,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,", "03000000c82d00001130000000000000,8BitDo Ultimate Wired Controller,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:b8,leftx:a0,lefty:a1,misc1:b26,paddle1:b24,paddle2:b25,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,", "03000000c82d00001330000000000000,8BitDo Ultimate Wireless Controller,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:b8,leftx:a0,lefty:a1,misc1:b26,paddle1:b23,paddle2:b19,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,", @@ -234,6 +236,8 @@ static const char *s_GamepadMappings[] = { "030000004c050000cc09000000000000,PS4 Controller,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,", "030000004c050000e60c000000000000,PS5 Controller,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,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,", "030000008f0e00000300000000000000,Piranha xtreme,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,", + "03000000d62000002640000000000000,PowerA OPS v1 Wireless Controller,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:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,", + "03000000d62000003340000000000000,PowerA OPS v3 Pro Wireless Controller,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:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,", "03000000d62000006dca000000000000,PowerA Pro Ex,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:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,", "03000000d62000009557000000000000,Pro Elite PS3 Controller,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:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,", "03000000d62000009f31000000000000,Pro Ex mini PS3 Controller,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:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,", @@ -624,8 +628,8 @@ static const char *s_GamepadMappings[] = { "05000000d6200000ad0d000001000000,Moga Pro,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,", "030000006b140000010c000010010000,NACON GC-400ES,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,", "030000001008000001e5000010010000,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,", + "03000000550900001072000011010000,NVIDIA Controller v01.03,a:b0,b:b1,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b4,leftstick:b8,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,", "03000000550900001472000011010000,NVIDIA Controller v01.04,a:b0,b:b1,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b17,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,", - "03000000550900001072000011010000,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b8,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a3,start:b7,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,", "03000000451300000830000010010000,NYKO CORE,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:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,", "03000000790000004318000010010000,Nintendo GameCube Controller,a:b1,b:b0,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:b2,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,", diff --git a/libs/SDL3/src/joystick/SDL_joystick.c b/libs/SDL3/src/joystick/SDL_joystick.c index 8e27bc4d..ba56156c 100644 --- a/libs/SDL3/src/joystick/SDL_joystick.c +++ b/libs/SDL3/src/joystick/SDL_joystick.c @@ -104,10 +104,10 @@ static SDL_JoystickDriver *SDL_joystick_drivers[] = { &SDL_VITA_JoystickDriver, #endif #ifdef SDL_JOYSTICK_N3DS - &SDL_N3DS_JoystickDriver + &SDL_N3DS_JoystickDriver, #endif #if defined(SDL_JOYSTICK_DUMMY) || defined(SDL_JOYSTICK_DISABLED) - &SDL_DUMMY_JoystickDriver + &SDL_DUMMY_JoystickDriver #endif }; @@ -2935,6 +2935,9 @@ bool SDL_IsJoystickXboxSeriesX(Uint16 vendor_id, Uint16 product_id) if (vendor_id == USB_VENDOR_POWERA_ALT) { if ((product_id >= 0x2001 && product_id <= 0x201a) || product_id == USB_PRODUCT_XBOX_SERIES_X_POWERA_FUSION_PRO2 || + product_id == USB_PRODUCT_XBOX_SERIES_X_POWERA_FUSION_PRO4 || + product_id == USB_PRODUCT_XBOX_SERIES_X_POWERA_FUSION_PRO_WIRELESS_USB || + product_id == USB_PRODUCT_XBOX_SERIES_X_POWERA_FUSION_PRO_WIRELESS_DONGLE || product_id == USB_PRODUCT_XBOX_SERIES_X_POWERA_MOGA_XP_ULTRA || product_id == USB_PRODUCT_XBOX_SERIES_X_POWERA_SPECTRA) { return true; @@ -2954,7 +2957,8 @@ bool SDL_IsJoystickXboxSeriesX(Uint16 vendor_id, Uint16 product_id) } if (vendor_id == USB_VENDOR_RAZER) { if (product_id == USB_PRODUCT_RAZER_WOLVERINE_V2 || - product_id == USB_PRODUCT_RAZER_WOLVERINE_V2_CHROMA) { + product_id == USB_PRODUCT_RAZER_WOLVERINE_V2_CHROMA || + product_id == USB_PRODUCT_RAZER_WOLVERINE_V3_PRO) { return true; } } diff --git a/libs/SDL3/src/joystick/bsd/SDL_bsdjoystick.c b/libs/SDL3/src/joystick/bsd/SDL_bsdjoystick.c index 4b34116c..53376207 100644 --- a/libs/SDL3/src/joystick/bsd/SDL_bsdjoystick.c +++ b/libs/SDL3/src/joystick/bsd/SDL_bsdjoystick.c @@ -77,7 +77,7 @@ #include "../SDL_joystick_c.h" #include "../hidapi/SDL_hidapijoystick_c.h" -#if defined(SDL_PLATFORM_FREEBSD) || SDL_HAVE_MACHINE_JOYSTICK_H || defined(__FreeBSD_kernel__) || defined(__DragonFly_) +#if defined(SDL_PLATFORM_FREEBSD) || defined(SDL_HAVE_MACHINE_JOYSTICK_H) || defined(__FreeBSD_kernel__) || defined(__DragonFly_) #define SUPPORT_JOY_GAMEPORT #endif diff --git a/libs/SDL3/src/joystick/controller_list.h b/libs/SDL3/src/joystick/controller_list.h index f178c1d5..5594d2bd 100644 --- a/libs/SDL3/src/joystick/controller_list.h +++ b/libs/SDL3/src/joystick/controller_list.h @@ -196,6 +196,7 @@ static const ControllerDescription_t arrControllers[] = { { MAKE_CONTROLLER_ID( 0x0738, 0xf738 ), k_eControllerType_XBox360Controller, NULL }, // Super SFIV FightStick TE S { MAKE_CONTROLLER_ID( 0x0955, 0x7210 ), k_eControllerType_XBox360Controller, NULL }, // Nvidia Shield local controller { MAKE_CONTROLLER_ID( 0x0955, 0xb400 ), k_eControllerType_XBox360Controller, NULL }, // NVIDIA Shield streaming controller + { MAKE_CONTROLLER_ID( 0x0b05, 0x1b4c ), k_eControllerType_XBox360Controller, NULL }, // ASUS ROG Ally X built-in controller { MAKE_CONTROLLER_ID( 0x0e6f, 0x0105 ), k_eControllerType_XBox360Controller, NULL }, // HSM3 Xbox360 dancepad { MAKE_CONTROLLER_ID( 0x0e6f, 0x0113 ), k_eControllerType_XBox360Controller, "PDP Xbox 360 Afterglow" }, // PDP Afterglow Gamepad for Xbox 360 { MAKE_CONTROLLER_ID( 0x0e6f, 0x011f ), k_eControllerType_XBox360Controller, "PDP Xbox 360 Rock Candy" }, // PDP Rock Candy Gamepad for Xbox 360 diff --git a/libs/SDL3/src/joystick/gdk/SDL_gameinputjoystick.c b/libs/SDL3/src/joystick/gdk/SDL_gameinputjoystick.c index 1a510de5..13b51082 100644 --- a/libs/SDL3/src/joystick/gdk/SDL_gameinputjoystick.c +++ b/libs/SDL3/src/joystick/gdk/SDL_gameinputjoystick.c @@ -261,7 +261,7 @@ static bool GAMEINPUT_JoystickInit(void) hR = GameInputCreateFunc(&g_pGameInput); if (FAILED(hR)) { - return SDL_SetError("GameInputCreate failure with HRESULT of %08X", hR); + return SDL_SetError("GameInputCreate failure with HRESULT of %08lX", hR); } } @@ -274,7 +274,7 @@ static bool GAMEINPUT_JoystickInit(void) GAMEINPUT_InternalJoystickDeviceCallback, &g_GameInputCallbackToken); if (FAILED(hR)) { - return SDL_SetError("IGameInput::RegisterDeviceCallback failure with HRESULT of %08X", hR); + return SDL_SetError("IGameInput::RegisterDeviceCallback failure with HRESULT of %08lX", hR); } // Calculate the relative offset between SDL timestamps and GameInput timestamps diff --git a/libs/SDL3/src/joystick/hidapi/SDL_hidapi_steam.c b/libs/SDL3/src/joystick/hidapi/SDL_hidapi_steam.c index f08586c0..43e95876 100644 --- a/libs/SDL3/src/joystick/hidapi/SDL_hidapi_steam.c +++ b/libs/SDL3/src/joystick/hidapi/SDL_hidapi_steam.c @@ -22,11 +22,27 @@ #ifdef SDL_JOYSTICK_HIDAPI +#include "../../SDL_hints_c.h" #include "../SDL_sysjoystick.h" #include "SDL_hidapijoystick_c.h" #ifdef SDL_JOYSTICK_HIDAPI_STEAM +// Define this if you want to log all packets from the controller +// #define DEBUG_STEAM_PROTOCOL + +#define SDL_HINT_JOYSTICK_HIDAPI_STEAM_PAIRING_ENABLED "SDL_JOYSTICK_HIDAPI_STEAM_PAIRING_ENABLED" + +#if defined(SDL_PLATFORM_ANDROID) || defined(SDL_PLATFORM_IOS) || defined(SDL_PLATFORM_TVOS) +// This requires prompting for Bluetooth permissions, so make sure the application really wants it +#define SDL_HINT_JOYSTICK_HIDAPI_STEAM_DEFAULT false +#else +#define SDL_HINT_JOYSTICK_HIDAPI_STEAM_DEFAULT SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI, SDL_HIDAPI_DEFAULT) +#endif + +#define PAIRING_STATE_DURATION_SECONDS 60 + + /*****************************************************************************************************/ #include "steam/controller_constants.h" @@ -136,6 +152,8 @@ typedef struct SteamControllerStateInternal_t #define D0G_WIRELESS_NEWLYPAIRED 3 #define D0G_IS_WIRELESS_DISCONNECT(data, len) (D0G_IS_VALID_WIRELESS_EVENT(data, len) && D0G_GET_WIRELESS_EVENT_TYPE(data) == D0G_WIRELESS_DISCONNECTED) +#define D0G_IS_WIRELESS_CONNECT(data, len) (D0G_IS_VALID_WIRELESS_EVENT(data, len) && D0G_GET_WIRELESS_EVENT_TYPE(data) != D0G_WIRELESS_DISCONNECTED) + #define MAX_REPORT_SEGMENT_PAYLOAD_SIZE 18 /* @@ -198,11 +216,7 @@ static uint8_t GetSegmentHeader(int nSegmentNumber, bool bLastPacket) static void hexdump(const uint8_t *ptr, int len) { - int i; - for (i = 0; i < len; ++i) { - printf("%02x ", ptr[i]); - } - printf("\n"); + HIDAPI_DumpPacket("Data", ptr, len); } static void ResetSteamControllerPacketAssembler(SteamControllerPacketAssembler *pAssembler) @@ -211,10 +225,9 @@ static void ResetSteamControllerPacketAssembler(SteamControllerPacketAssembler * pAssembler->nExpectedSegmentNumber = 0; } -static void InitializeSteamControllerPacketAssembler(SteamControllerPacketAssembler *pAssembler) +static void InitializeSteamControllerPacketAssembler(SteamControllerPacketAssembler *pAssembler, bool bIsBle) { - // We only support BLE devices right now - pAssembler->bIsBle = true; + pAssembler->bIsBle = bIsBle; ResetSteamControllerPacketAssembler(pAssembler); } @@ -283,17 +296,16 @@ static int WriteSegmentToSteamControllerPacketAssembler(SteamControllerPacketAss #define BLE_MAX_READ_RETRIES 8 -static int SetFeatureReport(SDL_hid_device *dev, unsigned char uBuffer[65], int nActualDataLen) +static int SetFeatureReport(SDL_HIDAPI_Device *dev, const unsigned char uBuffer[65], int nActualDataLen) { int nRet = -1; - bool bBle = true; // only wireless/BLE for now, though macOS could do wired in the future DPRINTF("SetFeatureReport %p %p %d\n", dev, uBuffer, nActualDataLen); - if (bBle) { + if (dev->is_bluetooth) { int nSegmentNumber = 0; uint8_t uPacketBuffer[MAX_REPORT_SEGMENT_SIZE]; - unsigned char *pBufferPtr = uBuffer + 1; + const unsigned char *pBufferPtr = uBuffer + 1; if (nActualDataLen < 1) { return -1; @@ -316,29 +328,38 @@ static int SetFeatureReport(SDL_hid_device *dev, unsigned char uBuffer[65], int pBufferPtr += nBytesInPacket; nSegmentNumber++; - nRet = SDL_hid_send_feature_report(dev, uPacketBuffer, sizeof(uPacketBuffer)); - DPRINTF("SetFeatureReport() ret = %d\n", nRet); + nRet = SDL_hid_send_feature_report(dev->dev, uPacketBuffer, sizeof(uPacketBuffer)); + } + } else { + for (int nRetries = 0; nRetries < RADIO_WORKAROUND_SLEEP_ATTEMPTS; nRetries++) { + nRet = SDL_hid_send_feature_report(dev->dev, uBuffer, 65); + if (nRet >= 0) { + break; + } + + SDL_DelayNS(RADIO_WORKAROUND_SLEEP_DURATION_US * 1000); } } + DPRINTF("SetFeatureReport() ret = %d\n", nRet); + return nRet; } -static int GetFeatureReport(SDL_hid_device *dev, unsigned char uBuffer[65]) +static int GetFeatureReport(SDL_HIDAPI_Device *dev, unsigned char uBuffer[65]) { int nRet = -1; - bool bBle = true; DPRINTF("GetFeatureReport( %p %p )\n", dev, uBuffer); - if (bBle) { + if (dev->is_bluetooth) { int nRetries = 0; uint8_t uSegmentBuffer[MAX_REPORT_SEGMENT_SIZE + 1]; uint8_t ucBytesToRead = MAX_REPORT_SEGMENT_SIZE; uint8_t ucDataStartOffset = 0; SteamControllerPacketAssembler assembler; - InitializeSteamControllerPacketAssembler(&assembler); + InitializeSteamControllerPacketAssembler(&assembler, dev->is_bluetooth); // On Windows and macOS, BLE devices get 2 copies of the feature report ID, one that is removed by ReadFeatureReport, // and one that's included in the buffer we receive. We pad the bytes to read and skip over the report ID @@ -351,7 +372,7 @@ static int GetFeatureReport(SDL_hid_device *dev, unsigned char uBuffer[65]) while (nRetries < BLE_MAX_READ_RETRIES) { SDL_memset(uSegmentBuffer, 0, sizeof(uSegmentBuffer)); uSegmentBuffer[0] = BLE_REPORT_NUMBER; - nRet = SDL_hid_get_feature_report(dev, uSegmentBuffer, ucBytesToRead); + nRet = SDL_hid_get_feature_report(dev->dev, uSegmentBuffer, ucBytesToRead); DPRINTF("GetFeatureReport ble ret=%d\n", nRet); HEXDUMP(uSegmentBuffer, nRet); @@ -378,35 +399,52 @@ static int GetFeatureReport(SDL_hid_device *dev, unsigned char uBuffer[65]) } printf("Could not get a full ble packet after %d retries\n", nRetries); return -1; + } else { + SDL_memset(uBuffer, 0, 65); + + for (int nRetries = 0; nRetries < RADIO_WORKAROUND_SLEEP_ATTEMPTS; nRetries++) { + nRet = SDL_hid_get_feature_report(dev->dev, uBuffer, 65); + if (nRet >= 0) { + break; + } + + SDL_DelayNS(RADIO_WORKAROUND_SLEEP_DURATION_US * 1000); + } + + DPRINTF("GetFeatureReport USB ret=%d\n", nRet); + HEXDUMP(uBuffer, nRet); } return nRet; } -static int ReadResponse(SDL_hid_device *dev, uint8_t uBuffer[65], int nExpectedResponse) +static int ReadResponse(SDL_HIDAPI_Device *dev, uint8_t uBuffer[65], int nExpectedResponse) { - int nRet = GetFeatureReport(dev, uBuffer); + for (int nRetries = 0; nRetries < 10; nRetries++) { + int nRet = GetFeatureReport(dev, uBuffer); - DPRINTF("ReadResponse( %p %p %d )\n", dev, uBuffer, nExpectedResponse); + DPRINTF("ReadResponse( %p %p 0x%x )\n", dev, uBuffer, nExpectedResponse); + + if (nRet < 0) { + continue; + } + + DPRINTF("ReadResponse got %d bytes of data: ", nRet); + HEXDUMP(uBuffer, nRet); + + if (uBuffer[1] != nExpectedResponse) { + continue; + } - if (nRet < 0) { return nRet; } - - DPRINTF("ReadResponse got %d bytes of data: ", nRet); - HEXDUMP(uBuffer, nRet); - - if (uBuffer[1] != nExpectedResponse) { - return -1; - } - - return nRet; + return -1; } //--------------------------------------------------------------------------- // Reset steam controller (unmap buttons and pads) and re-fetch capability bits //--------------------------------------------------------------------------- -static bool ResetSteamController(SDL_hid_device *dev, bool bSuppressErrorSpew, uint32_t *punUpdateRateUS) +static bool ResetSteamController(SDL_HIDAPI_Device *dev, bool bSuppressErrorSpew, uint32_t *punUpdateRateUS) { // Firmware quirk: Set Feature and Get Feature requests always require a 65-byte buffer. unsigned char buf[65]; @@ -590,10 +628,35 @@ static int ReadSteamController(SDL_hid_device *dev, uint8_t *pData, int nDataSiz return SDL_hid_read(dev, pData, nDataSize); } +//--------------------------------------------------------------------------- +// Set Steam Controller pairing state +//--------------------------------------------------------------------------- +static void SetPairingState(SDL_HIDAPI_Device *dev, bool bEnablePairing) +{ + unsigned char buf[65]; + SDL_memset(buf, 0, 65); + buf[1] = ID_ENABLE_PAIRING; + buf[2] = 2; // 2 payload bytes: bool + timeout + buf[3] = bEnablePairing ? 1 : 0; + buf[4] = bEnablePairing ? PAIRING_STATE_DURATION_SECONDS : 0; + SetFeatureReport(dev, buf, 5); +} + +//--------------------------------------------------------------------------- +// Commit Steam Controller pairing +//--------------------------------------------------------------------------- +static void CommitPairing(SDL_HIDAPI_Device *dev) +{ + unsigned char buf[65]; + SDL_memset(buf, 0, 65); + buf[1] = ID_DONGLE_COMMIT_DEVICE; + SetFeatureReport(dev, buf, 2); +} + //--------------------------------------------------------------------------- // Close a Steam Controller //--------------------------------------------------------------------------- -static void CloseSteamController(SDL_hid_device *dev) +static void CloseSteamController(SDL_HIDAPI_Device *dev) { // Switch the Steam Controller back to lizard mode so it works with the OS unsigned char buf[65]; @@ -937,15 +1000,23 @@ static bool UpdateSteamControllerState(const uint8_t *pData, int nDataSize, Stea typedef struct { + SDL_HIDAPI_Device *device; + bool connected; bool report_sensors; uint32_t update_rate_in_us; Uint64 sensor_timestamp; + Uint64 pairing_time; SteamControllerPacketAssembler m_assembler; SteamControllerStateInternal_t m_state; SteamControllerStateInternal_t m_last_state; } SDL_DriverSteam_Context; +static bool IsDongle(Uint16 product_id) +{ + return (product_id == USB_PRODUCT_VALVE_STEAM_CONTROLLER_DONGLE); +} + static void HIDAPI_DriverSteam_RegisterHints(SDL_HintCallback callback, void *userdata) { SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_STEAM, callback, userdata); @@ -958,12 +1029,82 @@ static void HIDAPI_DriverSteam_UnregisterHints(SDL_HintCallback callback, void * static bool HIDAPI_DriverSteam_IsEnabled(void) { - return SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_STEAM, false); + return SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_STEAM, SDL_HINT_JOYSTICK_HIDAPI_STEAM_DEFAULT); } static bool HIDAPI_DriverSteam_IsSupportedDevice(SDL_HIDAPI_Device *device, const char *name, SDL_GamepadType type, Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, int interface_class, int interface_subclass, int interface_protocol) { - return SDL_IsJoystickSteamController(vendor_id, product_id); + if (!SDL_IsJoystickSteamController(vendor_id, product_id)) { + return false; + } + + if (device->is_bluetooth) { + return true; + } + + if (IsDongle(product_id)) { + if (interface_number >= 1 && interface_number <= 4) { + // This is one of the wireless controller interfaces + return true; + } + } else { + if (interface_number == 2) { + // This is the controller interface (not mouse or keyboard) + return true; + } + } + return false; +} + +static void HIDAPI_DriverSteam_SetPairingState(SDL_DriverSteam_Context *ctx, bool enabled) +{ + // Only have one dongle in pairing mode at a time + static SDL_DriverSteam_Context *s_PairingContext = NULL; + + if (enabled && s_PairingContext != NULL) { + return; + } + + if (!enabled && s_PairingContext != ctx) { + return; + } + + if (ctx->connected) { + return; + } + + SetPairingState(ctx->device, enabled); + + if (enabled) { + ctx->pairing_time = SDL_GetTicks(); + s_PairingContext = ctx; + } else { + ctx->pairing_time = 0; + s_PairingContext = NULL; + } +} + +static void HIDAPI_DriverSteam_RenewPairingState(SDL_DriverSteam_Context *ctx) +{ + Uint64 now = SDL_GetTicks(); + + if (now >= ctx->pairing_time + PAIRING_STATE_DURATION_SECONDS * 1000) { + SetPairingState(ctx->device, true); + ctx->pairing_time = now; + } +} + +static void HIDAPI_DriverSteam_CommitPairing(SDL_DriverSteam_Context *ctx) +{ + CommitPairing(ctx->device); +} + +static void SDLCALL SDL_PairingEnabledHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint) +{ + SDL_DriverSteam_Context *ctx = (SDL_DriverSteam_Context *)userdata; + bool enabled = SDL_GetStringBoolean(hint, false); + + HIDAPI_DriverSteam_SetPairingState(ctx, enabled); } static bool HIDAPI_DriverSteam_InitDevice(SDL_HIDAPI_Device *device) @@ -974,6 +1115,7 @@ static bool HIDAPI_DriverSteam_InitDevice(SDL_HIDAPI_Device *device) if (!ctx) { return false; } + ctx->device = device; device->context = ctx; #ifdef SDL_PLATFORM_WIN32 @@ -986,7 +1128,56 @@ static bool HIDAPI_DriverSteam_InitDevice(SDL_HIDAPI_Device *device) HIDAPI_SetDeviceName(device, "Steam Controller"); - return HIDAPI_JoystickConnected(device, NULL); + // If this is a wireless dongle, request a wireless state update + if (IsDongle(device->product_id)) { + unsigned char buf[65]; + int res; + + buf[0] = 0; + buf[1] = ID_DONGLE_GET_WIRELESS_STATE; + res = SetFeatureReport(device, buf, 2); + if (res < 0) { + return SDL_SetError("Failed to send ID_DONGLE_GET_WIRELESS_STATE request"); + } + + for (int attempt = 0; attempt < 5; ++attempt) { + uint8_t data[128]; + + res = ReadSteamController(device->dev, data, sizeof(data)); + if (res == 0) { + SDL_Delay(1); + continue; + } + if (res < 0) { + break; + } + +#ifdef DEBUG_STEAM_PROTOCOL + HIDAPI_DumpPacket("Initial dongle packet: size = %d", data, res); +#endif + + if (D0G_IS_WIRELESS_CONNECT(data, res)) { + ctx->connected = true; + break; + } else if (D0G_IS_WIRELESS_DISCONNECT(data, res)) { + ctx->connected = false; + break; + } + } + + SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_STEAM_PAIRING_ENABLED, + SDL_PairingEnabledHintChanged, ctx); + } else { + // Wired and BLE controllers are always connected if HIDAPI can see them + ctx->connected = true; + } + + if (ctx->connected) { + return HIDAPI_JoystickConnected(device, NULL); + } else { + // We will enumerate any attached controllers in UpdateDevice() + return true; + } } static int HIDAPI_DriverSteam_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id) @@ -998,6 +1189,42 @@ static void HIDAPI_DriverSteam_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, S { } +static bool SetHomeLED(SDL_HIDAPI_Device *device, Uint8 value) +{ + unsigned char buf[65]; + int nSettings = 0; + + SDL_memset(buf, 0, 65); + buf[1] = ID_SET_SETTINGS_VALUES; + ADD_SETTING(SETTING_LED_USER_BRIGHTNESS, value); + buf[2] = (unsigned char)(nSettings * 3); + if (SetFeatureReport(device, buf, 3 + nSettings * 3) < 0) { + return SDL_SetError("Couldn't write feature report"); + } + return true; +} + +static void SDLCALL SDL_HomeLEDHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint) +{ + SDL_DriverSteam_Context *ctx = (SDL_DriverSteam_Context *)userdata; + + if (hint && *hint) { + int value; + + if (SDL_strchr(hint, '.') != NULL) { + value = (int)(100.0f * SDL_atof(hint)); + if (value > 255) { + value = 255; + } + } else if (SDL_GetStringBoolean(hint, true)) { + value = 100; + } else { + value = 0; + } + SetHomeLED(ctx->device, (Uint8)value); + } +} + static bool HIDAPI_DriverSteam_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) { SDL_DriverSteam_Context *ctx = (SDL_DriverSteam_Context *)device->context; @@ -1010,7 +1237,7 @@ static bool HIDAPI_DriverSteam_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joyst SDL_zero(ctx->m_state); SDL_zero(ctx->m_last_state); - if (!ResetSteamController(device->dev, false, &ctx->update_rate_in_us)) { + if (!ResetSteamController(device, false, &ctx->update_rate_in_us)) { SDL_SetError("Couldn't reset controller"); return false; } @@ -1018,16 +1245,23 @@ static bool HIDAPI_DriverSteam_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joyst update_rate_in_hz = 1000000.0f / ctx->update_rate_in_us; } - InitializeSteamControllerPacketAssembler(&ctx->m_assembler); + InitializeSteamControllerPacketAssembler(&ctx->m_assembler, device->is_bluetooth); // Initialize the joystick capabilities joystick->nbuttons = SDL_GAMEPAD_NUM_STEAM_BUTTONS; joystick->naxes = SDL_GAMEPAD_AXIS_COUNT; joystick->nhats = 1; + if (IsDongle(device->product_id)) { + joystick->connection_state = SDL_JOYSTICK_CONNECTION_WIRELESS; + } + SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_GYRO, update_rate_in_hz); SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL, update_rate_in_hz); + SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_STEAM_HOME_LED, + SDL_HomeLEDHintChanged, ctx); + return true; } @@ -1056,6 +1290,12 @@ static bool HIDAPI_DriverSteam_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joy static bool HIDAPI_DriverSteam_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, const void *data, int size) { + if (size == 65) { + if (SetFeatureReport(device, data, size) < 0) { + return SDL_SetError("Couldn't write feature report"); + } + return true; + } return SDL_Unsupported(); } @@ -1073,7 +1313,7 @@ static bool HIDAPI_DriverSteam_SetSensorsEnabled(SDL_HIDAPI_Device *device, SDL_ ADD_SETTING(SETTING_IMU_MODE, SETTING_GYRO_MODE_OFF); } buf[2] = (unsigned char)(nSettings * 3); - if (SetFeatureReport(device->dev, buf, 3 + nSettings * 3) < 0) { + if (SetFeatureReport(device, buf, 3 + nSettings * 3) < 0) { return SDL_SetError("Couldn't write feature report"); } @@ -1082,6 +1322,33 @@ static bool HIDAPI_DriverSteam_SetSensorsEnabled(SDL_HIDAPI_Device *device, SDL_ return true; } +static bool ControllerConnected(SDL_HIDAPI_Device *device, SDL_Joystick **joystick) +{ + SDL_DriverSteam_Context *ctx = (SDL_DriverSteam_Context *)device->context; + + if (!HIDAPI_JoystickConnected(device, NULL)) { + return false; + } + + // We'll automatically accept this controller if we're in pairing mode + HIDAPI_DriverSteam_CommitPairing(ctx); + + *joystick = SDL_GetJoystickFromID(device->joysticks[0]); + ctx->connected = true; + return true; +} + +static void ControllerDisconnected(SDL_HIDAPI_Device *device, SDL_Joystick **joystick) +{ + SDL_DriverSteam_Context *ctx = (SDL_DriverSteam_Context *)device->context; + + if (device->joysticks) { + HIDAPI_JoystickDisconnected(device, device->joysticks[0]); + } + ctx->connected = false; + *joystick = NULL; +} + static bool HIDAPI_DriverSteam_UpdateDevice(SDL_HIDAPI_Device *device) { SDL_DriverSteam_Context *ctx = (SDL_DriverSteam_Context *)device->context; @@ -1089,8 +1356,10 @@ static bool HIDAPI_DriverSteam_UpdateDevice(SDL_HIDAPI_Device *device) if (device->num_joysticks > 0) { joystick = SDL_GetJoystickFromID(device->joysticks[0]); - } else { - return false; + } + + if (ctx->pairing_time) { + HIDAPI_DriverSteam_RenewPairingState(ctx); } for (;;) { @@ -1102,21 +1371,31 @@ static bool HIDAPI_DriverSteam_UpdateDevice(SDL_HIDAPI_Device *device) if (r == 0) { break; } - - if (!joystick) { - continue; + if (r < 0) { + // Failed to read from controller + ControllerDisconnected(device, &joystick); + return false; } - nPacketLength = 0; - if (r > 0) { - nPacketLength = WriteSegmentToSteamControllerPacketAssembler(&ctx->m_assembler, data, r); - } +#ifdef DEBUG_STEAM_PROTOCOL + HIDAPI_DumpPacket("Steam Controller packet: size = %d", data, r); +#endif + nPacketLength = WriteSegmentToSteamControllerPacketAssembler(&ctx->m_assembler, data, r); pPacket = ctx->m_assembler.uBuffer; if (nPacketLength > 0 && UpdateSteamControllerState(pPacket, nPacketLength, &ctx->m_state)) { Uint64 timestamp = SDL_GetTicksNS(); + if (!ctx->connected) { + // Maybe we missed a wireless status packet? + ControllerConnected(device, &joystick); + } + + if (!joystick) { + continue; + } + if (ctx->m_state.ulButtons != ctx->m_last_state.ulButtons) { Uint8 hat = 0; @@ -1197,12 +1476,10 @@ static bool HIDAPI_DriverSteam_UpdateDevice(SDL_HIDAPI_Device *device) } ctx->m_last_state = ctx->m_state; - } - - if (r <= 0) { - // Failed to read from controller - HIDAPI_JoystickDisconnected(device, device->joysticks[0]); - return false; + } else if (!ctx->connected && D0G_IS_WIRELESS_CONNECT(pPacket, nPacketLength)) { + ControllerConnected(device, &joystick); + } else if (ctx->connected && D0G_IS_WIRELESS_DISCONNECT(pPacket, nPacketLength)) { + ControllerDisconnected(device, &joystick); } } return true; @@ -1210,11 +1487,24 @@ static bool HIDAPI_DriverSteam_UpdateDevice(SDL_HIDAPI_Device *device) static void HIDAPI_DriverSteam_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) { - CloseSteamController(device->dev); + SDL_DriverSteam_Context *ctx = (SDL_DriverSteam_Context *)device->context; + + SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_STEAM_HOME_LED, + SDL_HomeLEDHintChanged, ctx); + + CloseSteamController(device); } static void HIDAPI_DriverSteam_FreeDevice(SDL_HIDAPI_Device *device) { + SDL_DriverSteam_Context *ctx = (SDL_DriverSteam_Context *)device->context; + + if (IsDongle(device->product_id)) { + SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_STEAM_PAIRING_ENABLED, + SDL_PairingEnabledHintChanged, ctx); + + HIDAPI_DriverSteam_SetPairingState(ctx, false); + } } SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverSteam = { diff --git a/libs/SDL3/src/joystick/hidapi/SDL_hidapi_xbox360w.c b/libs/SDL3/src/joystick/hidapi/SDL_hidapi_xbox360w.c index b1114f03..1d2a1ee9 100644 --- a/libs/SDL3/src/joystick/hidapi/SDL_hidapi_xbox360w.c +++ b/libs/SDL3/src/joystick/hidapi/SDL_hidapi_xbox360w.c @@ -178,6 +178,7 @@ static bool HIDAPI_DriverXbox360W_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Jo joystick->nbuttons = 11; joystick->naxes = SDL_GAMEPAD_AXIS_COUNT; joystick->nhats = 1; + joystick->connection_state = SDL_JOYSTICK_CONNECTION_WIRELESS; return true; } diff --git a/libs/SDL3/src/joystick/hidapi/SDL_hidapijoystick.c b/libs/SDL3/src/joystick/hidapi/SDL_hidapijoystick.c index 3a0f8169..1b348f1e 100644 --- a/libs/SDL3/src/joystick/hidapi/SDL_hidapijoystick.c +++ b/libs/SDL3/src/joystick/hidapi/SDL_hidapijoystick.c @@ -1285,6 +1285,44 @@ bool HIDAPI_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, return result; } +char *HIDAPI_GetDeviceProductName(Uint16 vendor_id, Uint16 product_id) +{ + SDL_HIDAPI_Device *device; + char *name = NULL; + + SDL_LockJoysticks(); + for (device = SDL_HIDAPI_devices; device; device = device->next) { + if (vendor_id == device->vendor_id && product_id == device->product_id) { + if (device->product_string) { + name = SDL_strdup(device->product_string); + } + break; + } + } + SDL_UnlockJoysticks(); + + return name; +} + +char *HIDAPI_GetDeviceManufacturerName(Uint16 vendor_id, Uint16 product_id) +{ + SDL_HIDAPI_Device *device; + char *name = NULL; + + SDL_LockJoysticks(); + for (device = SDL_HIDAPI_devices; device; device = device->next) { + if (vendor_id == device->vendor_id && product_id == device->product_id) { + if (device->manufacturer_string) { + name = SDL_strdup(device->manufacturer_string); + } + break; + } + } + SDL_UnlockJoysticks(); + + return name; +} + SDL_JoystickType HIDAPI_GetJoystickTypeFromGUID(SDL_GUID guid) { SDL_HIDAPI_Device *device; diff --git a/libs/SDL3/src/joystick/hidapi/SDL_hidapijoystick_c.h b/libs/SDL3/src/joystick/hidapi/SDL_hidapijoystick_c.h index 5ec0a393..1e085737 100644 --- a/libs/SDL3/src/joystick/hidapi/SDL_hidapijoystick_c.h +++ b/libs/SDL3/src/joystick/hidapi/SDL_hidapijoystick_c.h @@ -32,7 +32,7 @@ #define SDL_JOYSTICK_HIDAPI_PS4 #define SDL_JOYSTICK_HIDAPI_PS5 #define SDL_JOYSTICK_HIDAPI_STADIA -#define SDL_JOYSTICK_HIDAPI_STEAM // Simple support for BLE Steam Controller, hint is disabled by default +#define SDL_JOYSTICK_HIDAPI_STEAM #define SDL_JOYSTICK_HIDAPI_STEAMDECK #define SDL_JOYSTICK_HIDAPI_SWITCH #define SDL_JOYSTICK_HIDAPI_WII @@ -160,6 +160,12 @@ extern bool HIDAPI_IsDeviceTypePresent(SDL_GamepadType type); // Return true if a HID device is present and supported as a joystick extern bool HIDAPI_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name); +// Return the name of a connected device, which should be freed with SDL_free(), or NULL if it's not available +extern char *HIDAPI_GetDeviceProductName(Uint16 vendor_id, Uint16 product_id); + +// Return the manufacturer of a connected device, which should be freed with SDL_free(), or NULL if it's not available +extern char *HIDAPI_GetDeviceManufacturerName(Uint16 vendor_id, Uint16 product_id); + // Return the type of a joystick if it's present and supported extern SDL_JoystickType HIDAPI_GetJoystickTypeFromGUID(SDL_GUID guid); diff --git a/libs/SDL3/src/joystick/linux/SDL_sysjoystick.c b/libs/SDL3/src/joystick/linux/SDL_sysjoystick.c index d4a1cedd..ed7cf8f2 100644 --- a/libs/SDL3/src/joystick/linux/SDL_sysjoystick.c +++ b/libs/SDL3/src/joystick/linux/SDL_sysjoystick.c @@ -46,7 +46,6 @@ #include "../SDL_sysjoystick.h" #include "../SDL_joystick_c.h" #include "../usb_ids.h" -#include "../steam/SDL_steamcontroller.h" #include "SDL_sysjoystick_c.h" #include "../hidapi/SDL_hidapijoystick_c.h" @@ -158,9 +157,6 @@ typedef struct SDL_joylist_item struct joystick_hwdata *hwdata; struct SDL_joylist_item *next; - // Steam Controller support - bool m_bSteamController; - bool checked_mapping; SDL_GamepadMapping *mapping; } SDL_joylist_item; @@ -216,6 +212,11 @@ static bool IsVirtualJoystick(Uint16 vendor, Uint16 product, Uint16 version, con } return false; } +#else +static bool IsVirtualJoystick(Uint16 vendor, Uint16 product, Uint16 version, const char *name) +{ + return false; +} #endif // SDL_JOYSTICK_HIDAPI static bool GetSteamVirtualGamepadSlot(int fd, int *slot) @@ -659,60 +660,6 @@ static void HandlePendingRemovals(void) } } -static bool SteamControllerConnectedCallback(const char *name, SDL_GUID guid, SDL_JoystickID *device_instance) -{ - SDL_joylist_item *item; - - item = (SDL_joylist_item *)SDL_calloc(1, sizeof(SDL_joylist_item)); - if (!item) { - return false; - } - - item->path = SDL_strdup(""); - item->name = SDL_strdup(name); - item->guid = guid; - item->m_bSteamController = true; - - if ((!item->path) || (!item->name)) { - FreeJoylistItem(item); - return false; - } - - *device_instance = item->device_instance = SDL_GetNextObjectID(); - SDL_LockJoysticks(); - if (!SDL_joylist_tail) { - SDL_joylist = SDL_joylist_tail = item; - } else { - SDL_joylist_tail->next = item; - SDL_joylist_tail = item; - } - - // Need to increment the joystick count before we post the event - ++numjoysticks; - - SDL_PrivateJoystickAdded(item->device_instance); - SDL_UnlockJoysticks(); - - return true; -} - -static void SteamControllerDisconnectedCallback(SDL_JoystickID device_instance) -{ - SDL_joylist_item *item; - SDL_joylist_item *prev = NULL; - - SDL_LockJoysticks(); - for (item = SDL_joylist; item; item = item->next) { - // found it, remove it. - if (item->device_instance == device_instance) { - RemoveJoylistItem(item, prev); - break; - } - prev = item; - } - SDL_UnlockJoysticks(); -} - static bool StrIsInteger(const char *string) { const char *p; @@ -1014,8 +961,6 @@ static void LINUX_JoystickDetect(void) } HandlePendingRemovals(); - - SDL_UpdateSteamControllers(); } static bool LINUX_JoystickIsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name) @@ -1051,9 +996,6 @@ static bool LINUX_JoystickInit(void) SDL_free(envcopy); } - SDL_InitSteamControllers(SteamControllerConnectedCallback, - SteamControllerDisconnectedCallback); - // Force immediate joystick detection if using fallback last_joy_detect_time = 0; last_input_dir_mtime = 0; @@ -1514,52 +1456,43 @@ static bool PrepareJoystickHwdata(SDL_Joystick *joystick, SDL_joylist_item *item joystick->hwdata->item_sensor = item_sensor; joystick->hwdata->guid = item->guid; joystick->hwdata->effect.id = -1; - joystick->hwdata->m_bSteamController = item->m_bSteamController; SDL_memset(joystick->hwdata->key_map, 0xFF, sizeof(joystick->hwdata->key_map)); SDL_memset(joystick->hwdata->abs_map, 0xFF, sizeof(joystick->hwdata->abs_map)); - if (item->m_bSteamController) { - joystick->hwdata->fd = -1; - joystick->hwdata->fd_sensor = -1; - SDL_GetSteamControllerInputs(&joystick->nbuttons, - &joystick->naxes, - &joystick->nhats); - } else { - int fd = -1, fd_sensor = -1; - // Try read-write first, so we can do rumble - fd = open(item->path, O_RDWR | O_CLOEXEC, 0); - if (fd < 0) { - // Try read-only again, at least we'll get events in this case - fd = open(item->path, O_RDONLY | O_CLOEXEC, 0); - } - if (fd < 0) { - return SDL_SetError("Unable to open %s", item->path); - } - // If opening sensor fail, continue with buttons and axes only - if (item_sensor) { - fd_sensor = open(item_sensor->path, O_RDONLY | O_CLOEXEC, 0); - } - - joystick->hwdata->fd = fd; - joystick->hwdata->fd_sensor = fd_sensor; - joystick->hwdata->fname = SDL_strdup(item->path); - if (!joystick->hwdata->fname) { - close(fd); - if (fd_sensor >= 0) { - close(fd_sensor); - } - return false; - } - - // Set the joystick to non-blocking read mode - fcntl(fd, F_SETFL, O_NONBLOCK); - if (fd_sensor >= 0) { - fcntl(fd_sensor, F_SETFL, O_NONBLOCK); - } - - // Get the number of buttons and axes on the joystick - ConfigJoystick(joystick, fd, fd_sensor); + int fd = -1, fd_sensor = -1; + // Try read-write first, so we can do rumble + fd = open(item->path, O_RDWR | O_CLOEXEC, 0); + if (fd < 0) { + // Try read-only again, at least we'll get events in this case + fd = open(item->path, O_RDONLY | O_CLOEXEC, 0); } + if (fd < 0) { + return SDL_SetError("Unable to open %s", item->path); + } + // If opening sensor fail, continue with buttons and axes only + if (item_sensor) { + fd_sensor = open(item_sensor->path, O_RDONLY | O_CLOEXEC, 0); + } + + joystick->hwdata->fd = fd; + joystick->hwdata->fd_sensor = fd_sensor; + joystick->hwdata->fname = SDL_strdup(item->path); + if (!joystick->hwdata->fname) { + close(fd); + if (fd_sensor >= 0) { + close(fd_sensor); + } + return false; + } + + // Set the joystick to non-blocking read mode + fcntl(fd, F_SETFL, O_NONBLOCK); + if (fd_sensor >= 0) { + fcntl(fd_sensor, F_SETFL, O_NONBLOCK); + } + + // Get the number of buttons and axes on the joystick + ConfigJoystick(joystick, fd, fd_sensor); return true; } @@ -2198,11 +2131,6 @@ static void LINUX_JoystickUpdate(SDL_Joystick *joystick) SDL_AssertJoysticksLocked(); - if (joystick->hwdata->m_bSteamController) { - SDL_UpdateSteamController(joystick); - return; - } - if (joystick->hwdata->classic) { HandleClassicEvents(joystick); } else { @@ -2289,8 +2217,6 @@ static void LINUX_JoystickQuit(void) SDL_UDEV_Quit(); } #endif - - SDL_QuitSteamControllers(); } /* diff --git a/libs/SDL3/src/joystick/steam/SDL_steamcontroller.h b/libs/SDL3/src/joystick/steam/SDL_steamcontroller.h deleted file mode 100644 index 8468aa0a..00000000 --- a/libs/SDL3/src/joystick/steam/SDL_steamcontroller.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2024 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ - -#ifndef SDL_steamcontroller_h_ -#define SDL_steamcontroller_h_ - -#include "SDL_internal.h" - -typedef bool (*SteamControllerConnectedCallback_t)(const char *name, SDL_GUID guid, SDL_JoystickID *device_instance); -typedef void (*SteamControllerDisconnectedCallback_t)(SDL_JoystickID device_instance); - -void SDL_InitSteamControllers(SteamControllerConnectedCallback_t connectedCallback, - SteamControllerDisconnectedCallback_t disconnectedCallback); -void SDL_GetSteamControllerInputs(int *nbuttons, int *naxes, int *nhats); -void SDL_UpdateSteamControllers(void); -void SDL_UpdateSteamController(SDL_Joystick *joystick); -void SDL_QuitSteamControllers(void); - -#endif // SDL_steamcontroller_h_ diff --git a/libs/SDL3/src/joystick/usb_ids.h b/libs/SDL3/src/joystick/usb_ids.h index 202e3edd..9c202d97 100644 --- a/libs/SDL3/src/joystick/usb_ids.h +++ b/libs/SDL3/src/joystick/usb_ids.h @@ -111,6 +111,7 @@ #define USB_PRODUCT_RAZER_WOLVERINE_V2_PRO_PS5_WIRELESS 0x100c #define USB_PRODUCT_RAZER_WOLVERINE_V2_PRO_XBOX_WIRED 0x1010 #define USB_PRODUCT_RAZER_WOLVERINE_V2_PRO_XBOX_WIRELESS 0x1011 +#define USB_PRODUCT_RAZER_WOLVERINE_V3_PRO 0x0a3f #define USB_PRODUCT_ROG_RAIKIRI 0x1a38 #define USB_PRODUCT_SAITEK_CYBORG_V3 0xf622 #define USB_PRODUCT_SHANWAN_DS3 0x0523 @@ -125,6 +126,7 @@ #define USB_PRODUCT_THRUSTMASTER_ESWAPX_PRO 0xd012 #define USB_PRODUCT_TURTLE_BEACH_SERIES_X_REACT_R 0x7013 #define USB_PRODUCT_TURTLE_BEACH_SERIES_X_RECON 0x7009 +#define USB_PRODUCT_VALVE_STEAM_CONTROLLER_DONGLE 0x1142 #define USB_PRODUCT_VICTRIX_FS_PRO 0x0203 #define USB_PRODUCT_VICTRIX_FS_PRO_V2 0x0207 #define USB_PRODUCT_XBOX360_XUSB_CONTROLLER 0x02a1 // XUSB driver software PID @@ -150,6 +152,9 @@ #define USB_PRODUCT_XBOX_SERIES_X_PDP_AFTERGLOW 0x02da #define USB_PRODUCT_XBOX_SERIES_X_PDP_BLUE 0x02d9 #define USB_PRODUCT_XBOX_SERIES_X_POWERA_FUSION_PRO2 0x4001 +#define USB_PRODUCT_XBOX_SERIES_X_POWERA_FUSION_PRO4 0x400b +#define USB_PRODUCT_XBOX_SERIES_X_POWERA_FUSION_PRO_WIRELESS_USB 0x4014 +#define USB_PRODUCT_XBOX_SERIES_X_POWERA_FUSION_PRO_WIRELESS_DONGLE 0x4016 #define USB_PRODUCT_XBOX_SERIES_X_POWERA_MOGA_XP_ULTRA 0x890b #define USB_PRODUCT_XBOX_SERIES_X_POWERA_SPECTRA 0x4002 #define USB_PRODUCT_XBOX_SERIES_X_VICTRIX_GAMBIT 0x02d6 diff --git a/libs/SDL3/src/joystick/windows/SDL_dinputjoystick.c b/libs/SDL3/src/joystick/windows/SDL_dinputjoystick.c index 03f2704f..05a123f5 100644 --- a/libs/SDL3/src/joystick/windows/SDL_dinputjoystick.c +++ b/libs/SDL3/src/joystick/windows/SDL_dinputjoystick.c @@ -267,14 +267,22 @@ static bool SDL_IsXInputDevice(Uint16 vendor_id, Uint16 product_id, const char * return false; } -static bool QueryDeviceName(LPDIRECTINPUTDEVICE8 device, char **device_name) +static bool QueryDeviceName(LPDIRECTINPUTDEVICE8 device, Uint16 vendor_id, Uint16 product_id, char **manufacturer_string, char **product_string) { DIPROPSTRING dipstr; - if (!device || !device_name) { + if (!device || !manufacturer_string || !product_string) { return false; } +#ifdef SDL_JOYSTICK_HIDAPI + *manufacturer_string = HIDAPI_GetDeviceManufacturerName(vendor_id, product_id); + *product_string = HIDAPI_GetDeviceProductName(vendor_id, product_id); + if (*product_string) { + return true; + } +#endif + dipstr.diph.dwSize = sizeof(dipstr); dipstr.diph.dwHeaderSize = sizeof(dipstr.diph); dipstr.diph.dwObj = 0; @@ -284,7 +292,8 @@ static bool QueryDeviceName(LPDIRECTINPUTDEVICE8 device, char **device_name) return false; } - *device_name = WIN_StringToUTF8(dipstr.wsz); + *manufacturer_string = NULL; + *product_string = WIN_StringToUTF8(dipstr.wsz); return true; } @@ -460,20 +469,21 @@ static BOOL CALLBACK EnumJoystickDetectCallback(LPCDIDEVICEINSTANCE pDeviceInsta Uint16 product = 0; Uint16 version = 0; char *hidPath = NULL; - char *name = NULL; + char *manufacturer_string = NULL; + char *product_string = NULL; LPDIRECTINPUTDEVICE8 device = NULL; // We are only supporting HID devices. CHECK(pDeviceInstance->dwDevType & DIDEVTYPE_HID); CHECK(SUCCEEDED(IDirectInput8_CreateDevice(dinput, &pDeviceInstance->guidInstance, &device, NULL))); - CHECK(QueryDeviceName(device, &name)); CHECK(QueryDevicePath(device, &hidPath)); CHECK(QueryDeviceInfo(device, &vendor, &product)); + CHECK(QueryDeviceName(device, vendor, product, &manufacturer_string, &product_string)); CHECK(!SDL_IsXInputDevice(vendor, product, hidPath)); - CHECK(!SDL_ShouldIgnoreJoystick(vendor, product, version, name)); - CHECK(!SDL_JoystickHandledByAnotherDriver(&SDL_WINDOWS_JoystickDriver, vendor, product, version, name)); + CHECK(!SDL_ShouldIgnoreJoystick(vendor, product, version, product_string)); + CHECK(!SDL_JoystickHandledByAnotherDriver(&SDL_WINDOWS_JoystickDriver, vendor, product, version, product_string)); pNewJoystick = *(JoyStick_DeviceData **)pContext; while (pNewJoystick) { @@ -507,13 +517,13 @@ static BOOL CALLBACK EnumJoystickDetectCallback(LPCDIDEVICEINSTANCE pDeviceInsta SDL_strlcpy(pNewJoystick->path, hidPath, SDL_arraysize(pNewJoystick->path)); SDL_memcpy(&pNewJoystick->dxdevice, pDeviceInstance, sizeof(DIDEVICEINSTANCE)); - pNewJoystick->joystickname = SDL_CreateJoystickName(vendor, product, NULL, name); + pNewJoystick->joystickname = SDL_CreateJoystickName(vendor, product, manufacturer_string, product_string); CHECK(pNewJoystick->joystickname); if (vendor && product) { - pNewJoystick->guid = SDL_CreateJoystickGUID(SDL_HARDWARE_BUS_USB, vendor, product, version, NULL, name, 0, 0); + pNewJoystick->guid = SDL_CreateJoystickGUID(SDL_HARDWARE_BUS_USB, vendor, product, version, manufacturer_string, product_string, 0, 0); } else { - pNewJoystick->guid = SDL_CreateJoystickGUID(SDL_HARDWARE_BUS_BLUETOOTH, vendor, product, version, NULL, name, 0, 0); + pNewJoystick->guid = SDL_CreateJoystickGUID(SDL_HARDWARE_BUS_BLUETOOTH, vendor, product, version, manufacturer_string, product_string, 0, 0); } WINDOWS_AddJoystickDevice(pNewJoystick); @@ -526,7 +536,8 @@ err: } SDL_free(hidPath); - SDL_free(name); + SDL_free(manufacturer_string); + SDL_free(product_string); if (device) { IDirectInputDevice8_Release(device); diff --git a/libs/SDL3/src/joystick/windows/SDL_windowsjoystick.c b/libs/SDL3/src/joystick/windows/SDL_windowsjoystick.c index b01d7a03..20ba28b0 100644 --- a/libs/SDL3/src/joystick/windows/SDL_windowsjoystick.c +++ b/libs/SDL3/src/joystick/windows/SDL_windowsjoystick.c @@ -20,7 +20,7 @@ */ #include "SDL_internal.h" -#if defined(SDL_JOYSTICK_DINPUT) || SDL_JOYSTICK_XINPUT +#if defined(SDL_JOYSTICK_DINPUT) || defined(SDL_JOYSTICK_XINPUT) /* DirectInput joystick driver; written by Glenn Maynard, based on Andrei de * A. Formiga's WINMM driver. diff --git a/libs/SDL3/src/locale/psp/SDL_syslocale.c b/libs/SDL3/src/locale/psp/SDL_syslocale.c new file mode 100644 index 00000000..3b799894 --- /dev/null +++ b/libs/SDL3/src/locale/psp/SDL_syslocale.c @@ -0,0 +1,78 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2024 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#include "SDL_internal.h" +#include "../SDL_syslocale.h" + +#include + +bool SDL_SYS_GetPreferredLocales(char *buf, size_t buflen) +{ + int current_locale_int = PSP_SYSTEMPARAM_LANGUAGE_ENGLISH; + + SDL_assert(buflen > 0); + + sceUtilityGetSystemParamInt(PSP_SYSTEMPARAM_ID_INT_LANGUAGE, ¤t_locale_int); + switch(current_locale_int) { + case PSP_SYSTEMPARAM_LANGUAGE_JAPANESE: + SDL_strlcpy(buf, "ja_JP", buflen); + break; + case PSP_SYSTEMPARAM_LANGUAGE_ENGLISH: + SDL_strlcpy(buf, "en_US", buflen); + break; + case PSP_SYSTEMPARAM_LANGUAGE_FRENCH: + SDL_strlcpy(buf, "fr_FR", buflen); + break; + case PSP_SYSTEMPARAM_LANGUAGE_SPANISH: + SDL_strlcpy(buf, "es_ES", buflen); + break; + case PSP_SYSTEMPARAM_LANGUAGE_GERMAN: + SDL_strlcpy(buf, "de_DE", buflen); + break; + case PSP_SYSTEMPARAM_LANGUAGE_ITALIAN: + SDL_strlcpy(buf, "it_IT", buflen); + break; + case PSP_SYSTEMPARAM_LANGUAGE_DUTCH: + SDL_strlcpy(buf, "nl_NL", buflen); + break; + case PSP_SYSTEMPARAM_LANGUAGE_PORTUGUESE: + SDL_strlcpy(buf, "pt_PT", buflen); + break; + case PSP_SYSTEMPARAM_LANGUAGE_RUSSIAN: + SDL_strlcpy(buf, "ru_RU", buflen); + break; + case PSP_SYSTEMPARAM_LANGUAGE_KOREAN: + SDL_strlcpy(buf, "ko_KR", buflen); + break; + case PSP_SYSTEMPARAM_LANGUAGE_CHINESE_TRADITIONAL: + SDL_strlcpy(buf, "zh_TW", buflen); + break; + case PSP_SYSTEMPARAM_LANGUAGE_CHINESE_SIMPLIFIED: + SDL_strlcpy(buf, "zh_CN", buflen); + break; + default: + SDL_strlcpy(buf, "en_US", buflen); + break; + } + return true; +} + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/libs/SDL3/src/main/ngage/SDL_sysmain_runapp.cpp b/libs/SDL3/src/main/ngage/SDL_sysmain_runapp.cpp deleted file mode 100644 index c675b130..00000000 --- a/libs/SDL3/src/main/ngage/SDL_sysmain_runapp.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/* - based on SDL_ngage_main.c, originally for SDL 1.2 by Hannu Viitala -*/ - -#include "SDL_internal.h" - -#ifdef SDL_PLATFORM_NGAGE - -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -int SDL_RunApp(int argc_, char* argv_[], SDL_main_func mainFunction, void * reserved) -{ - (void)argc_; (void)argv_; (void)reserved; - // Get the clean-up stack - CTrapCleanup *cleanup = CTrapCleanup::New(); - - // Arrange for multi-threaded operation - SpawnPosixServerThread(); - - // Get args and environment - int argc = 0; - char **argv = 0; - char **envp = 0; - - __crt0(argc, argv, envp); - - // Start the application! - - // Create stdlib - _REENT; - - // Set process and thread priority and name - - RThread currentThread; - RProcess thisProcess; - TParse exeName; - exeName.Set(thisProcess.FileName(), NULL, NULL); - currentThread.Rename(exeName.Name()); - currentThread.SetProcessPriority(EPriorityLow); - currentThread.SetPriority(EPriorityMuchLess); - - // Increase heap size - RHeap *newHeap = NULL; - RHeap *oldHeap = NULL; - TInt heapSize = 7500000; - int ret; - - newHeap = User::ChunkHeap(NULL, heapSize, heapSize, KMinHeapGrowBy); - - if (!newHeap) { - ret = 3; - goto cleanup; - } else { - oldHeap = User::SwitchHeap(newHeap); - // Call stdlib main - SDL_SetMainReady(); - ret = mainFunction(argc, argv); - } - -cleanup: - _cleanup(); - - CloseSTDLIB(); - delete cleanup; - return ret; -} - -#endif // SDL_PLATFORM_NGAGE diff --git a/libs/SDL3/src/render/SDL_render.c b/libs/SDL3/src/render/SDL_render.c index f55a1f75..6e8c7ad3 100644 --- a/libs/SDL3/src/render/SDL_render.c +++ b/libs/SDL3/src/render/SDL_render.c @@ -107,10 +107,6 @@ this should probably be removed at some point in the future. --ryan. */ #ifndef SDL_RENDER_DISABLED static const SDL_RenderDriver *render_drivers[] = { -// Temporarily list the GPU renderer first so we get testing feedback -#ifdef SDL_VIDEO_RENDER_GPU - &GPU_RenderDriver, -#endif #ifdef SDL_VIDEO_RENDER_D3D11 &D3D11_RenderDriver, #endif @@ -141,6 +137,9 @@ static const SDL_RenderDriver *render_drivers[] = { #ifdef SDL_VIDEO_RENDER_VULKAN &VULKAN_RenderDriver, #endif +#ifdef SDL_VIDEO_RENDER_GPU + &GPU_RenderDriver, +#endif #ifdef SDL_VIDEO_RENDER_SW &SW_RenderDriver, #endif @@ -1451,7 +1450,7 @@ SDL_Texture *SDL_CreateTextureWithProperties(SDL_Renderer *renderer, SDL_Propert renderer->textures = texture; if (SDL_ISPIXELFORMAT_FOURCC(texture->format)) { -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV texture->yuv = SDL_SW_CreateYUVTexture(texture->format, texture->colorspace, w, h); #else SDL_SetError("SDL not built with YUV support"); @@ -1972,7 +1971,7 @@ bool SDL_GetTextureScaleMode(SDL_Texture *texture, SDL_ScaleMode *scaleMode) return true; } -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV static bool SDL_UpdateTextureYUV(SDL_Texture *texture, const SDL_Rect *rect, const void *pixels, int pitch) { @@ -2084,7 +2083,7 @@ bool SDL_UpdateTexture(SDL_Texture *texture, const SDL_Rect *rect, const void *p if (real_rect.w == 0 || real_rect.h == 0) { return true; // nothing to do. -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV } else if (texture->yuv) { return SDL_UpdateTextureYUV(texture, &real_rect, pixels, pitch); #endif @@ -2099,7 +2098,7 @@ bool SDL_UpdateTexture(SDL_Texture *texture, const SDL_Rect *rect, const void *p } } -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV static bool SDL_UpdateTextureYUVPlanar(SDL_Texture *texture, const SDL_Rect *rect, const Uint8 *Yplane, int Ypitch, const Uint8 *Uplane, int Upitch, @@ -2208,7 +2207,7 @@ bool SDL_UpdateYUVTexture(SDL_Texture *texture, const SDL_Rect *rect, const Uint8 *Uplane, int Upitch, const Uint8 *Vplane, int Vpitch) { -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV SDL_Renderer *renderer; SDL_Rect real_rect; @@ -2274,7 +2273,7 @@ bool SDL_UpdateNVTexture(SDL_Texture *texture, const SDL_Rect *rect, const Uint8 *Yplane, int Ypitch, const Uint8 *UVplane, int UVpitch) { -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV SDL_Renderer *renderer; SDL_Rect real_rect; @@ -2330,7 +2329,7 @@ bool SDL_UpdateNVTexture(SDL_Texture *texture, const SDL_Rect *rect, #endif } -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV static bool SDL_LockTextureYUV(SDL_Texture *texture, const SDL_Rect *rect, void **pixels, int *pitch) { @@ -2367,7 +2366,7 @@ bool SDL_LockTexture(SDL_Texture *texture, const SDL_Rect *rect, void **pixels, rect = &full_rect; } -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV if (texture->yuv) { if (!FlushRenderCommandsIfTextureNeeded(texture)) { return false; @@ -2419,7 +2418,7 @@ bool SDL_LockTextureToSurface(SDL_Texture *texture, const SDL_Rect *rect, SDL_Su return true; } -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV static void SDL_UnlockTextureYUV(SDL_Texture *texture) { SDL_Texture *native = texture->native; @@ -2468,7 +2467,7 @@ void SDL_UnlockTexture(SDL_Texture *texture) if (texture->access != SDL_TEXTUREACCESS_STREAMING) { return; } -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV if (texture->yuv) { SDL_UnlockTextureYUV(texture); } else @@ -2789,6 +2788,29 @@ static void SDL_RenderLogicalPresentation(SDL_Renderer *renderer) } } +static bool SDL_RenderVectorFromWindow(SDL_Renderer *renderer, float window_dx, float window_dy, float *restrict dx, float *restrict dy) +{ + // Convert from window coordinates to pixels within the window + window_dx *= renderer->dpi_scale.x; + window_dy *= renderer->dpi_scale.y; + + // Convert from pixels within the window to pixels within the view + if (renderer->logical_presentation_mode != SDL_LOGICAL_PRESENTATION_DISABLED) { + const SDL_FRect *src = &renderer->logical_src_rect; + const SDL_FRect *dst = &renderer->logical_dst_rect; + window_dx = (window_dx * src->w) / dst->w; + window_dy = (window_dy * src->h) / dst->h; + } + + const SDL_RenderViewState *view = &renderer->main_view; + window_dx /= view->scale.x; + window_dy /= view->scale.y; + + *dx = window_dx; + *dy = window_dy; + return true; +} + bool SDL_RenderCoordinatesFromWindow(SDL_Renderer *renderer, float window_x, float window_y, float *x, float *y) { float render_x, render_y; @@ -2857,37 +2879,7 @@ bool SDL_ConvertEventToRenderCoordinates(SDL_Renderer *renderer, SDL_Event *even SDL_Window *window = SDL_GetWindowFromID(event->motion.windowID); if (window == renderer->window) { SDL_RenderCoordinatesFromWindow(renderer, event->motion.x, event->motion.y, &event->motion.x, &event->motion.y); - - if (event->motion.xrel != 0.0f) { - // Convert from window coordinates to pixels within the window - float scale = renderer->dpi_scale.x; - - // Convert from pixels within the window to pixels within the view - if (renderer->logical_presentation_mode != SDL_LOGICAL_PRESENTATION_DISABLED) { - const SDL_FRect *src = &renderer->logical_src_rect; - const SDL_FRect *dst = &renderer->logical_dst_rect; - scale = (scale * src->w) / dst->w; - } - - // Convert from pixels within the view to render coordinates - scale = (scale / renderer->main_view.scale.x); - event->motion.xrel *= scale; - } - if (event->motion.yrel != 0.0f) { - // Convert from window coordinates to pixels within the window - float scale = renderer->dpi_scale.y; - - // Convert from pixels within the window to pixels within the view - if (renderer->logical_presentation_mode != SDL_LOGICAL_PRESENTATION_DISABLED) { - const SDL_FRect *src = &renderer->logical_src_rect; - const SDL_FRect *dst = &renderer->logical_dst_rect; - scale = (scale * src->h) / dst->h; - } - - // Convert from pixels within the view to render coordinates - scale = (scale / renderer->main_view.scale.y); - event->motion.yrel *= scale; - } + SDL_RenderVectorFromWindow(renderer, event->motion.xrel, event->motion.yrel, &event->motion.xrel, &event->motion.yrel); } } else if (event->type == SDL_EVENT_MOUSE_BUTTON_DOWN || event->type == SDL_EVENT_MOUSE_BUTTON_UP) { @@ -2913,6 +2905,7 @@ bool SDL_ConvertEventToRenderCoordinates(SDL_Renderer *renderer, SDL_Event *even return false; } SDL_RenderCoordinatesFromWindow(renderer, event->tfinger.x * w, event->tfinger.y * h, &event->tfinger.x, &event->tfinger.y); + SDL_RenderVectorFromWindow(renderer, event->tfinger.dx * w, event->tfinger.dy * h, &event->tfinger.dx, &event->tfinger.dy); } } else if (event->type == SDL_EVENT_PEN_MOTION) { SDL_Window *window = SDL_GetWindowFromID(event->pmotion.windowID); @@ -3919,6 +3912,123 @@ bool SDL_RenderTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_F return SDL_RenderTextureInternal(renderer, texture, &real_srcrect, &real_dstrect); } +bool SDL_RenderTextureAffine(SDL_Renderer *renderer, SDL_Texture *texture, + const SDL_FRect *srcrect, const SDL_FPoint *origin, const SDL_FPoint *right, const SDL_FPoint *down) +{ + SDL_FRect real_srcrect; + SDL_FRect real_dstrect; + bool result; + + CHECK_RENDERER_MAGIC(renderer, false); + CHECK_TEXTURE_MAGIC(texture, false); + + if (renderer != texture->renderer) { + return SDL_SetError("Texture was not created with this renderer"); + } + if (!renderer->QueueCopyEx && !renderer->QueueGeometry) { + return SDL_SetError("Renderer does not support RenderCopyEx"); + } + +#if DONT_DRAW_WHILE_HIDDEN + // Don't draw while we're hidden + if (renderer->hidden) { + return true; + } +#endif + + real_srcrect.x = 0.0f; + real_srcrect.y = 0.0f; + real_srcrect.w = (float)texture->w; + real_srcrect.h = (float)texture->h; + if (srcrect) { + if (!SDL_GetRectIntersectionFloat(srcrect, &real_srcrect, &real_srcrect)) { + return true; + } + } + + GetRenderViewportSize(renderer, &real_dstrect); + + if (texture->native) { + texture = texture->native; + } + + texture->last_command_generation = renderer->render_command_generation; + + const float scale_x = renderer->view->current_scale.x; + const float scale_y = renderer->view->current_scale.y; + + { + float xy[8]; + const int xy_stride = 2 * sizeof(float); + float uv[8]; + const int uv_stride = 2 * sizeof(float); + const int num_vertices = 4; + const int *indices = rect_index_order; + const int num_indices = 6; + const int size_indices = 4; + + float minu = real_srcrect.x / texture->w; + float minv = real_srcrect.y / texture->h; + float maxu = (real_srcrect.x + real_srcrect.w) / texture->w; + float maxv = (real_srcrect.y + real_srcrect.h) / texture->h; + + uv[0] = minu; + uv[1] = minv; + uv[2] = maxu; + uv[3] = minv; + uv[4] = maxu; + uv[5] = maxv; + uv[6] = minu; + uv[7] = maxv; + + // (minx, miny) + if (origin) { + xy[0] = origin->x; + xy[1] = origin->y; + } else { + xy[0] = real_dstrect.x; + xy[1] = real_dstrect.y; + } + + // (maxx, miny) + if (right) { + xy[2] = right->x; + xy[3] = right->y; + } else { + xy[2] = real_dstrect.x + real_dstrect.w; + xy[3] = real_dstrect.y; + } + + // (minx, maxy) + if (down) { + xy[6] = down->x; + xy[7] = down->y; + } else { + xy[6] = real_dstrect.x; + xy[7] = real_dstrect.y + real_dstrect.h; + } + + // (maxx, maxy) + if (origin || right || down) { + xy[4] = xy[2] + xy[6] - xy[0]; + xy[5] = xy[3] + xy[7] - xy[1]; + } else { + xy[4] = real_dstrect.x + real_dstrect.w; + xy[5] = real_dstrect.y + real_dstrect.h; + } + + result = QueueCmdGeometry( + renderer, texture, + xy, xy_stride, + &texture->color, 0 /* color_stride */, + uv, uv_stride, + num_vertices, indices, num_indices, size_indices, + scale_x, scale_y, SDL_TEXTURE_ADDRESS_CLAMP + ); + } + return result; +} + bool SDL_RenderTextureRotated(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_FRect *srcrect, const SDL_FRect *dstrect, const double angle, const SDL_FPoint *center, const SDL_FlipMode flip) @@ -4264,15 +4374,28 @@ bool SDL_RenderTexture9Grid(SDL_Renderer *renderer, SDL_Texture *texture, const } if (scale <= 0.0f || scale == 1.0f) { - dst_left_width = left_width; - dst_right_width = right_width; - dst_top_height = top_height; - dst_bottom_height = bottom_height; + dst_left_width = SDL_ceilf(left_width); + dst_right_width = SDL_ceilf(right_width); + dst_top_height = SDL_ceilf(top_height); + dst_bottom_height = SDL_ceilf(bottom_height); } else { - dst_left_width = (left_width * scale); - dst_right_width = (right_width * scale); - dst_top_height = (top_height * scale); - dst_bottom_height = (bottom_height * scale); + dst_left_width = SDL_ceilf(left_width * scale); + dst_right_width = SDL_ceilf(right_width * scale); + dst_top_height = SDL_ceilf(top_height * scale); + dst_bottom_height = SDL_ceilf(bottom_height * scale); + } + + // Center + curr_src.x = srcrect->x + left_width; + curr_src.y = srcrect->y + top_height; + curr_src.w = srcrect->w - left_width - right_width; + curr_src.h = srcrect->h - top_height - bottom_height; + curr_dst.x = dstrect->x + dst_left_width; + curr_dst.y = dstrect->y + dst_top_height; + curr_dst.w = dstrect->w - dst_left_width - dst_right_width; + curr_dst.h = dstrect->h - dst_top_height - dst_bottom_height; + if (!SDL_RenderTexture(renderer, texture, &curr_src, &curr_dst)) { + return false; } // Upper-left corner @@ -4353,19 +4476,6 @@ bool SDL_RenderTexture9Grid(SDL_Renderer *renderer, SDL_Texture *texture, const return false; } - // Center - curr_src.x = srcrect->x + left_width; - curr_src.y = srcrect->y + top_height; - curr_src.w = srcrect->w - left_width - right_width; - curr_src.h = srcrect->h - top_height - bottom_height; - curr_dst.x = dstrect->x + dst_left_width; - curr_dst.y = dstrect->y + dst_top_height; - curr_dst.w = dstrect->w - dst_left_width - dst_right_width; - curr_dst.h = dstrect->h - dst_top_height - dst_bottom_height; - if (!SDL_RenderTexture(renderer, texture, &curr_src, &curr_dst)) { - return false; - } - return true; } @@ -5032,7 +5142,7 @@ static void SDL_DestroyTextureInternal(SDL_Texture *texture, bool is_destroying) if (texture->native) { SDL_DestroyTextureInternal(texture->native, is_destroying); } -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV if (texture->yuv) { SDL_SW_DestroyYUVTexture(texture->yuv); } @@ -5407,10 +5517,14 @@ static bool CreateDebugTextAtlas(SDL_Renderer *renderer) SDL_assert((row < rows) || ((row == rows) && (column == 0))); // make sure we didn't overflow the surface. // Convert temp surface into texture - renderer->debug_char_texture_atlas = SDL_CreateTextureFromSurface(renderer, atlas); + SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, atlas); + if (texture) { + SDL_SetTextureScaleMode(texture, SDL_SCALEMODE_NEAREST); + renderer->debug_char_texture_atlas = texture; + } SDL_DestroySurface(atlas); - return (renderer->debug_char_texture_atlas != NULL); + return texture != NULL; } static bool DrawDebugCharacter(SDL_Renderer *renderer, float x, float y, Uint32 c) @@ -5469,3 +5583,29 @@ bool SDL_RenderDebugText(SDL_Renderer *renderer, float x, float y, const char *s return result; } + +bool SDL_RenderDebugTextFormat(SDL_Renderer *renderer, float x, float y, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + + // fast path to avoid unnecessary allocation and copy. If you're going through the dynapi, there's a good chance + // you _always_ hit this path, since it probably had to process varargs before calling into the jumptable. + if (SDL_strcmp(fmt, "%s") == 0) { + const char *str = va_arg(ap, const char *); + va_end(ap); + return SDL_RenderDebugText(renderer, x, y, str); + } + + char *str = NULL; + const int rc = SDL_vasprintf(&str, fmt, ap); + va_end(ap); + + if (rc == -1) { + return false; + } + + const bool retval = SDL_RenderDebugText(renderer, x, y, str); + SDL_free(str); + return retval; +} diff --git a/libs/SDL3/src/render/SDL_sysrender.h b/libs/SDL3/src/render/SDL_sysrender.h index 381b516f..d3421cb4 100644 --- a/libs/SDL3/src/render/SDL_sysrender.h +++ b/libs/SDL3/src/render/SDL_sysrender.h @@ -203,7 +203,7 @@ struct SDL_Renderer bool (*UpdateTexture)(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *rect, const void *pixels, int pitch); -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV bool (*UpdateTextureYUV)(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *rect, const Uint8 *Yplane, int Ypitch, diff --git a/libs/SDL3/src/render/SDL_yuv_sw.c b/libs/SDL3/src/render/SDL_yuv_sw.c index e7e9fe4e..251330e0 100644 --- a/libs/SDL3/src/render/SDL_yuv_sw.c +++ b/libs/SDL3/src/render/SDL_yuv_sw.c @@ -22,7 +22,7 @@ // This is the software implementation of the YUV texture support -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV #include "SDL_yuv_sw_c.h" #include "../video/SDL_surface_c.h" diff --git a/libs/SDL3/src/render/direct3d/SDL_render_d3d.c b/libs/SDL3/src/render/direct3d/SDL_render_d3d.c index e757de08..2f2f9c43 100644 --- a/libs/SDL3/src/render/direct3d/SDL_render_d3d.c +++ b/libs/SDL3/src/render/direct3d/SDL_render_d3d.c @@ -65,7 +65,7 @@ typedef struct IDirect3DSurface9 *defaultRenderTarget; IDirect3DSurface9 *currentRenderTarget; void *d3dxDLL; -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV LPDIRECT3DPIXELSHADER9 shaders[NUM_SHADERS]; #endif LPDIRECT3DVERTEXBUFFER9 vertexBuffers[8]; @@ -93,7 +93,7 @@ typedef struct D3D9_Shader shader; const float *shader_params; -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV // YV12 texture support bool yuv; D3D_TextureRep utexture; @@ -546,7 +546,7 @@ static bool D3D_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_ if (!D3D_CreateTextureRep(data->device, &texturedata->texture, usage, texture->format, PixelFormatToD3DFMT(texture->format), texture->w, texture->h)) { return false; } -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV if (texture->format == SDL_PIXELFORMAT_YV12 || texture->format == SDL_PIXELFORMAT_IYUV) { texturedata->yuv = true; @@ -581,7 +581,7 @@ static bool D3D_RecreateTexture(SDL_Renderer *renderer, SDL_Texture *texture) if (!D3D_RecreateTextureRep(data->device, &texturedata->texture)) { return false; } -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV if (texturedata->yuv) { if (!D3D_RecreateTextureRep(data->device, &texturedata->utexture)) { return false; @@ -608,7 +608,7 @@ static bool D3D_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, if (!D3D_UpdateTextureRep(data->device, &texturedata->texture, rect->x, rect->y, rect->w, rect->h, pixels, pitch)) { return false; } -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV if (texturedata->yuv) { // Skip to the correct offset into the next texture pixels = (const void *)((const Uint8 *)pixels + rect->h * pitch); @@ -627,7 +627,7 @@ static bool D3D_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, return true; } -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV static bool D3D_UpdateTextureYUV(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *rect, const Uint8 *Yplane, int Ypitch, @@ -664,7 +664,7 @@ static bool D3D_LockTexture(SDL_Renderer *renderer, SDL_Texture *texture, if (!texturedata) { return SDL_SetError("Texture is not currently available"); } -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV texturedata->locked_rect = *rect; if (texturedata->yuv) { @@ -714,7 +714,7 @@ static void D3D_UnlockTexture(SDL_Renderer *renderer, SDL_Texture *texture) if (!texturedata) { return; } -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV if (texturedata->yuv) { const SDL_Rect *rect = &texturedata->locked_rect; void *pixels = @@ -971,7 +971,7 @@ static bool SetupTextureState(D3D_RenderData *data, SDL_Texture *texture, SDL_Te if (!BindTextureRep(data->device, &texturedata->texture, 0)) { return false; } -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV if (texturedata->yuv) { UpdateTextureScaleMode(data, texturedata, 1); UpdateTextureScaleMode(data, texturedata, 2); @@ -995,7 +995,7 @@ static bool SetDrawState(D3D_RenderData *data, const SDL_RenderCommand *cmd) const SDL_BlendMode blend = cmd->data.draw.blend; if (texture != data->drawstate.texture) { -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV D3D_TextureData *oldtexturedata = data->drawstate.texture ? (D3D_TextureData *)data->drawstate.texture->internal : NULL; D3D_TextureData *newtexturedata = texture ? (D3D_TextureData *)texture->internal : NULL; #endif @@ -1006,7 +1006,7 @@ static bool SetDrawState(D3D_RenderData *data, const SDL_RenderCommand *cmd) if (!texture) { IDirect3DDevice9_SetTexture(data->device, 0, NULL); } -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV if ((!newtexturedata || !newtexturedata->yuv) && (oldtexturedata && oldtexturedata->yuv)) { IDirect3DDevice9_SetTexture(data->device, 1, NULL); IDirect3DDevice9_SetTexture(data->device, 2, NULL); @@ -1016,7 +1016,7 @@ static bool SetDrawState(D3D_RenderData *data, const SDL_RenderCommand *cmd) return false; } -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV if (shader != data->drawstate.shader) { const HRESULT result = IDirect3DDevice9_SetPixelShader(data->device, data->shaders[shader]); if (FAILED(result)) { @@ -1041,7 +1041,7 @@ static bool SetDrawState(D3D_RenderData *data, const SDL_RenderCommand *cmd) } else if (texture) { D3D_TextureData *texturedata = (D3D_TextureData *)texture->internal; UpdateDirtyTexture(data->device, &texturedata->texture); -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV if (texturedata->yuv) { UpdateDirtyTexture(data->device, &texturedata->utexture); UpdateDirtyTexture(data->device, &texturedata->vtexture); @@ -1424,7 +1424,7 @@ static void D3D_DestroyTexture(SDL_Renderer *renderer, SDL_Texture *texture) renderdata->drawstate.shader_params = NULL; IDirect3DDevice9_SetPixelShader(renderdata->device, NULL); IDirect3DDevice9_SetTexture(renderdata->device, 0, NULL); -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV if (data->yuv) { IDirect3DDevice9_SetTexture(renderdata->device, 1, NULL); IDirect3DDevice9_SetTexture(renderdata->device, 2, NULL); @@ -1437,7 +1437,7 @@ static void D3D_DestroyTexture(SDL_Renderer *renderer, SDL_Texture *texture) } D3D_DestroyTextureRep(&data->texture); -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV D3D_DestroyTextureRep(&data->utexture); D3D_DestroyTextureRep(&data->vtexture); SDL_free(data->pixels); @@ -1462,7 +1462,7 @@ static void D3D_DestroyRenderer(SDL_Renderer *renderer) IDirect3DSurface9_Release(data->currentRenderTarget); data->currentRenderTarget = NULL; } -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV for (i = 0; i < SDL_arraysize(data->shaders); ++i) { if (data->shaders[i]) { IDirect3DPixelShader9_Release(data->shaders[i]); @@ -1559,8 +1559,9 @@ static bool D3D_Reset(SDL_Renderer *renderer) // Let the application know that render targets were reset { SDL_Event event; + SDL_zero(event); event.type = SDL_EVENT_RENDER_TARGETS_RESET; - event.common.timestamp = 0; + event.render.windowID = SDL_GetWindowID(SDL_GetRenderWindow(renderer)); SDL_PushEvent(&event); } @@ -1647,7 +1648,7 @@ static bool D3D_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL_P renderer->SupportsBlendMode = D3D_SupportsBlendMode; renderer->CreateTexture = D3D_CreateTexture; renderer->UpdateTexture = D3D_UpdateTexture; -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV renderer->UpdateTextureYUV = D3D_UpdateTextureYUV; #endif renderer->LockTexture = D3D_LockTexture; @@ -1750,7 +1751,7 @@ static bool D3D_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL_P // Set up parameters for rendering D3D_InitRenderState(data); -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV if (caps.MaxSimultaneousTextures >= 3) { int i; for (i = SHADER_NONE + 1; i < SDL_arraysize(data->shaders); ++i) { diff --git a/libs/SDL3/src/render/direct3d11/SDL_render_d3d11.c b/libs/SDL3/src/render/direct3d11/SDL_render_d3d11.c index c14346ff..604d7829 100644 --- a/libs/SDL3/src/render/direct3d11/SDL_render_d3d11.c +++ b/libs/SDL3/src/render/direct3d11/SDL_render_d3d11.c @@ -125,7 +125,7 @@ typedef struct D3D11_FILTER scaleMode; D3D11_Shader shader; const float *YCbCr_matrix; -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV // YV12 texture support bool yuv; ID3D11Texture2D *mainTextureU; @@ -1084,8 +1084,9 @@ static bool D3D11_HandleDeviceLost(SDL_Renderer *renderer) // Let the application know that the device has been reset or lost SDL_Event event; + SDL_zero(event); event.type = recovered ? SDL_EVENT_RENDER_DEVICE_RESET : SDL_EVENT_RENDER_DEVICE_LOST; - event.common.timestamp = 0; + event.render.windowID = SDL_GetWindowID(SDL_GetRenderWindow(renderer)); SDL_PushEvent(&event); return recovered; @@ -1214,7 +1215,7 @@ static bool D3D11_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD } } SDL_SetPointerProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_D3D11_TEXTURE_POINTER, textureData->mainTexture); -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV if (texture->format == SDL_PIXELFORMAT_YV12 || texture->format == SDL_PIXELFORMAT_IYUV) { textureData->yuv = true; @@ -1288,7 +1289,7 @@ static bool D3D11_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD if (FAILED(result)) { return WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11Device1::CreateShaderResourceView"), result); } -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV if (textureData->yuv) { result = ID3D11Device_CreateShaderResourceView(rendererData->d3dDevice, (ID3D11Resource *)textureData->mainTextureU, @@ -1357,7 +1358,7 @@ static void D3D11_DestroyTexture(SDL_Renderer *renderer, SAFE_RELEASE(data->mainTextureResourceView); SAFE_RELEASE(data->mainTextureRenderTargetView); SAFE_RELEASE(data->stagingTexture); -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV SAFE_RELEASE(data->mainTextureU); SAFE_RELEASE(data->mainTextureResourceViewU); SAFE_RELEASE(data->mainTextureV); @@ -1472,7 +1473,7 @@ static bool D3D11_UpdateTextureInternal(D3D11_RenderData *rendererData, ID3D11Te return true; } -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV static bool D3D11_UpdateTextureNV(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *rect, const Uint8 *Yplane, int Ypitch, @@ -1496,7 +1497,7 @@ static bool D3D11_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, return SDL_SetError("Texture is not currently available"); } -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV if (textureData->nv12) { const Uint8 *Yplane = (const Uint8 *)srcPixels; const Uint8 *UVplane = Yplane + rect->h * srcPitch; @@ -1524,7 +1525,7 @@ static bool D3D11_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, return true; } -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV static bool D3D11_UpdateTextureYUV(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *rect, const Uint8 *Yplane, int Ypitch, @@ -1676,7 +1677,7 @@ static bool D3D11_LockTexture(SDL_Renderer *renderer, SDL_Texture *texture, if (!textureData) { return SDL_SetError("Texture is not currently available"); } -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV if (textureData->yuv || textureData->nv12) { // It's more efficient to upload directly... if (!textureData->pixels) { @@ -1753,7 +1754,7 @@ static void D3D11_UnlockTexture(SDL_Renderer *renderer, SDL_Texture *texture) if (!textureData) { return; } -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV if (textureData->yuv || textureData->nv12) { const SDL_Rect *rect = &textureData->locked_rect; void *pixels = @@ -2332,7 +2333,7 @@ static bool D3D11_SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand * default: return SDL_SetError("Unknown scale mode: %d\n", textureData->scaleMode); } -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV if (textureData->yuv) { ID3D11ShaderResourceView *shaderResources[3]; @@ -2692,7 +2693,7 @@ static bool D3D11_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL renderer->SupportsBlendMode = D3D11_SupportsBlendMode; renderer->CreateTexture = D3D11_CreateTexture; renderer->UpdateTexture = D3D11_UpdateTexture; -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV renderer->UpdateTextureYUV = D3D11_UpdateTextureYUV; renderer->UpdateTextureNV = D3D11_UpdateTextureNV; #endif diff --git a/libs/SDL3/src/render/direct3d12/SDL_render_d3d12.c b/libs/SDL3/src/render/direct3d12/SDL_render_d3d12.c index 55114335..63017077 100644 --- a/libs/SDL3/src/render/direct3d12/SDL_render_d3d12.c +++ b/libs/SDL3/src/render/direct3d12/SDL_render_d3d12.c @@ -127,7 +127,7 @@ typedef struct D3D12_FILTER scaleMode; D3D12_Shader shader; const float *YCbCr_matrix; -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV // YV12 texture support bool yuv; ID3D12Resource *mainTextureU; @@ -1459,8 +1459,9 @@ static bool D3D12_HandleDeviceLost(SDL_Renderer *renderer) // Let the application know that the device has been reset or lost SDL_Event event; + SDL_zero(event); event.type = recovered ? SDL_EVENT_RENDER_DEVICE_RESET : SDL_EVENT_RENDER_DEVICE_LOST; - event.common.timestamp = 0; + event.render.windowID = SDL_GetWindowID(SDL_GetRenderWindow(renderer)); SDL_PushEvent(&event); return recovered; @@ -1618,7 +1619,7 @@ static bool D3D12_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD } textureData->mainResourceState = D3D12_RESOURCE_STATE_COPY_DEST; SDL_SetPointerProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_D3D12_TEXTURE_POINTER, textureData->mainTexture); -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV if (texture->format == SDL_PIXELFORMAT_YV12 || texture->format == SDL_PIXELFORMAT_IYUV) { textureData->yuv = true; @@ -1705,7 +1706,7 @@ static bool D3D12_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD textureData->mainTexture, &resourceViewDesc, textureData->mainTextureResourceView); -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV if (textureData->yuv) { D3D_CALL_RET(rendererData->srvDescriptorHeap, GetCPUDescriptorHandleForHeapStart, &textureData->mainTextureResourceViewU); textureData->mainSRVIndexU = D3D12_GetAvailableSRVIndex(renderer); @@ -1780,7 +1781,7 @@ static void D3D12_DestroyTexture(SDL_Renderer *renderer, D3D_SAFE_RELEASE(textureData->mainTexture); D3D_SAFE_RELEASE(textureData->stagingBuffer); D3D12_FreeSRVIndex(renderer, textureData->mainSRVIndex); -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV D3D_SAFE_RELEASE(textureData->mainTextureU); D3D_SAFE_RELEASE(textureData->mainTextureV); if (textureData->yuv) { @@ -1948,7 +1949,7 @@ static bool D3D12_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, if (!D3D12_UpdateTextureInternal(rendererData, textureData->mainTexture, 0, rect->x, rect->y, rect->w, rect->h, srcPixels, srcPitch, &textureData->mainResourceState)) { return false; } -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV if (textureData->yuv) { // Skip to the correct offset into the next texture srcPixels = (const void *)((const Uint8 *)srcPixels + rect->h * srcPitch); @@ -1981,7 +1982,7 @@ static bool D3D12_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, return true; } -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV static bool D3D12_UpdateTextureYUV(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *rect, const Uint8 *Yplane, int Ypitch, @@ -2047,7 +2048,7 @@ static bool D3D12_LockTexture(SDL_Renderer *renderer, SDL_Texture *texture, if (!textureData) { return SDL_SetError("Texture is not currently available"); } -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV if (textureData->yuv || textureData->nv12) { // It's more efficient to upload directly... if (!textureData->pixels) { @@ -2166,7 +2167,7 @@ static void D3D12_UnlockTexture(SDL_Renderer *renderer, SDL_Texture *texture) if (!textureData) { return; } -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV if (textureData->yuv || textureData->nv12) { const SDL_Rect *rect = &textureData->lockedRect; void *pixels = @@ -2759,7 +2760,7 @@ static bool D3D12_SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand * default: return SDL_SetError("Unknown scale mode: %d\n", textureData->scaleMode); } -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV if (textureData->yuv) { D3D12_CPU_DESCRIPTOR_HANDLE shaderResources[3]; @@ -3228,7 +3229,7 @@ bool D3D12_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL_Proper renderer->SupportsBlendMode = D3D12_SupportsBlendMode; renderer->CreateTexture = D3D12_CreateTexture; renderer->UpdateTexture = D3D12_UpdateTexture; -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV renderer->UpdateTextureYUV = D3D12_UpdateTextureYUV; renderer->UpdateTextureNV = D3D12_UpdateTextureNV; #endif diff --git a/libs/SDL3/src/render/gpu/SDL_pipeline_gpu.c b/libs/SDL3/src/render/gpu/SDL_pipeline_gpu.c index c8fdcbaf..9795d171 100644 --- a/libs/SDL3/src/render/gpu/SDL_pipeline_gpu.c +++ b/libs/SDL3/src/render/gpu/SDL_pipeline_gpu.c @@ -83,7 +83,7 @@ static void NukePipelineCacheEntry(const void *key, const void *value, void *dat bool GPU_InitPipelineCache(GPU_PipelineCache *cache, SDL_GPUDevice *device) { // FIXME how many buckets do we need? - cache->table = SDL_CreateHashTable(device, 32, HashPassthrough, MatchPipelineCacheKey, NukePipelineCacheEntry, true); + cache->table = SDL_CreateHashTable(device, 32, HashPassthrough, MatchPipelineCacheKey, NukePipelineCacheEntry, false, true); if (!cache->table) { return false; } diff --git a/libs/SDL3/src/render/gpu/SDL_render_gpu.c b/libs/SDL3/src/render/gpu/SDL_render_gpu.c index c36784ce..89d24735 100644 --- a/libs/SDL3/src/render/gpu/SDL_render_gpu.c +++ b/libs/SDL3/src/render/gpu/SDL_render_gpu.c @@ -1267,6 +1267,8 @@ static bool GPU_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL_P SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_RGBX32); SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_BGRX32); + SDL_SetNumberProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_MAX_TEXTURE_SIZE_NUMBER, 16384); + data->state.draw_color.r = 1.0f; data->state.draw_color.g = 1.0f; data->state.draw_color.b = 1.0f; @@ -1282,6 +1284,8 @@ static bool GPU_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL_P return false; } + SDL_SetPointerProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_GPU_DEVICE_POINTER, data->device); + return true; } diff --git a/libs/SDL3/src/render/gpu/SDL_shaders_gpu.c b/libs/SDL3/src/render/gpu/SDL_shaders_gpu.c index ce5a55e2..80e336b5 100644 --- a/libs/SDL3/src/render/gpu/SDL_shaders_gpu.c +++ b/libs/SDL3/src/render/gpu/SDL_shaders_gpu.c @@ -42,15 +42,6 @@ typedef struct GPU_ShaderModuleSource #define HAVE_SPIRV_SHADERS 0 #endif -#ifdef SDL_GPU_D3D11 -#define IF_D3D11(...) __VA_ARGS__ -#define HAVE_DXBC50_SHADERS 1 -#include "shaders/dxbc50.h" -#else -#define IF_D3D11(...) -#define HAVE_DXBC50_SHADERS 0 -#endif - #ifdef SDL_GPU_D3D12 #define IF_D3D12(...) __VA_ARGS__ #define HAVE_DXIL60_SHADERS 1 @@ -72,7 +63,6 @@ typedef struct GPU_ShaderModuleSource typedef struct GPU_ShaderSources { IF_VULKAN(GPU_ShaderModuleSource spirv;) - IF_D3D11(GPU_ShaderModuleSource dxbc50;) IF_D3D12(GPU_ShaderModuleSource dxil60;) IF_METAL(GPU_ShaderModuleSource msl;) unsigned int num_samplers; @@ -82,9 +72,6 @@ typedef struct GPU_ShaderSources #define SHADER_SPIRV(code) \ IF_VULKAN(.spirv = { code, sizeof(code), SDL_GPU_SHADERFORMAT_SPIRV }, ) -#define SHADER_DXBC50(code) \ - IF_D3D11(.dxbc50 = { (const unsigned char *)code, sizeof(code), SDL_GPU_SHADERFORMAT_DXBC }, ) - #define SHADER_DXIL60(code) \ IF_D3D12(.dxil60 = { code, sizeof(code), SDL_GPU_SHADERFORMAT_DXIL }, ) @@ -97,7 +84,6 @@ static const GPU_ShaderSources vert_shader_sources[NUM_VERT_SHADERS] = { .num_samplers = 0, .num_uniform_buffers = 1, SHADER_SPIRV(linepoint_vert_spv) - SHADER_DXBC50(linepoint_vert_sm50_dxbc) SHADER_DXIL60(linepoint_vert_sm60_dxil) SHADER_METAL(linepoint_vert_metal) }, @@ -105,7 +91,6 @@ static const GPU_ShaderSources vert_shader_sources[NUM_VERT_SHADERS] = { .num_samplers = 0, .num_uniform_buffers = 1, SHADER_SPIRV(tri_color_vert_spv) - SHADER_DXBC50(tri_color_vert_sm50_dxbc) SHADER_DXIL60(tri_color_vert_sm60_dxil) SHADER_METAL(tri_color_vert_metal) }, @@ -113,7 +98,6 @@ static const GPU_ShaderSources vert_shader_sources[NUM_VERT_SHADERS] = { .num_samplers = 0, .num_uniform_buffers = 1, SHADER_SPIRV(tri_texture_vert_spv) - SHADER_DXBC50(tri_texture_vert_sm50_dxbc) SHADER_DXIL60(tri_texture_vert_sm60_dxil) SHADER_METAL(tri_texture_vert_metal) }, @@ -124,7 +108,6 @@ static const GPU_ShaderSources frag_shader_sources[NUM_FRAG_SHADERS] = { .num_samplers = 0, .num_uniform_buffers = 0, SHADER_SPIRV(color_frag_spv) - SHADER_DXBC50(color_frag_sm50_dxbc) SHADER_DXIL60(color_frag_sm60_dxil) SHADER_METAL(color_frag_metal) }, @@ -132,7 +115,6 @@ static const GPU_ShaderSources frag_shader_sources[NUM_FRAG_SHADERS] = { .num_samplers = 1, .num_uniform_buffers = 0, SHADER_SPIRV(texture_rgb_frag_spv) - SHADER_DXBC50(texture_rgb_frag_sm50_dxbc) SHADER_DXIL60(texture_rgb_frag_sm60_dxil) SHADER_METAL(texture_rgb_frag_metal) }, @@ -140,7 +122,6 @@ static const GPU_ShaderSources frag_shader_sources[NUM_FRAG_SHADERS] = { .num_samplers = 1, .num_uniform_buffers = 0, SHADER_SPIRV(texture_rgba_frag_spv) - SHADER_DXBC50(texture_rgba_frag_sm50_dxbc) SHADER_DXIL60(texture_rgba_frag_sm60_dxil) SHADER_METAL(texture_rgba_frag_metal) }, @@ -159,10 +140,6 @@ static SDL_GPUShader *CompileShader(const GPU_ShaderSources *sources, SDL_GPUDev } else if (formats & SDL_GPU_SHADERFORMAT_SPIRV) { sms = &sources->spirv; #endif // HAVE_SPIRV_SHADERS -#if HAVE_DXBC50_SHADERS - } else if (formats & SDL_GPU_SHADERFORMAT_DXBC) { - sms = &sources->dxbc50; -#endif // HAVE_DXBC50_SHADERS #if HAVE_DXIL60_SHADERS } else if (formats & SDL_GPU_SHADERFORMAT_DXIL) { sms = &sources->dxil60; @@ -248,7 +225,6 @@ SDL_GPUShader *GPU_GetFragmentShader(GPU_Shaders *shaders, GPU_FragmentShaderID void GPU_FillSupportedShaderFormats(SDL_PropertiesID props) { SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_SPIRV_BOOLEAN, HAVE_SPIRV_SHADERS); - SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_DXBC_BOOLEAN, HAVE_DXBC50_SHADERS); SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_DXIL_BOOLEAN, HAVE_DXIL60_SHADERS); SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_MSL_BOOLEAN, HAVE_METAL_SHADERS); } diff --git a/libs/SDL3/src/render/metal/SDL_render_metal.m b/libs/SDL3/src/render/metal/SDL_render_metal.m index 338120c9..55c17826 100644 --- a/libs/SDL3/src/render/metal/SDL_render_metal.m +++ b/libs/SDL3/src/render/metal/SDL_render_metal.m @@ -158,7 +158,7 @@ typedef struct METAL_ShaderPipelines @property(nonatomic, retain) id mtltexture; @property(nonatomic, retain) id mtltextureUv; @property(nonatomic, assign) SDL_MetalFragmentFunction fragmentFunction; -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV @property(nonatomic, assign) BOOL yuv; @property(nonatomic, assign) BOOL nv12; @property(nonatomic, assign) size_t conversionBufferOffset; @@ -650,7 +650,6 @@ static bool METAL_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD switch (texture->format) { case SDL_PIXELFORMAT_ABGR8888: - case SDL_PIXELFORMAT_XBGR8888: if (renderer->output_colorspace == SDL_COLORSPACE_SRGB_LINEAR) { pixfmt = MTLPixelFormatRGBA8Unorm_sRGB; } else { @@ -658,14 +657,13 @@ static bool METAL_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD } break; case SDL_PIXELFORMAT_ARGB8888: - case SDL_PIXELFORMAT_XRGB8888: if (renderer->output_colorspace == SDL_COLORSPACE_SRGB_LINEAR) { pixfmt = MTLPixelFormatBGRA8Unorm_sRGB; } else { pixfmt = MTLPixelFormatBGRA8Unorm; } break; - case SDL_PIXELFORMAT_XBGR2101010: + case SDL_PIXELFORMAT_ABGR2101010: pixfmt = MTLPixelFormatRGB10A2Unorm; break; case SDL_PIXELFORMAT_IYUV: @@ -713,7 +711,7 @@ static bool METAL_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD } mtltextureUv = nil; -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV BOOL yuv = (texture->format == SDL_PIXELFORMAT_IYUV || texture->format == SDL_PIXELFORMAT_YV12); BOOL nv12 = (texture->format == SDL_PIXELFORMAT_NV12 || texture->format == SDL_PIXELFORMAT_NV21 || texture->format == SDL_PIXELFORMAT_P010); @@ -749,7 +747,7 @@ static bool METAL_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD texturedata = [[SDL3METAL_TextureData alloc] init]; if (SDL_COLORSPACETRANSFER(texture->colorspace) == SDL_TRANSFER_CHARACTERISTICS_SRGB) { texturedata.fragmentFunction = SDL_METAL_FRAGMENT_COPY; -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV } else if (yuv) { texturedata.fragmentFunction = SDL_METAL_FRAGMENT_YUV; #endif @@ -758,7 +756,7 @@ static bool METAL_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD } texturedata.mtltexture = mtltexture; texturedata.mtltextureUv = mtltextureUv; -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV texturedata.yuv = yuv; texturedata.nv12 = nv12; if (yuv || nv12) { @@ -872,7 +870,7 @@ static bool METAL_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, if (!METAL_UpdateTextureInternal(renderer, texturedata, texturedata.mtltexture, *rect, 0, pixels, pitch)) { return false; } -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV if (texturedata.yuv) { int Uslice = texture->format == SDL_PIXELFORMAT_YV12 ? 1 : 0; int Vslice = texture->format == SDL_PIXELFORMAT_YV12 ? 0 : 1; @@ -909,7 +907,7 @@ static bool METAL_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, } } -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV static bool METAL_UpdateTextureYUV(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *rect, const Uint8 *Yplane, int Ypitch, @@ -986,7 +984,7 @@ static bool METAL_LockTexture(SDL_Renderer *renderer, SDL_Texture *texture, } *pitch = SDL_BYTESPERPIXEL(texture->format) * rect->w; -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV if (texturedata.yuv || texturedata.nv12) { buffersize = ((*pitch) * rect->h) + (2 * (*pitch + 1) / 2) * ((rect->h + 1) / 2); } else @@ -1016,7 +1014,7 @@ static void METAL_UnlockTexture(SDL_Renderer *renderer, SDL_Texture *texture) id blitcmd; SDL_Rect rect = texturedata.lockedrect; int pitch = SDL_BYTESPERPIXEL(texture->format) * rect.w; -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV SDL_Rect UVrect = { rect.x / 2, rect.y / 2, (rect.w + 1) / 2, (rect.h + 1) / 2 }; #endif @@ -1044,7 +1042,7 @@ static void METAL_UnlockTexture(SDL_Renderer *renderer, SDL_Texture *texture) destinationSlice:0 destinationLevel:0 destinationOrigin:MTLOriginMake(rect.x, rect.y, 0)]; -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV if (texturedata.yuv) { int Uslice = texture->format == SDL_PIXELFORMAT_YV12 ? 1 : 0; int Vslice = texture->format == SDL_PIXELFORMAT_YV12 ? 0 : 1; @@ -1524,7 +1522,7 @@ static bool SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, c [data.mtlcmdencoder setFragmentSamplerState:mtlsampler atIndex:0]; [data.mtlcmdencoder setFragmentTexture:texturedata.mtltexture atIndex:0]; -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV if (texturedata.yuv || texturedata.nv12) { [data.mtlcmdencoder setFragmentTexture:texturedata.mtltextureUv atIndex:1]; [data.mtlcmdencoder setFragmentBuffer:data.mtlbufconstants offset:texturedata.conversionBufferOffset atIndex:1]; @@ -1737,7 +1735,7 @@ static SDL_Surface *METAL_RenderReadPixels(SDL_Renderer *renderer, const SDL_Rec format = SDL_PIXELFORMAT_ABGR8888; break; case MTLPixelFormatRGB10A2Unorm: - format = SDL_PIXELFORMAT_XBGR2101010; + format = SDL_PIXELFORMAT_ABGR2101010; break; case MTLPixelFormatRGBA16Float: format = SDL_PIXELFORMAT_RGBA64_FLOAT; @@ -2153,7 +2151,7 @@ static bool METAL_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL renderer->SupportsBlendMode = METAL_SupportsBlendMode; renderer->CreateTexture = METAL_CreateTexture; renderer->UpdateTexture = METAL_UpdateTexture; -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV renderer->UpdateTextureYUV = METAL_UpdateTextureYUV; renderer->UpdateTextureNV = METAL_UpdateTextureNV; #endif @@ -2179,9 +2177,7 @@ static bool METAL_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL renderer->name = METAL_RenderDriver.name; SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_ARGB8888); SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_ABGR8888); - SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_XRGB8888); - SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_XBGR8888); - SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_XBGR2101010); + SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_ABGR2101010); SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_RGBA64_FLOAT); SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_RGBA128_FLOAT); SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_YV12); diff --git a/libs/SDL3/src/render/opengl/SDL_render_gl.c b/libs/SDL3/src/render/opengl/SDL_render_gl.c index b598098a..a721021e 100644 --- a/libs/SDL3/src/render/opengl/SDL_render_gl.c +++ b/libs/SDL3/src/render/opengl/SDL_render_gl.c @@ -138,7 +138,7 @@ typedef struct int pitch; SDL_Rect locked_rect; -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV // YUV texture support bool yuv; bool nv12; @@ -577,7 +577,7 @@ static bool GL_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_P return false; } -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV if (texture->format == SDL_PIXELFORMAT_YV12 || texture->format == SDL_PIXELFORMAT_IYUV) { data->yuv = true; @@ -641,7 +641,7 @@ static bool GL_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_P data->shader = SHADER_RGB; } -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV if (data->yuv || data->nv12) { if (data->yuv) { data->shader = SHADER_YUV; @@ -688,7 +688,7 @@ static bool GL_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, renderdata->glTexSubImage2D(textype, 0, rect->x, rect->y, rect->w, rect->h, data->format, data->formattype, pixels); -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV if (data->yuv) { renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, ((pitch + 1) / 2)); @@ -729,7 +729,7 @@ static bool GL_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, return GL_CheckError("glTexSubImage2D()", renderer); } -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV static bool GL_UpdateTextureYUV(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *rect, const Uint8 *Yplane, int Ypitch, @@ -833,7 +833,7 @@ static void GL_SetTextureScaleMode(SDL_Renderer *renderer, SDL_Texture *texture, renderdata->glTexParameteri(textype, GL_TEXTURE_MIN_FILTER, glScaleMode); renderdata->glTexParameteri(textype, GL_TEXTURE_MAG_FILTER, glScaleMode); -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV if (texture->format == SDL_PIXELFORMAT_YV12 || texture->format == SDL_PIXELFORMAT_IYUV) { renderdata->glBindTexture(textype, data->utexture); @@ -1146,7 +1146,7 @@ static bool SetCopyState(GL_RenderData *data, const SDL_RenderCommand *cmd) if (texture != data->drawstate.texture) { const GLenum textype = data->textype; -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV if (texturedata->yuv) { if (data->GL_ARB_multitexture_supported) { data->glActiveTextureARB(GL_TEXTURE2_ARB); @@ -1529,7 +1529,7 @@ static void GL_DestroyTexture(SDL_Renderer *renderer, SDL_Texture *texture) if (data->texture && !data->texture_external) { renderdata->glDeleteTextures(1, &data->texture); } -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV if (data->yuv) { if (!data->utexture_external) { renderdata->glDeleteTextures(1, &data->utexture); @@ -1650,7 +1650,7 @@ static bool GL_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL_Pr renderer->SupportsBlendMode = GL_SupportsBlendMode; renderer->CreateTexture = GL_CreateTexture; renderer->UpdateTexture = GL_UpdateTexture; -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV renderer->UpdateTextureYUV = GL_UpdateTextureYUV; renderer->UpdateTextureNV = GL_UpdateTextureNV; #endif @@ -1766,7 +1766,7 @@ static bool GL_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL_Pr data->shaders = GL_CreateShaderContext(); SDL_LogInfo(SDL_LOG_CATEGORY_RENDER, "OpenGL shaders: %s", data->shaders ? "ENABLED" : "DISABLED"); -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV // We support YV12 textures using 3 textures and a shader if (data->shaders && data->num_texture_units >= 3) { SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_YV12); diff --git a/libs/SDL3/src/render/opengl/SDL_shaders_gl.c b/libs/SDL3/src/render/opengl/SDL_shaders_gl.c index c876fb98..cb5ba997 100644 --- a/libs/SDL3/src/render/opengl/SDL_shaders_gl.c +++ b/libs/SDL3/src/render/opengl/SDL_shaders_gl.c @@ -284,7 +284,7 @@ static const char *shader_source[NUM_SHADERS][2] = { " gl_FragColor = texture2D(tex0, v_texCoord) * v_color;\n" "}" }, -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV // SHADER_YUV { // vertex shader diff --git a/libs/SDL3/src/render/opengl/SDL_shaders_gl.h b/libs/SDL3/src/render/opengl/SDL_shaders_gl.h index 73c1a3db..7c976ddb 100644 --- a/libs/SDL3/src/render/opengl/SDL_shaders_gl.h +++ b/libs/SDL3/src/render/opengl/SDL_shaders_gl.h @@ -33,7 +33,7 @@ typedef enum SHADER_SOLID, SHADER_RGB, SHADER_RGBA, -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV SHADER_YUV, SHADER_NV12_RA, SHADER_NV12_RG, diff --git a/libs/SDL3/src/render/opengles2/SDL_render_gles2.c b/libs/SDL3/src/render/opengles2/SDL_render_gles2.c index 37aab070..f0139717 100644 --- a/libs/SDL3/src/render/opengles2/SDL_render_gles2.c +++ b/libs/SDL3/src/render/opengles2/SDL_render_gles2.c @@ -67,7 +67,7 @@ typedef struct GLES2_TextureData GLenum pixel_type; void *pixel_data; int pitch; -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV // YUV texture support bool yuv; bool nv12; @@ -625,7 +625,7 @@ static bool GLES2_SelectProgram(GLES2_RenderData *data, GLES2_ImageSource source case GLES2_IMAGESOURCE_TEXTURE_BGR: ftype = GLES2_SHADER_FRAGMENT_TEXTURE_BGR; break; -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV case GLES2_IMAGESOURCE_TEXTURE_YUV: ftype = GLES2_SHADER_FRAGMENT_TEXTURE_YUV; shader_params = SDL_GetYCbCRtoRGBConversionMatrix(colorspace, 0, 0, 8); @@ -1114,7 +1114,7 @@ static bool SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, v break; } break; -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV case SDL_PIXELFORMAT_IYUV: case SDL_PIXELFORMAT_YV12: sourceType = GLES2_IMAGESOURCE_TEXTURE_YUV; @@ -1149,7 +1149,7 @@ static bool SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, v case SDL_PIXELFORMAT_RGBX32: sourceType = GLES2_IMAGESOURCE_TEXTURE_BGR; break; -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV case SDL_PIXELFORMAT_IYUV: case SDL_PIXELFORMAT_YV12: sourceType = GLES2_IMAGESOURCE_TEXTURE_YUV; @@ -1173,7 +1173,7 @@ static bool SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, v if (texture != data->drawstate.texture) { GLES2_TextureData *tdata = (GLES2_TextureData *)texture->internal; -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV if (tdata->yuv) { data->glActiveTexture(GL_TEXTURE2); data->glBindTexture(tdata->texture_type, tdata->texture_v); @@ -1501,7 +1501,7 @@ static bool GLES2_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD format = GL_RGBA; type = GL_UNSIGNED_BYTE; break; -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV case SDL_PIXELFORMAT_IYUV: case SDL_PIXELFORMAT_YV12: case SDL_PIXELFORMAT_NV12: @@ -1538,7 +1538,7 @@ static bool GLES2_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD #endif data->pixel_format = format; data->pixel_type = type; -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV data->yuv = ((texture->format == SDL_PIXELFORMAT_IYUV) || (texture->format == SDL_PIXELFORMAT_YV12)); data->nv12 = ((texture->format == SDL_PIXELFORMAT_NV12) || (texture->format == SDL_PIXELFORMAT_NV21)); data->texture_u = 0; @@ -1551,7 +1551,7 @@ static bool GLES2_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD size_t size; data->pitch = texture->w * SDL_BYTESPERPIXEL(texture->format); size = (size_t)texture->h * data->pitch; -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV if (data->yuv) { // Need to add size for the U and V planes size += 2 * ((texture->h + 1) / 2) * ((data->pitch + 1) / 2); @@ -1570,7 +1570,7 @@ static bool GLES2_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD // Allocate the texture GL_CheckError("", renderer); -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV if (data->yuv) { data->texture_v = (GLuint)SDL_GetNumberProperty(create_props, SDL_PROP_TEXTURE_CREATE_OPENGLES2_TEXTURE_V_NUMBER, 0); if (data->texture_v) { @@ -1729,7 +1729,7 @@ static bool GLES2_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, co tdata->pixel_type, pixels, pitch, SDL_BYTESPERPIXEL(texture->format)); -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV if (tdata->yuv) { // Skip to the correct offset into the next texture pixels = (const void *)((const Uint8 *)pixels + rect->h * pitch); @@ -1780,7 +1780,7 @@ static bool GLES2_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, co return GL_CheckError("glTexSubImage2D()", renderer); } -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV static bool GLES2_UpdateTextureYUV(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *rect, const Uint8 *Yplane, int Ypitch, @@ -1906,7 +1906,7 @@ static void GLES2_SetTextureScaleMode(SDL_Renderer *renderer, SDL_Texture *textu GLES2_TextureData *data = (GLES2_TextureData *)texture->internal; GLenum glScaleMode = (scaleMode == SDL_SCALEMODE_NEAREST) ? GL_NEAREST : GL_LINEAR; -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV if (data->yuv) { renderdata->glActiveTexture(GL_TEXTURE2); renderdata->glBindTexture(data->texture_type, data->texture_v); @@ -1974,7 +1974,7 @@ static void GLES2_DestroyTexture(SDL_Renderer *renderer, SDL_Texture *texture) if (tdata->texture && !tdata->texture_external) { data->glDeleteTextures(1, &tdata->texture); } -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV if (tdata->texture_v && !tdata->texture_v_external) { data->glDeleteTextures(1, &tdata->texture_v); } @@ -2145,7 +2145,7 @@ static bool GLES2_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL renderer->SupportsBlendMode = GLES2_SupportsBlendMode; renderer->CreateTexture = GLES2_CreateTexture; renderer->UpdateTexture = GLES2_UpdateTexture; -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV renderer->UpdateTextureYUV = GLES2_UpdateTextureYUV; renderer->UpdateTextureNV = GLES2_UpdateTextureNV; #endif @@ -2165,7 +2165,7 @@ static bool GLES2_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL renderer->DestroyTexture = GLES2_DestroyTexture; renderer->DestroyRenderer = GLES2_DestroyRenderer; renderer->SetVSync = GLES2_SetVSync; -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_YV12); SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_IYUV); SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV12); diff --git a/libs/SDL3/src/render/opengles2/SDL_shaders_gles2.c b/libs/SDL3/src/render/opengles2/SDL_shaders_gles2.c index cd6b44eb..91f7b267 100644 --- a/libs/SDL3/src/render/opengles2/SDL_shaders_gles2.c +++ b/libs/SDL3/src/render/opengles2/SDL_shaders_gles2.c @@ -148,7 +148,7 @@ static const char GLES2_Fragment_TextureBGR[] = \ "}\n" \ ; -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV #define YUV_SHADER_PROLOGUE \ "uniform sampler2D u_texture;\n" \ @@ -365,7 +365,7 @@ const char *GLES2_GetShader(GLES2_ShaderType type) return GLES2_Fragment_TextureRGB; case GLES2_SHADER_FRAGMENT_TEXTURE_BGR: return GLES2_Fragment_TextureBGR; -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV case GLES2_SHADER_FRAGMENT_TEXTURE_YUV: return GLES2_Fragment_TextureYUV; case GLES2_SHADER_FRAGMENT_TEXTURE_NV12_RA: diff --git a/libs/SDL3/src/render/opengles2/SDL_shaders_gles2.h b/libs/SDL3/src/render/opengles2/SDL_shaders_gles2.h index 396049fd..e24cacd6 100644 --- a/libs/SDL3/src/render/opengles2/SDL_shaders_gles2.h +++ b/libs/SDL3/src/render/opengles2/SDL_shaders_gles2.h @@ -43,7 +43,7 @@ typedef enum GLES2_SHADER_FRAGMENT_TEXTURE_ARGB, GLES2_SHADER_FRAGMENT_TEXTURE_BGR, GLES2_SHADER_FRAGMENT_TEXTURE_RGB, -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV GLES2_SHADER_FRAGMENT_TEXTURE_YUV, GLES2_SHADER_FRAGMENT_TEXTURE_NV12_RA, GLES2_SHADER_FRAGMENT_TEXTURE_NV12_RG, diff --git a/libs/SDL3/src/render/vitagxm/SDL_render_vita_gxm.c b/libs/SDL3/src/render/vitagxm/SDL_render_vita_gxm.c index 20449cf2..de0a9456 100644 --- a/libs/SDL3/src/render/vitagxm/SDL_render_vita_gxm.c +++ b/libs/SDL3/src/render/vitagxm/SDL_render_vita_gxm.c @@ -210,7 +210,7 @@ static bool VITA_GXM_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, renderer->SupportsBlendMode = VITA_GXM_SupportsBlendMode; renderer->CreateTexture = VITA_GXM_CreateTexture; renderer->UpdateTexture = VITA_GXM_UpdateTexture; -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV renderer->UpdateTextureYUV = VITA_GXM_UpdateTextureYUV; renderer->UpdateTextureNV = VITA_GXM_UpdateTextureNV; #endif @@ -299,7 +299,7 @@ static bool VITA_GXM_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, VITA_GXM_SetTextureScaleMode(renderer, texture, texture->scaleMode); -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV vita_texture->yuv = ((texture->format == SDL_PIXELFORMAT_IYUV) || (texture->format == SDL_PIXELFORMAT_YV12)); vita_texture->nv12 = ((texture->format == SDL_PIXELFORMAT_NV12) || (texture->format == SDL_PIXELFORMAT_NV21)); #endif @@ -339,7 +339,7 @@ static bool VITA_GXM_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, Uint8 *dst; int row, length, dpitch; -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV if (vita_texture->yuv || vita_texture->nv12) { VITA_GXM_SetYUVProfile(renderer, texture); } @@ -358,7 +358,7 @@ static bool VITA_GXM_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, } } -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV if (vita_texture->yuv) { void *Udst; void *Vdst; @@ -425,7 +425,7 @@ static bool VITA_GXM_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, return true; } -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV static bool VITA_GXM_UpdateTextureYUV(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *rect, const Uint8 *Yplane, int Ypitch, diff --git a/libs/SDL3/src/render/vulkan/SDL_render_vulkan.c b/libs/SDL3/src/render/vulkan/SDL_render_vulkan.c index 2ccad3e0..ee8b7dd2 100644 --- a/libs/SDL3/src/render/vulkan/SDL_render_vulkan.c +++ b/libs/SDL3/src/render/vulkan/SDL_render_vulkan.c @@ -2519,8 +2519,9 @@ static bool VULKAN_HandleDeviceLost(SDL_Renderer *renderer) // Let the application know that the device has been reset or lost SDL_Event event; + SDL_zero(event); event.type = recovered ? SDL_EVENT_RENDER_DEVICE_RESET : SDL_EVENT_RENDER_DEVICE_LOST; - event.common.timestamp = 0; + event.render.windowID = SDL_GetWindowID(SDL_GetRenderWindow(renderer)); SDL_PushEvent(&event); return recovered; @@ -2595,7 +2596,7 @@ static bool VULKAN_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, S } textureData->scaleMode = (texture->scaleMode == SDL_SCALEMODE_NEAREST) ? VK_FILTER_NEAREST : VK_FILTER_LINEAR; -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV // YUV textures must have even width and height. Also create Ycbcr conversion if (texture->format == SDL_PIXELFORMAT_YV12 || texture->format == SDL_PIXELFORMAT_IYUV || @@ -2769,7 +2770,7 @@ static void VULKAN_DestroyTexture(SDL_Renderer *renderer, VULKAN_DestroyImage(rendererData, &textureData->mainImage); -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV if (textureData->samplerYcbcrConversion != VK_NULL_HANDLE) { vkDestroySamplerYcbcrConversionKHR(rendererData->device, textureData->samplerYcbcrConversion, NULL); textureData->samplerYcbcrConversion = VK_NULL_HANDLE; @@ -2910,7 +2911,7 @@ static bool VULKAN_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, if (!VULKAN_UpdateTextureInternal(rendererData, textureData->mainImage.image, textureData->mainImage.format, 0, rect->x, rect->y, rect->w, rect->h, srcPixels, srcPitch, &textureData->mainImage.imageLayout)) { return false; } -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV Uint32 numPlanes = VULKAN_VkFormatGetNumPlanes(textureData->mainImage.format); // Skip to the correct offset into the next texture srcPixels = (const void *)((const Uint8 *)srcPixels + rect->h * srcPitch); @@ -2942,7 +2943,7 @@ static bool VULKAN_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, return true; } -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV static bool VULKAN_UpdateTextureYUV(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *rect, const Uint8 *Yplane, int Ypitch, @@ -4277,7 +4278,7 @@ static bool VULKAN_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SD renderer->SupportsBlendMode = VULKAN_SupportsBlendMode; renderer->CreateTexture = VULKAN_CreateTexture; renderer->UpdateTexture = VULKAN_UpdateTexture; -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV renderer->UpdateTextureYUV = VULKAN_UpdateTextureYUV; renderer->UpdateTextureNV = VULKAN_UpdateTextureNV; #endif @@ -4322,7 +4323,7 @@ static bool VULKAN_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SD return false; } -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV if (rendererData->supportsKHRSamplerYCbCrConversion) { SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_YV12); SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_IYUV); diff --git a/libs/SDL3/src/stdlib/SDL_getenv.c b/libs/SDL3/src/stdlib/SDL_getenv.c index 8802ee4b..81d3bfbe 100644 --- a/libs/SDL3/src/stdlib/SDL_getenv.c +++ b/libs/SDL3/src/stdlib/SDL_getenv.c @@ -88,7 +88,7 @@ SDL_Environment *SDL_CreateEnvironment(bool populated) return NULL; } - env->strings = SDL_CreateHashTable(NULL, 16, SDL_HashString, SDL_KeyMatchString, SDL_NukeFreeKey, false); + env->strings = SDL_CreateHashTable(NULL, 16, SDL_HashString, SDL_KeyMatchString, SDL_NukeFreeKey, false, false); if (!env->strings) { SDL_free(env); return NULL; diff --git a/libs/SDL3/src/stdlib/SDL_malloc.c b/libs/SDL3/src/stdlib/SDL_malloc.c index 4cdee69f..d3fc367e 100644 --- a/libs/SDL3/src/stdlib/SDL_malloc.c +++ b/libs/SDL3/src/stdlib/SDL_malloc.c @@ -6352,6 +6352,17 @@ static struct real_malloc, real_calloc, real_realloc, real_free, { 0 } }; +// Define this if you want to track the number of allocations active +// #define SDL_TRACK_ALLOCATION_COUNT +#ifdef SDL_TRACK_ALLOCATION_COUNT +#define INCREMENT_ALLOCATION_COUNT() (void)SDL_AtomicIncRef(&s_mem.num_allocations) +#define DECREMENT_ALLOCATION_COUNT() (void)SDL_AtomicDecRef(&s_mem.num_allocations) +#else +#define INCREMENT_ALLOCATION_COUNT() +#define DECREMENT_ALLOCATION_COUNT() +#endif + + void SDL_GetOriginalMemoryFunctions(SDL_malloc_func *malloc_func, SDL_calloc_func *calloc_func, SDL_realloc_func *realloc_func, @@ -6417,7 +6428,11 @@ bool SDL_SetMemoryFunctions(SDL_malloc_func malloc_func, int SDL_GetNumAllocations(void) { +#ifdef SDL_TRACK_ALLOCATION_COUNT return SDL_GetAtomicInt(&s_mem.num_allocations); +#else + return -1; +#endif } void *SDL_malloc(size_t size) @@ -6430,7 +6445,7 @@ void *SDL_malloc(size_t size) mem = s_mem.malloc_func(size); if (mem) { - SDL_AtomicIncRef(&s_mem.num_allocations); + INCREMENT_ALLOCATION_COUNT(); } else { SDL_OutOfMemory(); } @@ -6449,7 +6464,7 @@ void *SDL_calloc(size_t nmemb, size_t size) mem = s_mem.calloc_func(nmemb, size); if (mem) { - SDL_AtomicIncRef(&s_mem.num_allocations); + INCREMENT_ALLOCATION_COUNT(); } else { SDL_OutOfMemory(); } @@ -6467,7 +6482,7 @@ void *SDL_realloc(void *ptr, size_t size) mem = s_mem.realloc_func(ptr, size); if (mem && !ptr) { - SDL_AtomicIncRef(&s_mem.num_allocations); + INCREMENT_ALLOCATION_COUNT(); } else if (!mem) { SDL_OutOfMemory(); } @@ -6482,5 +6497,5 @@ void SDL_free(void *ptr) } s_mem.free_func(ptr); - (void)SDL_AtomicDecRef(&s_mem.num_allocations); + DECREMENT_ALLOCATION_COUNT(); } diff --git a/libs/SDL3/src/stdlib/SDL_vacopy.h b/libs/SDL3/src/stdlib/SDL_vacopy.h index c50c17f0..ef4d17dc 100644 --- a/libs/SDL3/src/stdlib/SDL_vacopy.h +++ b/libs/SDL3/src/stdlib/SDL_vacopy.h @@ -20,11 +20,7 @@ */ // Do our best to make sure va_copy is working -#ifdef SDL_PLATFORM_NGAGE -#undef va_copy -#define va_copy(dst, src) dst = src - -#elif defined(_MSC_VER) && _MSC_VER <= 1800 +#if defined(_MSC_VER) && _MSC_VER <= 1800 // Visual Studio 2013 tries to link with _vacopy in the C runtime. Newer versions do an inline assignment #undef va_copy #define va_copy(dst, src) dst = src diff --git a/libs/SDL3/src/test/SDL_test_common.c b/libs/SDL3/src/test/SDL_test_common.c index b434be49..62602f66 100644 --- a/libs/SDL3/src/test/SDL_test_common.c +++ b/libs/SDL3/src/test/SDL_test_common.c @@ -834,6 +834,9 @@ static void SDLTest_PrintWindowFlag(char *text, size_t maxlen, SDL_WindowFlags f case SDL_WINDOW_OPENGL: SDL_snprintfcat(text, maxlen, "OPENGL"); break; + case SDL_WINDOW_OCCLUDED: + SDL_snprintfcat(text, maxlen, "OCCLUDED"); + break; case SDL_WINDOW_HIDDEN: SDL_snprintfcat(text, maxlen, "HIDDEN"); break; @@ -861,12 +864,18 @@ static void SDLTest_PrintWindowFlag(char *text, size_t maxlen, SDL_WindowFlags f case SDL_WINDOW_EXTERNAL: SDL_snprintfcat(text, maxlen, "EXTERNAL"); break; + case SDL_WINDOW_MODAL: + SDL_snprintfcat(text, maxlen, "MODAL"); + break; case SDL_WINDOW_HIGH_PIXEL_DENSITY: SDL_snprintfcat(text, maxlen, "HIGH_PIXEL_DENSITY"); break; case SDL_WINDOW_MOUSE_CAPTURE: SDL_snprintfcat(text, maxlen, "MOUSE_CAPTURE"); break; + case SDL_WINDOW_MOUSE_RELATIVE_MODE: + SDL_snprintfcat(text, maxlen, "MOUSE_RELATIVE_MODE"); + break; case SDL_WINDOW_ALWAYS_ON_TOP: SDL_snprintfcat(text, maxlen, "ALWAYS_ON_TOP"); break; @@ -891,6 +900,9 @@ static void SDLTest_PrintWindowFlag(char *text, size_t maxlen, SDL_WindowFlags f case SDL_WINDOW_TRANSPARENT: SDL_snprintfcat(text, maxlen, "TRANSPARENT"); break; + case SDL_WINDOW_NOT_FOCUSABLE: + SDL_snprintfcat(text, maxlen, "NOT_FOCUSABLE"); + break; default: SDL_snprintfcat(text, maxlen, "0x%16.16" SDL_PRIx64, flag); break; @@ -902,6 +914,7 @@ static void SDLTest_PrintWindowFlags(char *text, size_t maxlen, SDL_WindowFlags const SDL_WindowFlags window_flags[] = { SDL_WINDOW_FULLSCREEN, SDL_WINDOW_OPENGL, + SDL_WINDOW_OCCLUDED, SDL_WINDOW_HIDDEN, SDL_WINDOW_BORDERLESS, SDL_WINDOW_RESIZABLE, @@ -911,8 +924,10 @@ static void SDLTest_PrintWindowFlags(char *text, size_t maxlen, SDL_WindowFlags SDL_WINDOW_INPUT_FOCUS, SDL_WINDOW_MOUSE_FOCUS, SDL_WINDOW_EXTERNAL, + SDL_WINDOW_MODAL, SDL_WINDOW_HIGH_PIXEL_DENSITY, SDL_WINDOW_MOUSE_CAPTURE, + SDL_WINDOW_MOUSE_RELATIVE_MODE, SDL_WINDOW_ALWAYS_ON_TOP, SDL_WINDOW_UTILITY, SDL_WINDOW_TOOLTIP, @@ -920,7 +935,8 @@ static void SDLTest_PrintWindowFlags(char *text, size_t maxlen, SDL_WindowFlags SDL_WINDOW_KEYBOARD_GRABBED, SDL_WINDOW_VULKAN, SDL_WINDOW_METAL, - SDL_WINDOW_TRANSPARENT + SDL_WINDOW_TRANSPARENT, + SDL_WINDOW_NOT_FOCUSABLE }; int i; @@ -1077,6 +1093,10 @@ static void SDLTest_PrintRenderer(SDL_Renderer *renderer) name = SDL_GetRendererName(renderer); SDL_Log(" Renderer %s:\n", name); + if (SDL_strcmp(name, "gpu") == 0) { + SDL_GPUDevice *device = SDL_GetPointerProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_GPU_DEVICE_POINTER, NULL); + SDL_Log(" Driver: %s\n", SDL_GetGPUDeviceDriver(device)); + } SDL_Log(" VSync: %d\n", (int)SDL_GetNumberProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_VSYNC_NUMBER, 0)); texture_formats = (const SDL_PixelFormat *)SDL_GetPointerProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_TEXTURE_FORMATS_POINTER, NULL); @@ -1899,13 +1919,13 @@ void SDLTest_PrintEvent(const SDL_Event *event) break; case SDL_EVENT_RENDER_TARGETS_RESET: - SDL_Log("SDL EVENT: render targets reset"); + SDL_Log("SDL EVENT: render targets reset in window %" SDL_PRIu32, event->render.windowID); break; case SDL_EVENT_RENDER_DEVICE_RESET: - SDL_Log("SDL EVENT: render device reset"); + SDL_Log("SDL EVENT: render device reset in window %" SDL_PRIu32, event->render.windowID); break; case SDL_EVENT_RENDER_DEVICE_LOST: - SDL_Log("SDL EVENT: render device lost"); + SDL_Log("SDL EVENT: render device lost in window %" SDL_PRIu32, event->render.windowID); break; case SDL_EVENT_TERMINATING: @@ -2457,11 +2477,17 @@ SDL_AppResult SDLTest_CommonEventMainCallbacks(SDLTest_CommonState *state, const SDL_Window *window = SDL_GetWindowFromEvent(event); if (window) { SDL_WindowFlags flags = SDL_GetWindowFlags(window); + if (!(flags & SDL_WINDOW_RESIZABLE)) { + SDL_SetWindowResizable(window, true); + } if (flags & SDL_WINDOW_MAXIMIZED) { SDL_RestoreWindow(window); } else { SDL_MaximizeWindow(window); } + if (!(flags & SDL_WINDOW_RESIZABLE)) { + SDL_SetWindowResizable(window, false); + } } } if (withShift) { diff --git a/libs/SDL3/src/test/SDL_test_memory.c b/libs/SDL3/src/test/SDL_test_memory.c index 1b790726..594acb3d 100644 --- a/libs/SDL3/src/test/SDL_test_memory.c +++ b/libs/SDL3/src/test/SDL_test_memory.c @@ -72,6 +72,7 @@ static SDL_calloc_func SDL_calloc_orig = NULL; static SDL_realloc_func SDL_realloc_orig = NULL; static SDL_free_func SDL_free_orig = NULL; static int s_previous_allocations = 0; +static int s_unknown_frees = 0; static SDL_tracked_allocation *s_tracked_allocations[256]; static bool s_randfill_allocations = false; static SDL_AtomicInt s_lock; @@ -211,6 +212,9 @@ static void SDL_UntrackAllocation(void *mem) } prev = entry; } + if (s_tracked_allocations < 0) { + s_unknown_frees += 1; + } UNLOCK_ALLOCATOR(); } @@ -277,7 +281,7 @@ static void SDLCALL SDLTest_TrackedFree(void *ptr) return; } - if (!s_previous_allocations) { + if (s_previous_allocations == 0) { SDL_assert(SDL_IsAllocationTracked(ptr)); } SDL_UntrackAllocation(ptr); @@ -293,7 +297,9 @@ void SDLTest_TrackAllocations(void) SDLTest_Crc32Init(&s_crc32_context); s_previous_allocations = SDL_GetNumAllocations(); - if (s_previous_allocations != 0) { + if (s_previous_allocations < 0) { + SDL_Log("SDL was built without allocation count support, disabling free() validation"); + } else if (s_previous_allocations != 0) { SDL_Log("SDLTest_TrackAllocations(): There are %d previous allocations, disabling free() validation", s_previous_allocations); } #ifdef SDLTEST_UNWIND_NO_PROC_NAME_BY_IP @@ -439,7 +445,13 @@ void SDLTest_LogAllocations(void) ++count; } } - (void)SDL_snprintf(line, sizeof(line), "Total: %.2f Kb in %d allocations\n", total_allocated / 1024.0, count); + (void)SDL_snprintf(line, sizeof(line), "Total: %.2f Kb in %d allocations", total_allocated / 1024.0, count); + ADD_LINE(); + if (s_unknown_frees != 0) { + (void)SDL_snprintf(line, sizeof(line), ", %d unknown frees", s_unknown_frees); + ADD_LINE(); + } + (void)SDL_snprintf(line, sizeof(line), "\n"); ADD_LINE(); #undef ADD_LINE diff --git a/libs/SDL3/src/thread/SDL_thread.c b/libs/SDL3/src/thread/SDL_thread.c index e69462cb..fcb11019 100644 --- a/libs/SDL3/src/thread/SDL_thread.c +++ b/libs/SDL3/src/thread/SDL_thread.c @@ -456,6 +456,11 @@ void SDL_WaitThread(SDL_Thread *thread, int *status) } } +SDL_ThreadState SDL_GetThreadState(SDL_Thread *thread) +{ + return (SDL_ThreadState)SDL_GetAtomicInt(&thread->state); +} + void SDL_DetachThread(SDL_Thread *thread) { if (!thread) { diff --git a/libs/SDL3/src/thread/SDL_thread_c.h b/libs/SDL3/src/thread/SDL_thread_c.h index ec6107bb..f8b3fb89 100644 --- a/libs/SDL3/src/thread/SDL_thread_c.h +++ b/libs/SDL3/src/thread/SDL_thread_c.h @@ -38,22 +38,12 @@ #include "vita/SDL_systhread_c.h" #elif defined(SDL_THREAD_N3DS) #include "n3ds/SDL_systhread_c.h" -#elif defined(SDL_THREAD_NGAGE) -#include "ngage/SDL_systhread_c.h" #else #error Need thread implementation for this platform #include "generic/SDL_systhread_c.h" #endif #include "../SDL_error_c.h" -typedef enum SDL_ThreadState -{ - SDL_THREAD_STATE_ALIVE, - SDL_THREAD_STATE_DETACHED, - SDL_THREAD_STATE_ZOMBIE, - SDL_THREAD_STATE_CLEANED, -} SDL_ThreadState; - // This is the system-independent thread info structure struct SDL_Thread { diff --git a/libs/SDL3/src/thread/ngage/SDL_sysmutex.cpp b/libs/SDL3/src/thread/ngage/SDL_sysmutex.cpp deleted file mode 100644 index 307896e9..00000000 --- a/libs/SDL3/src/thread/ngage/SDL_sysmutex.cpp +++ /dev/null @@ -1,100 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2024 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "SDL_internal.h" - -// An implementation of mutexes using the Symbian API. - -#include - -#include "SDL_systhread_c.h" - -struct SDL_Mutex -{ - TInt handle; -}; - -extern TInt CreateUnique(TInt (*aFunc)(const TDesC &aName, TAny *, TAny *), TAny *, TAny *); - -static TInt NewMutex(const TDesC &aName, TAny *aPtr1, TAny *) -{ - return ((RMutex *)aPtr1)->CreateGlobal(aName); -} - -// Create a mutex -SDL_Mutex *SDL_CreateMutex(void) -{ - RMutex rmutex; - - TInt status = CreateUnique(NewMutex, &rmutex, NULL); - if (status != KErrNone) { - SDL_SetError("Couldn't create mutex."); - return NULL; - } - SDL_Mutex *mutex = new /*(ELeave)*/ SDL_Mutex; - mutex->handle = rmutex.Handle(); - return mutex; -} - -// Free the mutex -void SDL_DestroyMutex(SDL_Mutex *mutex) -{ - if (mutex) { - RMutex rmutex; - rmutex.SetHandle(mutex->handle); - rmutex.Signal(); - rmutex.Close(); - delete (mutex); - mutex = NULL; - } -} - -// Lock the mutex -void SDL_LockMutex(SDL_Mutex *mutex) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes -{ - if (mutex) { - RMutex rmutex; - rmutex.SetHandle(mutex->handle); - rmutex.Wait(); - } -} - -// Try to lock the mutex -#if 0 -bool SDL_TryLockMutex(SDL_Mutex *mutex) -{ - if (mutex) { - // Not yet implemented. - return true; - } - return true; -} -#endif - -// Unlock the mutex -void SDL_UnlockMutex(SDL_Mutex *mutex) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes -{ - if (mutex) { - RMutex rmutex; - rmutex.SetHandle(mutex->handle); - rmutex.Signal(); - } -} - diff --git a/libs/SDL3/src/thread/ngage/SDL_syssem.cpp b/libs/SDL3/src/thread/ngage/SDL_syssem.cpp deleted file mode 100644 index 01de3845..00000000 --- a/libs/SDL3/src/thread/ngage/SDL_syssem.cpp +++ /dev/null @@ -1,157 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2024 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "SDL_internal.h" - -// An implementation of semaphores using the Symbian API. - -#include - -struct SDL_Semaphore -{ - TInt handle; - TInt count; -}; - -struct TInfo -{ - TInfo(TInt aTime, TInt aHandle) : iTime(aTime), iHandle(aHandle), iVal(true) {} - TInt iTime; - TInt iHandle; - bool iVal; -}; - -extern TInt CreateUnique(TInt (*aFunc)(const TDesC &aName, TAny *, TAny *), TAny *, TAny *); - -static TBool RunThread(TAny *aInfo) -{ - TInfo *info = STATIC_CAST(TInfo *, aInfo); - User::After(info->iTime); - RSemaphore sema; - sema.SetHandle(info->iHandle); - sema.Signal(); - info->iVal = false; - return 0; -} - -static TInt NewThread(const TDesC &aName, TAny *aPtr1, TAny *aPtr2) -{ - return ((RThread *)(aPtr1))->Create(aName, RunThread, KDefaultStackSize, NULL, aPtr2); -} - -static TInt NewSema(const TDesC &aName, TAny *aPtr1, TAny *aPtr2) -{ - TInt value = *((TInt *)aPtr2); - return ((RSemaphore *)aPtr1)->CreateGlobal(aName, value); -} - -static void WaitAll(SDL_Semaphore *sem) -{ - RSemaphore sema; - sema.SetHandle(sem->handle); - sema.Wait(); - while (sem->count < 0) { - sema.Wait(); - } -} - -SDL_Semaphore *SDL_CreateSemaphore(Uint32 initial_value) -{ - RSemaphore s; - TInt status = CreateUnique(NewSema, &s, &initial_value); - if (status != KErrNone) { - SDL_SetError("Couldn't create semaphore"); - } - SDL_Semaphore *sem = new /*(ELeave)*/ SDL_Semaphore; - sem->handle = s.Handle(); - sem->count = initial_value; - return sem; -} - -void SDL_DestroySemaphore(SDL_Semaphore *sem) -{ - if (sem) { - RSemaphore sema; - sema.SetHandle(sem->handle); - sema.Signal(sema.Count()); - sema.Close(); - delete sem; - sem = NULL; - } -} - -bool SDL_WaitSemaphoreTimeoutNS(SDL_Semaphore *sem, Sint64 timeoutNS) -{ - if (!sem) { - return true; - } - - if (timeoutNS == 0) { - if (sem->count > 0) { - --sem->count; - return true; - } - return false; - } - - if (timeoutNS == -1) { // -1 == wait indefinitely. - WaitAll(sem); - return true; - } - - RThread thread; - TInfo *info = new (ELeave) TInfo((TInt)SDL_NS_TO_MS(timeoutNS), sem->handle); - TInt status = CreateUnique(NewThread, &thread, info); - - if (status != KErrNone) { - return false; - } - - thread.Resume(); - WaitAll(sem); - - if (thread.ExitType() == EExitPending) { - thread.Kill(false); - } - - thread.Close(); - return info->iVal; -} - -Uint32 SDL_GetSemaphoreValue(SDL_Semaphore *sem) -{ - if (!sem) { - SDL_InvalidParamError("sem"); - return 0; - } - return sem->count; -} - -int SDL_SignalSemaphore(SDL_Semaphore *sem) -{ - if (!sem) { - return SDL_InvalidParamError("sem"); - } - sem->count++; - RSemaphore sema; - sema.SetHandle(sem->handle); - sema.Signal(); - return 0; -} diff --git a/libs/SDL3/src/thread/ngage/SDL_systhread.cpp b/libs/SDL3/src/thread/ngage/SDL_systhread.cpp deleted file mode 100644 index ff3e3e2f..00000000 --- a/libs/SDL3/src/thread/ngage/SDL_systhread.cpp +++ /dev/null @@ -1,112 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2024 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "SDL_internal.h" - -#ifdef SDL_THREAD_NGAGE - -// N-Gage thread management routines for SDL - -#include - -extern "C" { -#undef NULL -#include "../SDL_systhread.h" -#include "../SDL_thread_c.h" -}; - -static int object_count; - -static int RunThread(TAny *data) -{ - SDL_RunThread((SDL_Thread *)data); - return 0; -} - -static TInt NewThread(const TDesC &aName, TAny *aPtr1, TAny *aPtr2) -{ - return ((RThread *)(aPtr1))->Create(aName, RunThread, KDefaultStackSize, NULL, aPtr2); -} - -int CreateUnique(TInt (*aFunc)(const TDesC &aName, TAny *, TAny *), TAny *aPtr1, TAny *aPtr2) -{ - TBuf<16> name; - TInt status = KErrNone; - do { - object_count++; - name.Format(_L("SDL_%x"), object_count); - status = aFunc(name, aPtr1, aPtr2); - } while (status == KErrAlreadyExists); - return status; -} - -bool SDL_SYS_CreateThread(SDL_Thread *thread, - SDL_FunctionPointer pfnBeginThread, - SDL_FunctionPointer pfnEndThread) -{ - RThread rthread; - - TInt status = CreateUnique(NewThread, &rthread, thread); - if (status != KErrNone) { - delete (RThread *)thread->handle; - thread->handle = NULL; - return SDL_SetError("Not enough resources to create thread"); - } - - rthread.Resume(); - thread->handle = rthread.Handle(); - return true; -} - -void SDL_SYS_SetupThread(const char *name) -{ - return; -} - -SDL_ThreadID SDL_GetCurrentThreadID(void) -{ - RThread current; - TThreadId id = current.Id(); - return id; -} - -bool SDL_SYS_SetThreadPriority(SDL_ThreadPriority priority) -{ - return true; -} - -void SDL_SYS_WaitThread(SDL_Thread *thread) -{ - RThread t; - t.Open(thread->threadid); - if (t.ExitReason() == EExitPending) { - TRequestStatus status; - t.Logon(status); - User::WaitForRequest(status); - } - t.Close(); -} - -void SDL_SYS_DetachThread(SDL_Thread *thread) -{ - return; -} - -#endif // SDL_THREAD_NGAGE diff --git a/libs/SDL3/src/timer/SDL_timer.c b/libs/SDL3/src/timer/SDL_timer.c index b47117e5..c2dc0954 100644 --- a/libs/SDL3/src/timer/SDL_timer.c +++ b/libs/SDL3/src/timer/SDL_timer.c @@ -533,7 +533,7 @@ bool SDL_RemoveTimer(SDL_TimerID id) } } -#endif // !defined(SDL_PLATFORM_EMSCRIPTEN) || !SDL_THREADS_DISABLED +#endif // !SDL_PLATFORM_EMSCRIPTEN || !SDL_THREADS_DISABLED static Uint64 tick_start; static Uint32 tick_numerator_ns; diff --git a/libs/SDL3/src/timer/ngage/SDL_systimer.cpp b/libs/SDL3/src/timer/ngage/SDL_systimer.cpp deleted file mode 100644 index bfcacc63..00000000 --- a/libs/SDL3/src/timer/ngage/SDL_systimer.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2024 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "SDL_internal.h" - -#ifdef SDL_TIMER_NGAGE - -#include -#include - -static TUint start_tick = 0; - -#ifdef __cplusplus -extern "C" { -#endif - - -Uint64 SDL_GetPerformanceCounter(void) -{ - // FIXME: Need to account for 32-bit wrapping - return (Uint64)User::TickCount(); -} - -Uint64 SDL_GetPerformanceFrequency(void) -{ - return SDL_US_PER_SECOND; -} - -void SDL_SYS_DelayNS(Uint64 ns) -{ - const Uint64 max_delay = 0x7fffffffLL * SDL_NS_PER_US; - if (ns > max_delay) { - ns = max_delay; - } - User::After(TTimeIntervalMicroSeconds32((TInt)SDL_NS_TO_US(ns))); -} - -#ifdef __cplusplus -} -#endif - -#endif // SDL_TIMER_NGAGE diff --git a/libs/SDL3/src/tray/cocoa/SDL_tray.m b/libs/SDL3/src/tray/cocoa/SDL_tray.m new file mode 100644 index 00000000..5197b7da --- /dev/null +++ b/libs/SDL3/src/tray/cocoa/SDL_tray.m @@ -0,0 +1,450 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2024 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#include "SDL_internal.h" + +#ifdef SDL_PLATFORM_MACOS + +#include + +#include "../../video/SDL_surface_c.h" + +/* applicationDockMenu */ + +struct SDL_TrayMenu { + NSMenu *nsmenu; + + int nEntries; + SDL_TrayEntry **entries; + + SDL_Tray *parent_tray; + SDL_TrayEntry *parent_entry; +}; + +struct SDL_TrayEntry { + NSMenuItem *nsitem; + + SDL_TrayEntryFlags flags; + SDL_TrayCallback callback; + void *userdata; + SDL_TrayMenu *submenu; + + SDL_TrayMenu *parent; +}; + +struct SDL_Tray { + NSStatusBar *statusBar; + NSStatusItem *statusItem; + + SDL_TrayMenu *menu; +}; + +static NSApplication *app = NULL; + +@interface AppDelegate: NSObject + - (IBAction)menu:(id)sender; +@end + +@implementation AppDelegate{} + - (IBAction)menu:(id)sender + { + SDL_TrayEntry *entry = [[sender representedObject] pointerValue]; + + if (!entry) { + return; + } + + if (entry->flags & SDL_TRAYENTRY_CHECKBOX) { + SDL_SetTrayEntryChecked(entry, !SDL_GetTrayEntryChecked(entry)); + } + + if (entry->callback) { + entry->callback(entry->userdata, entry); + } + } +@end + +static void DestroySDLMenu(SDL_TrayMenu *menu) +{ + for (int i = 0; i < menu->nEntries; i++) { + if (menu->entries[i] && menu->entries[i]->submenu) { + DestroySDLMenu(menu->entries[i]->submenu); + } + SDL_free(menu->entries[i]); + } + + SDL_free(menu->entries); + + if (menu->parent_entry) { + [menu->parent_entry->parent->nsmenu setSubmenu:nil forItem:menu->parent_entry->nsitem]; + } else if (menu->parent_tray) { + [menu->parent_tray->statusItem setMenu:nil]; + } + + SDL_free(menu); +} + +SDL_Tray *SDL_CreateTray(SDL_Surface *icon, const char *tooltip) +{ + SDL_Tray *tray = (SDL_Tray *)SDL_calloc(1, sizeof(*tray)); + + AppDelegate *delegate = [[AppDelegate alloc] init]; + app = [NSApplication sharedApplication]; + [app setDelegate:delegate]; + + if (!tray) { + return NULL; + } + + tray->statusItem = nil; + tray->statusBar = [NSStatusBar systemStatusBar]; + tray->statusItem = [tray->statusBar statusItemWithLength:NSVariableStatusItemLength]; + [app activateIgnoringOtherApps:TRUE]; + + if (tooltip) { + tray->statusItem.button.toolTip = [NSString stringWithUTF8String:tooltip]; + } else { + tray->statusItem.button.toolTip = nil; + } + + if (icon) { + SDL_Surface *iconfmt = SDL_ConvertSurface(icon, SDL_PIXELFORMAT_RGBA32); + if (!iconfmt) { + goto skip_putting_an_icon; + } + + NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:(unsigned char **)&iconfmt->pixels + pixelsWide:iconfmt->w + pixelsHigh:iconfmt->h + bitsPerSample:8 + samplesPerPixel:4 + hasAlpha:YES + isPlanar:NO + colorSpaceName:NSCalibratedRGBColorSpace + bytesPerRow:iconfmt->pitch + bitsPerPixel:32]; + NSImage *iconimg = [[NSImage alloc] initWithSize:NSMakeSize(iconfmt->w, iconfmt->h)]; + [iconimg addRepresentation:bitmap]; + + /* A typical icon size is 22x22 on macOS. Failing to resize the icon + may give oversized status bar buttons. */ + NSImage *iconimg22 = [[NSImage alloc] initWithSize:NSMakeSize(22, 22)]; + [iconimg22 lockFocus]; + [iconimg setSize:NSMakeSize(22, 22)]; + [iconimg drawInRect:NSMakeRect(0, 0, 22, 22)]; + [iconimg22 unlockFocus]; + + tray->statusItem.button.image = iconimg22; + + SDL_DestroySurface(iconfmt); + } + +skip_putting_an_icon: + return tray; +} + +void SDL_SetTrayIcon(SDL_Tray *tray, SDL_Surface *icon) +{ + if (!icon) { + tray->statusItem.button.image = nil; + return; + } + + SDL_Surface *iconfmt = SDL_ConvertSurface(icon, SDL_PIXELFORMAT_RGBA32); + if (!iconfmt) { + tray->statusItem.button.image = nil; + return; + } + + NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:(unsigned char **)&iconfmt->pixels + pixelsWide:iconfmt->w + pixelsHigh:iconfmt->h + bitsPerSample:8 + samplesPerPixel:4 + hasAlpha:YES + isPlanar:NO + colorSpaceName:NSCalibratedRGBColorSpace + bytesPerRow:iconfmt->pitch + bitsPerPixel:32]; + NSImage *iconimg = [[NSImage alloc] initWithSize:NSMakeSize(iconfmt->w, iconfmt->h)]; + [iconimg addRepresentation:bitmap]; + + /* A typical icon size is 22x22 on macOS. Failing to resize the icon + may give oversized status bar buttons. */ + NSImage *iconimg22 = [[NSImage alloc] initWithSize:NSMakeSize(22, 22)]; + [iconimg22 lockFocus]; + [iconimg setSize:NSMakeSize(22, 22)]; + [iconimg drawInRect:NSMakeRect(0, 0, 22, 22)]; + [iconimg22 unlockFocus]; + + tray->statusItem.button.image = iconimg22; + + SDL_DestroySurface(iconfmt); +} + +void SDL_SetTrayTooltip(SDL_Tray *tray, const char *tooltip) +{ + if (tooltip) { + tray->statusItem.button.toolTip = [NSString stringWithUTF8String:tooltip]; + } else { + tray->statusItem.button.toolTip = nil; + } +} + +SDL_TrayMenu *SDL_CreateTrayMenu(SDL_Tray *tray) +{ + SDL_TrayMenu *menu = (SDL_TrayMenu *)SDL_calloc(1, sizeof(*menu)); + if (!menu) { + return NULL; + } + + NSMenu *nsmenu = [[NSMenu alloc] init]; + [nsmenu setAutoenablesItems:FALSE]; + + [tray->statusItem setMenu:nsmenu]; + + tray->menu = menu; + menu->nsmenu = nsmenu; + menu->nEntries = 0; + menu->entries = NULL; + menu->parent_tray = tray; + menu->parent_entry = NULL; + + return menu; +} + +SDL_TrayMenu *SDL_GetTrayMenu(SDL_Tray *tray) +{ + return tray->menu; +} + +SDL_TrayMenu *SDL_CreateTraySubmenu(SDL_TrayEntry *entry) +{ + if (entry->submenu) { + SDL_SetError("Tray entry submenu already exists"); + return NULL; + } + + if (!(entry->flags & SDL_TRAYENTRY_SUBMENU)) { + SDL_SetError("Cannot create submenu for entry not created with SDL_TRAYENTRY_SUBMENU"); + return NULL; + } + + SDL_TrayMenu *menu = (SDL_TrayMenu *)SDL_calloc(1, sizeof(*menu)); + if (!menu) { + return NULL; + } + + NSMenu *nsmenu = [[NSMenu alloc] init]; + [nsmenu setAutoenablesItems:FALSE]; + + entry->submenu = menu; + menu->nsmenu = nsmenu; + menu->nEntries = 0; + menu->entries = NULL; + menu->parent_tray = NULL; + menu->parent_entry = entry; + + [entry->parent->nsmenu setSubmenu:nsmenu forItem:entry->nsitem]; + + return menu; +} + +SDL_TrayMenu *SDL_GetTraySubmenu(SDL_TrayEntry *entry) +{ + return entry->submenu; +} + +const SDL_TrayEntry **SDL_GetTrayEntries(SDL_TrayMenu *menu, int *size) +{ + if (size) { + *size = menu->nEntries; + } + + return (const SDL_TrayEntry **) menu->entries; +} + +void SDL_RemoveTrayEntry(SDL_TrayEntry *entry) +{ + if (!entry) { + return; + } + + SDL_TrayMenu *menu = entry->parent; + + bool found = false; + for (int i = 0; i < menu->nEntries - 1; i++) { + if (menu->entries[i] == entry) { + found = true; + } + + if (found) { + menu->entries[i] = menu->entries[i + 1]; + } + } + + if (entry->submenu) { + DestroySDLMenu(entry->submenu); + } + + menu->nEntries--; + SDL_TrayEntry **new_entries = (SDL_TrayEntry **)SDL_realloc(menu->entries, menu->nEntries * sizeof(*new_entries)); + + /* Not sure why shrinking would fail, but even if it does, we can live with a "too big" array */ + if (new_entries) { + menu->entries = new_entries; + } + + [menu->nsmenu removeItem:entry->nsitem]; + + SDL_free(entry); +} + +SDL_TrayEntry *SDL_InsertTrayEntryAt(SDL_TrayMenu *menu, int pos, const char *label, SDL_TrayEntryFlags flags) +{ + if (pos < -1 || pos > menu->nEntries) { + SDL_InvalidParamError("pos"); + return NULL; + } + + if (pos == -1) { + pos = menu->nEntries; + } + + SDL_TrayEntry *entry = (SDL_TrayEntry *)SDL_calloc(1, sizeof(*entry)); + if (!entry) { + return NULL; + } + + SDL_TrayEntry **new_entries = (SDL_TrayEntry **)SDL_realloc(menu->entries, (menu->nEntries + 1) * sizeof(*new_entries)); + if (!new_entries) { + SDL_free(entry); + return NULL; + } + + menu->entries = new_entries; + menu->nEntries++; + + for (int i = menu->nEntries - 1; i > pos; i--) { + menu->entries[i] = menu->entries[i - 1]; + } + + new_entries[pos] = entry; + + NSMenuItem *nsitem; + if (label == NULL) { + nsitem = [NSMenuItem separatorItem]; + } else { + nsitem = [[NSMenuItem alloc] initWithTitle:[NSString stringWithUTF8String:label] action:@selector(menu:) keyEquivalent:@""]; + [nsitem setEnabled:((flags & SDL_TRAYENTRY_DISABLED) ? FALSE : TRUE)]; + [nsitem setState:((flags & SDL_TRAYENTRY_CHECKED) ? NSControlStateValueOn : NSControlStateValueOff)]; + [nsitem setRepresentedObject:[NSValue valueWithPointer:entry]]; + } + + [menu->nsmenu insertItem:nsitem atIndex:pos]; + + entry->nsitem = nsitem; + entry->flags = flags; + entry->callback = NULL; + entry->userdata = NULL; + entry->submenu = NULL; + entry->parent = menu; + + return entry; +} + +void SDL_SetTrayEntryLabel(SDL_TrayEntry *entry, const char *label) +{ + [entry->nsitem setTitle:[NSString stringWithUTF8String:label]]; +} + +const char *SDL_GetTrayEntryLabel(SDL_TrayEntry *entry) +{ + return [[entry->nsitem title] UTF8String]; +} + +void SDL_SetTrayEntryChecked(SDL_TrayEntry *entry, bool checked) +{ + [entry->nsitem setState:(checked ? NSControlStateValueOn : NSControlStateValueOff)]; +} + +bool SDL_GetTrayEntryChecked(SDL_TrayEntry *entry) +{ + return entry->nsitem.state == NSControlStateValueOn; +} + +void SDL_SetTrayEntryEnabled(SDL_TrayEntry *entry, bool enabled) +{ + if (!(entry->flags & SDL_TRAYENTRY_CHECKBOX)) { + SDL_SetError("Cannot update check for entry not created with SDL_TRAYENTRY_CHECKBOX"); + return; + } + + [entry->nsitem setEnabled:(enabled ? YES : NO)]; +} + +bool SDL_GetTrayEntryEnabled(SDL_TrayEntry *entry) +{ + if (!(entry->flags & SDL_TRAYENTRY_CHECKBOX)) { + SDL_SetError("Cannot fetch check for entry not created with SDL_TRAYENTRY_CHECKBOX"); + return false; + } + + return entry->nsitem.enabled; +} + +void SDL_SetTrayEntryCallback(SDL_TrayEntry *entry, SDL_TrayCallback callback, void *userdata) +{ + entry->callback = callback; + entry->userdata = userdata; +} + +SDL_TrayMenu *SDL_GetTrayEntryParent(SDL_TrayEntry *entry) +{ + return entry->parent; +} + +SDL_TrayEntry *SDL_GetTrayMenuParentEntry(SDL_TrayMenu *menu) +{ + return menu->parent_entry; +} + +SDL_Tray *SDL_GetTrayMenuParentTray(SDL_TrayMenu *menu) +{ + return menu->parent_tray; +} + +void SDL_DestroyTray(SDL_Tray *tray) +{ + if (!tray) { + return; + } + + [[NSStatusBar systemStatusBar] removeStatusItem:tray->statusItem]; + + if (tray->menu) { + DestroySDLMenu(tray->menu); + } + + SDL_free(tray); +} + +#endif // SDL_PLATFORM_MACOS diff --git a/libs/SDL3/src/tray/dummy/SDL_tray.c b/libs/SDL3/src/tray/dummy/SDL_tray.c new file mode 100644 index 00000000..e8eaebd3 --- /dev/null +++ b/libs/SDL3/src/tray/dummy/SDL_tray.c @@ -0,0 +1,143 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2024 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#include "SDL_internal.h" + +#ifndef SDL_PLATFORM_MACOS + +SDL_Tray *SDL_CreateTray(SDL_Surface *icon, const char *tooltip) +{ + SDL_Unsupported(); + return NULL; +} + +void SDL_SetTrayIcon(SDL_Tray *tray, SDL_Surface *icon) +{ + SDL_Unsupported(); +} + +void SDL_SetTrayTooltip(SDL_Tray *tray, const char *tooltip) +{ + SDL_Unsupported(); +} + +SDL_TrayMenu *SDL_CreateTrayMenu(SDL_Tray *tray) +{ + SDL_Unsupported(); + return NULL; +} + +SDL_TrayMenu *SDL_GetTrayMenu(SDL_Tray *tray) +{ + SDL_Unsupported(); + return NULL; +} + +SDL_TrayMenu *SDL_CreateTraySubmenu(SDL_TrayEntry *entry) +{ + SDL_Unsupported(); + return NULL; +} + +SDL_TrayMenu *SDL_GetTraySubmenu(SDL_TrayEntry *entry) +{ + return NULL; +} + +const SDL_TrayEntry **SDL_GetTrayEntries(SDL_TrayMenu *menu, int *size) +{ + SDL_Unsupported(); + return NULL; +} + +void SDL_RemoveTrayEntry(SDL_TrayEntry *entry) +{ + SDL_Unsupported(); +} + +SDL_TrayEntry *SDL_InsertTrayEntryAt(SDL_TrayMenu *menu, int pos, const char *label, SDL_TrayEntryFlags flags) +{ + SDL_Unsupported(); + return NULL; +} + +void SDL_SetTrayEntryLabel(SDL_TrayEntry *entry, const char *label) +{ + SDL_Unsupported(); +} + +const char *SDL_GetTrayEntryLabel(SDL_TrayEntry *entry) +{ + SDL_Unsupported(); + return NULL; +} + +void SDL_SetTrayEntryChecked(SDL_TrayEntry *entry, bool checked) +{ + SDL_Unsupported(); +} + +bool SDL_GetTrayEntryChecked(SDL_TrayEntry *entry) +{ + SDL_Unsupported(); + return false; +} + +void SDL_SetTrayEntryEnabled(SDL_TrayEntry *entry, bool enabled) +{ + SDL_Unsupported(); +} + +bool SDL_GetTrayEntryEnabled(SDL_TrayEntry *entry) +{ + SDL_Unsupported(); + return false; +} + +void SDL_SetTrayEntryCallback(SDL_TrayEntry *entry, SDL_TrayCallback callback, void *userdata) +{ + SDL_Unsupported(); +} + +SDL_TrayMenu *SDL_GetTrayEntryParent(SDL_TrayEntry *entry) +{ + SDL_Unsupported(); + return NULL; +} + +SDL_TrayEntry *SDL_GetTrayMenuParentEntry(SDL_TrayMenu *menu) +{ + SDL_Unsupported(); + return NULL; +} + +SDL_Tray *SDL_GetTrayMenuParentTray(SDL_TrayMenu *menu) +{ + SDL_Unsupported(); + return NULL; +} + +void SDL_DestroyTray(SDL_Tray *tray) +{ + SDL_Unsupported(); +} + +#endif // !SDL_PLATFORM_MACOS diff --git a/libs/SDL3/src/tray/unix/SDL_tray.c b/libs/SDL3/src/tray/unix/SDL_tray.c new file mode 100644 index 00000000..b2e81640 --- /dev/null +++ b/libs/SDL3/src/tray/unix/SDL_tray.c @@ -0,0 +1,664 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2024 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#include "SDL_internal.h" + +#include + +/* getpid() */ +#include + +/* APPINDICATOR_HEADER is not exposed as a build setting, but the code has been + written nevertheless to make future maintenance easier. */ +#ifdef APPINDICATOR_HEADER +#include APPINDICATOR_HEADER +#else +/* ------------------------------------------------------------------------- */ +/* BEGIN THIRD-PARTY HEADER CONTENT */ +/* ------------------------------------------------------------------------- */ +/* Glib 2.0 */ + +typedef unsigned long gulong; +typedef void* gpointer; +typedef char gchar; +typedef int gint; +typedef unsigned int guint; +typedef gint gboolean; +typedef void (*GCallback)(void); +typedef struct _GClosure GClosure; +typedef void (*GClosureNotify) (gpointer data, GClosure *closure); +typedef gboolean (*GSourceFunc) (gpointer user_data); +typedef enum +{ + G_CONNECT_AFTER = 1 << 0, + G_CONNECT_SWAPPED = 1 << 1 +} GConnectFlags; +gulong (*g_signal_connect_data)(gpointer instance, const gchar *detailed_signal, GCallback c_handler, gpointer data, GClosureNotify destroy_data, GConnectFlags connect_flags); + +#define g_signal_connect(instance, detailed_signal, c_handler, data) \ + g_signal_connect_data ((instance), (detailed_signal), (c_handler), (data), NULL, (GConnectFlags) 0) + +#define _G_TYPE_CIC(ip, gt, ct) ((ct*) ip) + +#define G_TYPE_CHECK_INSTANCE_CAST(instance, g_type, c_type) (_G_TYPE_CIC ((instance), (g_type), c_type)) + +#define G_CALLBACK(f) ((GCallback) (f)) + +#define FALSE 0 +#define TRUE 1 + +/* GTK 3.0 */ + +typedef struct _GtkMenu GtkMenu; +typedef struct _GtkMenuItem GtkMenuItem; +typedef struct _GtkMenuShell GtkMenuShell; +typedef struct _GtkWidget GtkWidget; +typedef struct _GtkCheckMenuItem GtkCheckMenuItem; + +gboolean (*gtk_init_check)(int *argc, char ***argv); +void (*gtk_main)(void); + +GtkWidget* (*gtk_menu_new)(void); +GtkWidget* (*gtk_separator_menu_item_new)(void); +GtkWidget* (*gtk_menu_item_new_with_label)(const gchar *label); +void (*gtk_menu_item_set_submenu)(GtkMenuItem *menu_item, GtkWidget *submenu); +GtkWidget* (*gtk_check_menu_item_new_with_label)(const gchar *label); +void (*gtk_check_menu_item_set_active)(GtkCheckMenuItem *check_menu_item, gboolean is_active); +void (*gtk_widget_set_sensitive)(GtkWidget *widget, gboolean sensitive); +void (*gtk_widget_show)(GtkWidget *widget); +void (*gtk_menu_shell_append)(GtkMenuShell *menu_shell, GtkWidget *child); +void (*gtk_menu_shell_insert)(GtkMenuShell *menu_shell, GtkWidget *child, gint position); +void (*gtk_widget_destroy)(GtkWidget *widget); +const gchar *(*gtk_menu_item_get_label)(GtkMenuItem *menu_item); +void (*gtk_menu_item_set_label)(GtkMenuItem *menu_item, const gchar *label); +gboolean (*gtk_check_menu_item_get_active)(GtkCheckMenuItem *check_menu_item); +gboolean (*gtk_widget_get_sensitive)(GtkWidget *widget); + +#define GTK_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_MENU_ITEM, GtkMenuItem)) +#define GTK_WIDGET(widget) (G_TYPE_CHECK_INSTANCE_CAST ((widget), GTK_TYPE_WIDGET, GtkWidget)) +#define GTK_CHECK_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CHECK_MENU_ITEM, GtkCheckMenuItem)) +#define GTK_MENU(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_MENU, GtkMenu)) + +/* AppIndicator */ + +typedef enum { + APP_INDICATOR_CATEGORY_APPLICATION_STATUS, + APP_INDICATOR_CATEGORY_COMMUNICATIONS, + APP_INDICATOR_CATEGORY_SYSTEM_SERVICES, + APP_INDICATOR_CATEGORY_HARDWARE, + APP_INDICATOR_CATEGORY_OTHER +} AppIndicatorCategory; + +typedef enum { + APP_INDICATOR_STATUS_PASSIVE, + APP_INDICATOR_STATUS_ACTIVE, + APP_INDICATOR_STATUS_ATTENTION +} AppIndicatorStatus; + +typedef struct _AppIndicator AppIndicator; +AppIndicator *(*app_indicator_new)(const gchar *id, const gchar *icon_name, AppIndicatorCategory category); +void (*app_indicator_set_status)(AppIndicator *self, AppIndicatorStatus status); +void (*app_indicator_set_icon)(AppIndicator *self, const gchar *icon_name); +void (*app_indicator_set_menu)(AppIndicator *self, GtkMenu *menu); +/* ------------------------------------------------------------------------- */ +/* END THIRD-PARTY HEADER CONTENT */ +/* ------------------------------------------------------------------------- */ +#endif + +static int main_gtk_thread(void *data) +{ + gtk_main(); + return 0; +} + +#ifdef APPINDICATOR_HEADER + +static void quit_gtk(void) +{ +} + +static bool init_gtk(void) +{ + SDL_DetachThread(SDL_CreateThread(main_gtk_thread, "tray gtk", NULL)); +} + +#else + +static bool gtk_is_init = false; + +static void *libappindicator = NULL; +static void *libgtk = NULL; +static void *libgdk = NULL; + +static void quit_gtk(void) +{ + if (libappindicator) { + dlclose(libappindicator); + libappindicator = NULL; + } + + if (libgtk) { + dlclose(libgtk); + libgtk = NULL; + } + + if (libgdk) { + dlclose(libgdk); + libgdk = NULL; + } + + gtk_is_init = false; +} + +const char *appindicator_names[] = { + "libayatana-appindicator3.so", + "libayatana-appindicator3.so.1", + "libayatana-appindicator.so", + "libappindicator3.so", + "libappindicator3.so.1", + "libappindicator.so", + "libappindicator.so.1", + NULL +}; + +const char *gtk_names[] = { + "libgtk-3.so", + "libgtk-3.so.0", + NULL +}; + +const char *gdk_names[] = { + "libgdk-3.so", + "libgdk-3.so.0", + NULL +}; + +static void *find_lib(const char **names) +{ + const char **name_ptr = names; + void *handle = NULL; + + do { + handle = dlopen(*name_ptr, RTLD_LAZY); + } while (*++name_ptr && !handle); + + return handle; +} + +static bool init_gtk(void) +{ + if (gtk_is_init) { + return true; + } + + libappindicator = find_lib(appindicator_names); + libgtk = find_lib(gtk_names); + libgdk = find_lib(gdk_names); + + if (!libappindicator || !libgtk || !libgdk) { + quit_gtk(); + return SDL_SetError("Could not load GTK/AppIndicator libraries"); + } + + gtk_init_check = dlsym(libgtk, "gtk_init_check"); + gtk_main = dlsym(libgtk, "gtk_main"); + gtk_menu_new = dlsym(libgtk, "gtk_menu_new"); + gtk_separator_menu_item_new = dlsym(libgtk, "gtk_separator_menu_item_new"); + gtk_menu_item_new_with_label = dlsym(libgtk, "gtk_menu_item_new_with_label"); + gtk_menu_item_set_submenu = dlsym(libgtk, "gtk_menu_item_set_submenu"); + gtk_check_menu_item_new_with_label = dlsym(libgtk, "gtk_check_menu_item_new_with_label"); + gtk_check_menu_item_set_active = dlsym(libgtk, "gtk_check_menu_item_set_active"); + gtk_widget_set_sensitive = dlsym(libgtk, "gtk_widget_set_sensitive"); + gtk_widget_show = dlsym(libgtk, "gtk_widget_show"); + gtk_menu_shell_append = dlsym(libgtk, "gtk_menu_shell_append"); + gtk_menu_shell_insert = dlsym(libgtk, "gtk_menu_shell_insert"); + gtk_widget_destroy = dlsym(libgtk, "gtk_widget_destroy"); + gtk_menu_item_get_label = dlsym(libgtk, "gtk_menu_item_get_label"); + gtk_menu_item_set_label = dlsym(libgtk, "gtk_menu_item_set_label"); + gtk_check_menu_item_get_active = dlsym(libgtk, "gtk_check_menu_item_get_active"); + gtk_widget_get_sensitive = dlsym(libgtk, "gtk_widget_get_sensitive"); + + g_signal_connect_data = dlsym(libgdk, "g_signal_connect_data"); + + app_indicator_new = dlsym(libappindicator, "app_indicator_new"); + app_indicator_set_status = dlsym(libappindicator, "app_indicator_set_status"); + app_indicator_set_icon = dlsym(libappindicator, "app_indicator_set_icon"); + app_indicator_set_menu = dlsym(libappindicator, "app_indicator_set_menu"); + + if (!gtk_init_check || + !gtk_main || + !gtk_menu_new || + !gtk_separator_menu_item_new || + !gtk_menu_item_new_with_label || + !gtk_menu_item_set_submenu || + !gtk_check_menu_item_new_with_label || + !gtk_check_menu_item_set_active || + !gtk_widget_set_sensitive || + !gtk_widget_show || + !gtk_menu_shell_append || + !gtk_menu_shell_insert || + !gtk_widget_destroy || + !g_signal_connect_data || + !app_indicator_new || + !app_indicator_set_status || + !app_indicator_set_icon || + !app_indicator_set_menu || + !gtk_menu_item_get_label || + !gtk_menu_item_set_label || + !gtk_check_menu_item_get_active || + !gtk_widget_get_sensitive) { + quit_gtk(); + return SDL_SetError("Could not load GTK/AppIndicator functions"); + } + + if (gtk_init_check(0, NULL) == FALSE) { + quit_gtk(); + return SDL_SetError("Could not init GTK"); + } + + gtk_is_init = true; + + SDL_DetachThread(SDL_CreateThread(main_gtk_thread, "tray gtk", NULL)); + + return true; +} +#endif + +struct SDL_TrayMenu { + GtkMenuShell *menu; + + size_t nEntries; + SDL_TrayEntry **entries; + + SDL_Tray *parent_tray; + SDL_TrayEntry *parent_entry; +}; + +struct SDL_TrayEntry { + SDL_TrayMenu *parent; + GtkWidget *item; + + /* Checkboxes are "activated" when programmatically checked/unchecked; this + is a workaround. */ + bool ignore_signal; + + SDL_TrayEntryFlags flags; + SDL_TrayCallback callback; + void *userdata; + SDL_TrayMenu *submenu; +}; + +struct SDL_Tray { + AppIndicator *indicator; + SDL_TrayMenu *menu; + char icon_path[256]; +}; + +static void call_callback(GtkMenuItem *item, gpointer ptr) +{ + SDL_TrayEntry *entry = ptr; + + /* Not needed with AppIndicator, may be needed with other frameworks */ + /* if (entry->flags & SDL_TRAYENTRY_CHECKBOX) { + SDL_SetTrayEntryChecked(entry, !SDL_GetTrayEntryChecked(entry)); + } */ + + if (entry->ignore_signal) { + return; + } + + if (entry->callback) { + entry->callback(entry->userdata, entry); + } +} + +/* Since AppIndicator deals only in filenames, which are inherently subject to + timing attacks, don't bother generating a secure filename. */ +static bool get_tmp_filename(char *buffer, size_t size) +{ + static int count = 0; + + if (size < 64) { + return SDL_SetError("Can't create temporary file for icon: size %ld < 64", size); + } + + int would_have_written = SDL_snprintf(buffer, size, "/tmp/sdl_appindicator_icon_%d_%d.bmp", getpid(), count++); + + return would_have_written > 0 && would_have_written < size - 1; +} + +static const char *get_appindicator_id(void) +{ + static int count = 0; + static char buffer[256]; + + int would_have_written = SDL_snprintf(buffer, sizeof(buffer), "sdl-appindicator-%d-%d", getpid(), count++); + + if (would_have_written <= 0 || would_have_written >= sizeof(buffer) - 1) { + SDL_SetError("Couldn't fit %d bytes in buffer of size %ld", would_have_written, sizeof(buffer)); + return NULL; + } + + return buffer; +} + +static void DestroySDLMenu(SDL_TrayMenu *menu) +{ + for (int i = 0; i < menu->nEntries; i++) { + if (menu->entries[i] && menu->entries[i]->submenu) { + DestroySDLMenu(menu->entries[i]->submenu); + } + SDL_free(menu->entries[i]); + } + SDL_free(menu->entries); + SDL_free(menu); +} + +SDL_Tray *SDL_CreateTray(SDL_Surface *icon, const char *tooltip) +{ + if (init_gtk() != true) { + return NULL; + } + + SDL_Tray *tray = (SDL_Tray *) SDL_malloc(sizeof(SDL_Tray)); + + if (!tray) { + return NULL; + } + + SDL_memset((void *) tray, 0, sizeof(*tray)); + + get_tmp_filename(tray->icon_path, sizeof(tray->icon_path)); + SDL_SaveBMP(icon, tray->icon_path); + + tray->indicator = app_indicator_new(get_appindicator_id(), tray->icon_path, + APP_INDICATOR_CATEGORY_APPLICATION_STATUS); + + app_indicator_set_status(tray->indicator, APP_INDICATOR_STATUS_ACTIVE); + + return tray; +} + +void SDL_SetTrayIcon(SDL_Tray *tray, SDL_Surface *icon) +{ + if (*tray->icon_path) { + SDL_RemovePath(tray->icon_path); + } + + /* AppIndicator caches the icon files; always change filename to avoid caching */ + + if (icon) { + get_tmp_filename(tray->icon_path, sizeof(tray->icon_path)); + SDL_SaveBMP(icon, tray->icon_path); + app_indicator_set_icon(tray->indicator, tray->icon_path); + } else { + *tray->icon_path = '\0'; + app_indicator_set_icon(tray->indicator, NULL); + } +} + +void SDL_SetTrayTooltip(SDL_Tray *tray, const char *tooltip) +{ + /* AppIndicator provides no tooltip support. */ +} + +SDL_TrayMenu *SDL_CreateTrayMenu(SDL_Tray *tray) +{ + tray->menu = SDL_malloc(sizeof(SDL_TrayMenu)); + + if (!tray->menu) { + return NULL; + } + + tray->menu->menu = (GtkMenuShell *)gtk_menu_new(); + tray->menu->parent_tray = tray; + tray->menu->parent_entry = NULL; + tray->menu->nEntries = 0; + tray->menu->entries = NULL; + + app_indicator_set_menu(tray->indicator, GTK_MENU(tray->menu->menu)); + + return tray->menu; +} + +SDL_TrayMenu *SDL_GetTrayMenu(SDL_Tray *tray) +{ + return tray->menu; +} + +SDL_TrayMenu *SDL_CreateTraySubmenu(SDL_TrayEntry *entry) +{ + if (entry->submenu) { + SDL_SetError("Tray entry submenu already exists"); + return NULL; + } + + if (!(entry->flags & SDL_TRAYENTRY_SUBMENU)) { + SDL_SetError("Cannot create submenu for entry not created with SDL_TRAYENTRY_SUBMENU"); + return NULL; + } + + entry->submenu = SDL_malloc(sizeof(SDL_TrayMenu)); + + if (!entry->submenu) { + return NULL; + } + + entry->submenu->menu = (GtkMenuShell *)gtk_menu_new(); + entry->submenu->parent_tray = NULL; + entry->submenu->parent_entry = entry; + entry->submenu->nEntries = 0; + entry->submenu->entries = NULL; + + gtk_menu_item_set_submenu(GTK_MENU_ITEM(entry->item), GTK_WIDGET(entry->submenu->menu)); + + return entry->submenu; +} + +SDL_TrayMenu *SDL_GetTraySubmenu(SDL_TrayEntry *entry) +{ + return entry->submenu; +} + +const SDL_TrayEntry **SDL_GetTrayEntries(SDL_TrayMenu *menu, int *size) +{ + if (size) { + *size = menu->nEntries; + } + + return (const SDL_TrayEntry **) menu->entries; +} + +void SDL_RemoveTrayEntry(SDL_TrayEntry *entry) +{ + if (!entry) { + return; + } + + SDL_TrayMenu *menu = entry->parent; + + bool found = false; + for (int i = 0; i < menu->nEntries - 1; i++) { + if (menu->entries[i] == entry) { + found = true; + } + + if (found) { + menu->entries[i] = menu->entries[i + 1]; + } + } + + if (entry->submenu) { + DestroySDLMenu(entry->submenu); + } + + menu->nEntries--; + SDL_TrayEntry ** new_entries = SDL_realloc(menu->entries, menu->nEntries * sizeof(SDL_TrayEntry *)); + + /* Not sure why shrinking would fail, but even if it does, we can live with a "too big" array */ + if (new_entries) { + menu->entries = new_entries; + } + + gtk_widget_destroy(entry->item); + SDL_free(entry); +} + +SDL_TrayEntry *SDL_InsertTrayEntryAt(SDL_TrayMenu *menu, int pos, const char *label, SDL_TrayEntryFlags flags) +{ + if (pos < -1 || pos > (int) menu->nEntries) { + SDL_InvalidParamError("pos"); + return NULL; + } + + if (pos == -1) { + pos = menu->nEntries; + } + + SDL_TrayEntry *entry = SDL_malloc(sizeof(SDL_TrayEntry)); + + if (!entry) { + return NULL; + } + + SDL_memset((void *) entry, 0, sizeof(*entry)); + entry->parent = menu; + entry->item = NULL; + entry->ignore_signal = false; + entry->flags = flags; + entry->callback = NULL; + entry->userdata = NULL; + entry->submenu = NULL; + + if (label == NULL) { + entry->item = gtk_separator_menu_item_new(); + } else if (flags & SDL_TRAYENTRY_CHECKBOX) { + entry->item = gtk_check_menu_item_new_with_label(label); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(entry->item), !!(flags & SDL_TRAYENTRY_CHECKED)); + } else { + entry->item = gtk_menu_item_new_with_label(label); + } + + gtk_widget_set_sensitive(entry->item, !(flags & SDL_TRAYENTRY_DISABLED)); + + SDL_TrayEntry **new_entries = (SDL_TrayEntry **) SDL_realloc(menu->entries, (menu->nEntries + 1) * sizeof(SDL_TrayEntry *)); + + if (!new_entries) { + SDL_free(entry); + return NULL; + } + + menu->entries = new_entries; + menu->nEntries++; + + for (int i = menu->nEntries - 1; i > pos; i--) { + menu->entries[i] = menu->entries[i - 1]; + } + + new_entries[pos] = entry; + + gtk_widget_show(entry->item); + gtk_menu_shell_insert(menu->menu, entry->item, (pos == menu->nEntries) ? -1 : pos); + + g_signal_connect(entry->item, "activate", G_CALLBACK(call_callback), entry); + + return entry; +} + +void SDL_SetTrayEntryLabel(SDL_TrayEntry *entry, const char *label) +{ + gtk_menu_item_set_label(GTK_MENU_ITEM(entry->item), label); +} + +const char *SDL_GetTrayEntryLabel(SDL_TrayEntry *entry) +{ + return gtk_menu_item_get_label(GTK_MENU_ITEM(entry->item)); +} + +void SDL_SetTrayEntryChecked(SDL_TrayEntry *entry, bool checked) +{ + if (!(entry->flags & SDL_TRAYENTRY_CHECKBOX)) { + SDL_SetError("Cannot update check for entry not created with SDL_TRAYENTRY_CHECKBOX"); + return; + } + + entry->ignore_signal = true; + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(entry->item), checked); + entry->ignore_signal = false; +} + +bool SDL_GetTrayEntryChecked(SDL_TrayEntry *entry) +{ + if (!(entry->flags & SDL_TRAYENTRY_CHECKBOX)) { + SDL_SetError("Cannot fetch check for entry not created with SDL_TRAYENTRY_CHECKBOX"); + return false; + } + + return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(entry->item)); +} + +void SDL_SetTrayEntryEnabled(SDL_TrayEntry *entry, bool enabled) +{ + gtk_widget_set_sensitive(entry->item, enabled); +} + +bool SDL_GetTrayEntryEnabled(SDL_TrayEntry *entry) +{ + return gtk_widget_get_sensitive(entry->item); +} + +void SDL_SetTrayEntryCallback(SDL_TrayEntry *entry, SDL_TrayCallback callback, void *userdata) +{ + entry->callback = callback; + entry->userdata = userdata; +} + +SDL_TrayMenu *SDL_GetTrayEntryParent(SDL_TrayEntry *entry) +{ + return entry->parent; +} + +SDL_TrayEntry *SDL_GetTrayMenuParentEntry(SDL_TrayMenu *menu) +{ + return menu->parent_entry; +} + +SDL_Tray *SDL_GetTrayMenuParentTray(SDL_TrayMenu *menu) +{ + return menu->parent_tray; +} + +void SDL_DestroyTray(SDL_Tray *tray) +{ + if (!tray) { + return; + } + + if (tray->menu) { + DestroySDLMenu(tray->menu); + } + + if (*tray->icon_path) { + SDL_RemovePath(tray->icon_path); + } + + SDL_free(tray); +} diff --git a/libs/SDL3/src/tray/windows/SDL_tray.c b/libs/SDL3/src/tray/windows/SDL_tray.c new file mode 100644 index 00000000..e2823a1c --- /dev/null +++ b/libs/SDL3/src/tray/windows/SDL_tray.c @@ -0,0 +1,565 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2024 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#include "SDL_internal.h" + +#include "../../video/windows/SDL_surface_utils.h" + +#include +#include +#include + +#include + +#define WM_TRAYICON (WM_USER + 1) + +struct SDL_TrayMenu { + HMENU hMenu; + + size_t nEntries; + SDL_TrayEntry **entries; + + SDL_Tray *parent_tray; + SDL_TrayEntry *parent_entry; +}; + +struct SDL_TrayEntry { + SDL_TrayMenu *parent; + UINT_PTR id; + + char label_cache[4096]; + SDL_TrayEntryFlags flags; + SDL_TrayCallback callback; + void *userdata; + SDL_TrayMenu *submenu; +}; + +struct SDL_Tray { + NOTIFYICONDATAW nid; + HWND hwnd; + HICON icon; + SDL_TrayMenu *menu; +}; + +static UINT_PTR get_next_id(void) +{ + static UINT_PTR next_id = 0; + return ++next_id; +} + +static SDL_TrayEntry *find_entry_in_menu(SDL_TrayMenu *menu, UINT_PTR id) +{ + for (size_t i = 0; i < menu->nEntries; i++) { + SDL_TrayEntry *entry = menu->entries[i]; + + if (entry->id == id) { + return entry; + } + + if (entry->submenu) { + SDL_TrayEntry *e = find_entry_in_menu(entry->submenu, id); + + if (e) { + return e; + } + } + } + + return NULL; +} + +static SDL_TrayEntry *find_entry_with_id(SDL_Tray *tray, UINT_PTR id) +{ + if (!tray->menu) { + return NULL; + } + + return find_entry_in_menu(tray->menu, id); +} + +LRESULT CALLBACK TrayWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + SDL_Tray *tray = (SDL_Tray *) GetWindowLongPtr(hwnd, GWLP_USERDATA); + SDL_TrayEntry *entry = NULL; + + if (!tray) { + return DefWindowProc(hwnd, uMsg, wParam, lParam); + } + + switch (uMsg) { + case WM_TRAYICON: + if (LOWORD(lParam) == WM_CONTEXTMENU || LOWORD(lParam) == WM_LBUTTONUP) { + SetForegroundWindow(hwnd); + TrackPopupMenu(tray->menu->hMenu, TPM_BOTTOMALIGN | TPM_RIGHTALIGN, GET_X_LPARAM(wParam), GET_Y_LPARAM(wParam), 0, hwnd, NULL); + } + break; + + case WM_COMMAND: + entry = find_entry_with_id(tray, LOWORD(wParam)); + + if (entry && (entry->flags & SDL_TRAYENTRY_CHECKBOX)) { + SDL_SetTrayEntryChecked(entry, !SDL_GetTrayEntryChecked(entry)); + } + + if (entry && entry->callback) { + entry->callback(entry->userdata, entry); + } + break; + + default: + return DefWindowProc(hwnd, uMsg, wParam, lParam); + } + return 0; +} + +static void DestroySDLMenu(SDL_TrayMenu *menu) +{ + for (size_t i = 0; i < menu->nEntries; i++) { + if (menu->entries[i] && menu->entries[i]->submenu) { + DestroySDLMenu(menu->entries[i]->submenu); + } + SDL_free(menu->entries[i]); + } + SDL_free(menu->entries); + DestroyMenu(menu->hMenu); + SDL_free(menu); +} + +static wchar_t *convert_label(const char *in) +{ + const char *c; + char *c2; + int len = 0; + + for (c = in; *c; c++) { + len += (*c == '&') ? 2 : 1; + } + + char *escaped = SDL_malloc(SDL_strlen(in) + len + 1); + + if (!escaped) { + return NULL; + } + + for (c = in, c2 = escaped; *c;) { + if (*c == '&') { + *c2++ = *c; + } + + *c2++ = *c++; + } + + *c2 = '\0'; + + int len_w = MultiByteToWideChar(CP_UTF8, 0, escaped, len + 1, NULL, 0); + wchar_t *out = (wchar_t *)SDL_malloc(len_w * sizeof(wchar_t)); + + if (!out) { + SDL_free(escaped); + return NULL; + } + + MultiByteToWideChar(CP_UTF8, 0, escaped, -1, out, len_w); + + SDL_free(escaped); + + return out; +} + +SDL_Tray *SDL_CreateTray(SDL_Surface *icon, const char *tooltip) +{ + SDL_Tray *tray = SDL_malloc(sizeof(SDL_Tray)); + + if (!tray) { + return NULL; + } + + tray->menu = NULL; + tray->hwnd = CreateWindowEx(0, TEXT("Message"), NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL); + SetWindowLongPtr(tray->hwnd, GWLP_WNDPROC, (LONG_PTR) TrayWindowProc); + + ZeroMemory(&tray->nid, sizeof(NOTIFYICONDATAW)); + tray->nid.cbSize = sizeof(NOTIFYICONDATAW); + tray->nid.hWnd = tray->hwnd; + tray->nid.uID = (UINT) get_next_id(); + tray->nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP | NIF_SHOWTIP; + tray->nid.uCallbackMessage = WM_TRAYICON; + tray->nid.uVersion = NOTIFYICON_VERSION_4; + mbstowcs_s(NULL, tray->nid.szTip, sizeof(tray->nid.szTip) / sizeof(*tray->nid.szTip), tooltip, _TRUNCATE); + + if (icon) { + tray->nid.hIcon = CreateIconFromSurface(icon); + + if (!tray->nid.hIcon) { + tray->nid.hIcon = LoadIcon(NULL, IDI_APPLICATION); + } + + tray->icon = tray->nid.hIcon; + } else { + tray->nid.hIcon = LoadIcon(NULL, IDI_APPLICATION); + tray->icon = tray->nid.hIcon; + } + + Shell_NotifyIconW(NIM_ADD, &tray->nid); + Shell_NotifyIconW(NIM_SETVERSION, &tray->nid); + + SetWindowLongPtr(tray->hwnd, GWLP_USERDATA, (LONG_PTR) tray); + + return tray; +} + +void SDL_SetTrayIcon(SDL_Tray *tray, SDL_Surface *icon) +{ + if (tray->icon) { + DestroyIcon(tray->icon); + } + + if (icon) { + tray->nid.hIcon = CreateIconFromSurface(icon); + + if (!tray->nid.hIcon) { + tray->nid.hIcon = LoadIcon(NULL, IDI_APPLICATION); + } + + tray->icon = tray->nid.hIcon; + } else { + tray->nid.hIcon = LoadIcon(NULL, IDI_APPLICATION); + tray->icon = tray->nid.hIcon; + } + + Shell_NotifyIconW(NIM_MODIFY, &tray->nid); +} + +void SDL_SetTrayTooltip(SDL_Tray *tray, const char *tooltip) +{ + if (tooltip) { + mbstowcs_s(NULL, tray->nid.szTip, sizeof(tray->nid.szTip) / sizeof(*tray->nid.szTip), tooltip, _TRUNCATE); + } else { + tray->nid.szTip[0] = '\0'; + } + + Shell_NotifyIconW(NIM_MODIFY, &tray->nid); +} + +SDL_TrayMenu *SDL_CreateTrayMenu(SDL_Tray *tray) +{ + tray->menu = SDL_malloc(sizeof(SDL_TrayMenu)); + + if (!tray->menu) { + return NULL; + } + + SDL_memset((void *) tray->menu, 0, sizeof(*tray->menu)); + + tray->menu->hMenu = CreatePopupMenu(); + tray->menu->parent_tray = tray; + + return tray->menu; +} + +SDL_TrayMenu *SDL_GetTrayMenu(SDL_Tray *tray) +{ + return tray->menu; +} + +SDL_TrayMenu *SDL_CreateTraySubmenu(SDL_TrayEntry *entry) +{ + if (!entry->submenu) { + SDL_SetError("Cannot create submenu for entry not created with SDL_TRAYENTRY_SUBMENU"); + } + + return entry->submenu; +} + +SDL_TrayMenu *SDL_GetTraySubmenu(SDL_TrayEntry *entry) +{ + return entry->submenu; +} + +const SDL_TrayEntry **SDL_GetTrayEntries(SDL_TrayMenu *menu, int *size) +{ + if (size) { + *size = (int) menu->nEntries; + } + + return (const SDL_TrayEntry **) menu->entries; +} + +void SDL_RemoveTrayEntry(SDL_TrayEntry *entry) +{ + if (!entry) { + return; + } + + SDL_TrayMenu *menu = entry->parent; + + bool found = false; + for (size_t i = 0; i < menu->nEntries - 1; i++) { + if (menu->entries[i] == entry) { + found = true; + } + + if (found) { + menu->entries[i] = menu->entries[i + 1]; + } + } + + if (entry->submenu) { + DestroySDLMenu(entry->submenu); + } + + menu->nEntries--; + SDL_TrayEntry ** new_entries = SDL_realloc(menu->entries, menu->nEntries * sizeof(SDL_TrayEntry *)); + + /* Not sure why shrinking would fail, but even if it does, we can live with a "too big" array */ + if (new_entries) { + menu->entries = new_entries; + } + + if (!DeleteMenu(menu->hMenu, (UINT) entry->id, MF_BYCOMMAND)) { + /* This is somewhat useless since we don't return anything, but might help with eventual bugs */ + SDL_SetError("Couldn't destroy tray entry"); + } + + SDL_free(entry); +} + +SDL_TrayEntry *SDL_InsertTrayEntryAt(SDL_TrayMenu *menu, int pos, const char *label, SDL_TrayEntryFlags flags) +{ + if (pos < -1 || pos > (int) menu->nEntries) { + SDL_InvalidParamError("pos"); + return NULL; + } + + int windows_compatible_pos = pos; + + if (pos == -1) { + pos = (int) menu->nEntries; + } else if (pos == menu->nEntries) { + windows_compatible_pos = -1; + } + + SDL_TrayEntry *entry = SDL_malloc(sizeof(SDL_TrayEntry)); + + if (!entry) { + return NULL; + } + + wchar_t *label_w = NULL; + + if (label && !(label_w = convert_label(label))) { + SDL_free(entry); + return NULL; + } + + entry->parent = menu; + entry->flags = flags; + entry->callback = NULL; + entry->userdata = NULL; + entry->submenu = NULL; + SDL_snprintf(entry->label_cache, sizeof(entry->label_cache), "%s", label ? label : ""); + + if (label != NULL && flags & SDL_TRAYENTRY_SUBMENU) { + entry->submenu = SDL_malloc(sizeof(SDL_TrayMenu)); + + if (!entry->submenu) { + SDL_free(entry); + SDL_free(label_w); + return NULL; + } + + entry->submenu->hMenu = CreatePopupMenu(); + entry->submenu->nEntries = 0; + entry->submenu->entries = NULL; + + entry->id = (UINT_PTR) entry->submenu->hMenu; + } else { + entry->id = get_next_id(); + } + + SDL_TrayEntry **new_entries = (SDL_TrayEntry **) SDL_realloc(menu->entries, (menu->nEntries + 1) * sizeof(SDL_TrayEntry **)); + + if (!new_entries) { + SDL_free(entry); + SDL_free(label_w); + if (entry->submenu) { + DestroyMenu(entry->submenu->hMenu); + SDL_free(entry->submenu); + } + return NULL; + } + + menu->entries = new_entries; + menu->nEntries++; + + for (int i = (int) menu->nEntries - 1; i > pos; i--) { + menu->entries[i] = menu->entries[i - 1]; + } + + new_entries[pos] = entry; + + if (label == NULL) { + InsertMenuW(menu->hMenu, windows_compatible_pos, MF_SEPARATOR | MF_BYPOSITION, entry->id, NULL); + } else { + UINT mf = MF_STRING | MF_BYPOSITION; + if (flags & SDL_TRAYENTRY_SUBMENU) { + mf = MF_POPUP; + } + + if (flags & SDL_TRAYENTRY_DISABLED) { + mf |= MF_DISABLED | MF_GRAYED; + } + + if (flags & SDL_TRAYENTRY_CHECKED) { + mf |= MF_CHECKED; + } + + InsertMenuW(menu->hMenu, windows_compatible_pos, mf, entry->id, label_w); + + SDL_free(label_w); + } + + return entry; +} + +void SDL_SetTrayEntryLabel(SDL_TrayEntry *entry, const char *label) +{ + SDL_snprintf(entry->label_cache, sizeof(entry->label_cache), "%s", label); + + wchar_t *label_w = convert_label(label); + + if (!label_w) { + return; + } + + MENUITEMINFOW mii; + mii.cbSize = sizeof(MENUITEMINFOW); + mii.fMask = MIIM_STRING; + + mii.dwTypeData = label_w; + mii.cch = (UINT) wcslen(label_w); + + if (!SetMenuItemInfoW(entry->parent->hMenu, (UINT) entry->id, TRUE, &mii)) { + SDL_SetError("Couldn't update tray entry label"); + } + + SDL_free(label_w); +} + +const char *SDL_GetTrayEntryLabel(SDL_TrayEntry *entry) +{ + return entry->label_cache; +} + +void SDL_SetTrayEntryChecked(SDL_TrayEntry *entry, bool checked) +{ + if (!(entry->flags & SDL_TRAYENTRY_CHECKBOX)) { + SDL_SetError("Can't check/uncheck tray entry not created with SDL_TRAYENTRY_CHECKBOX"); + return; + } + + CheckMenuItem(entry->parent->hMenu, (UINT) entry->id, checked ? MF_CHECKED : MF_UNCHECKED); +} + +bool SDL_GetTrayEntryChecked(SDL_TrayEntry *entry) +{ + if (!(entry->flags & SDL_TRAYENTRY_CHECKBOX)) { + SDL_SetError("Can't get check status of tray entry not created with SDL_TRAYENTRY_CHECKBOX"); + return false; + } + + MENUITEMINFOW mii; + mii.cbSize = sizeof(MENUITEMINFOW); + mii.fMask = MIIM_STATE; + + GetMenuItemInfoW(entry->parent->hMenu, (UINT) entry->id, FALSE, &mii); + + return !!(mii.fState & MFS_CHECKED); +} + +void SDL_SetTrayEntryEnabled(SDL_TrayEntry *entry, bool enabled) +{ + if (!(entry->flags & SDL_TRAYENTRY_CHECKBOX)) { + SDL_SetError("Cannot update check for entry not created with SDL_TRAYENTRY_CHECKBOX"); + return; + } + + EnableMenuItem(entry->parent->hMenu, (UINT) entry->id, MF_BYCOMMAND | (enabled ? MF_ENABLED : (MF_DISABLED | MF_GRAYED))); +} + +bool SDL_GetTrayEntryEnabled(SDL_TrayEntry *entry) +{ + if (!(entry->flags & SDL_TRAYENTRY_CHECKBOX)) { + SDL_SetError("Cannot fetch check for entry not created with SDL_TRAYENTRY_CHECKBOX"); + return false; + } + + MENUITEMINFOW mii; + mii.cbSize = sizeof(MENUITEMINFOW); + mii.fMask = MIIM_STATE; + + GetMenuItemInfoW(entry->parent->hMenu, (UINT) entry->id, FALSE, &mii); + + return !!(mii.fState & MFS_ENABLED); +} + +void SDL_SetTrayEntryCallback(SDL_TrayEntry *entry, SDL_TrayCallback callback, void *userdata) +{ + entry->callback = callback; + entry->userdata = userdata; +} + +SDL_TrayMenu *SDL_GetTrayEntryParent(SDL_TrayEntry *entry) +{ + return entry->parent; +} + +SDL_TrayEntry *SDL_GetTrayMenuParentEntry(SDL_TrayMenu *menu) +{ + return menu->parent_entry; +} + +SDL_Tray *SDL_GetTrayMenuParentTray(SDL_TrayMenu *menu) +{ + return menu->parent_tray; +} + +void SDL_DestroyTray(SDL_Tray *tray) +{ + if (!tray) { + return; + } + + Shell_NotifyIconW(NIM_DELETE, &tray->nid); + + if (tray->menu) { + DestroySDLMenu(tray->menu); + } + + if (tray->icon) { + DestroyIcon(tray->icon); + } + + if (tray->hwnd) { + DestroyWindow(tray->hwnd); + } + + SDL_free(tray); +} diff --git a/libs/SDL3/src/video/SDL_RLEaccel.c b/libs/SDL3/src/video/SDL_RLEaccel.c index 0defb010..665bf985 100644 --- a/libs/SDL3/src/video/SDL_RLEaccel.c +++ b/libs/SDL3/src/video/SDL_RLEaccel.c @@ -20,7 +20,7 @@ */ #include "SDL_internal.h" -#if SDL_HAVE_RLE +#ifdef SDL_HAVE_RLE /* * RLE encoding for software colorkey and alpha-channel acceleration diff --git a/libs/SDL3/src/video/SDL_blit.c b/libs/SDL3/src/video/SDL_blit.c index aafc4398..805f914a 100644 --- a/libs/SDL3/src/video/SDL_blit.c +++ b/libs/SDL3/src/video/SDL_blit.c @@ -97,7 +97,7 @@ static bool SDLCALL SDL_SoftBlit(SDL_Surface *src, const SDL_Rect *srcrect, return okay; } -#if SDL_HAVE_BLIT_AUTO +#ifdef SDL_HAVE_BLIT_AUTO #ifdef SDL_PLATFORM_MACOS #include @@ -189,7 +189,7 @@ bool SDL_CalculateBlit(SDL_Surface *surface, SDL_Surface *dst) return SDL_SetError("Blit combination not supported"); } -#if SDL_HAVE_RLE +#ifdef SDL_HAVE_RLE // Clean everything out to start if (surface->flags & SDL_INTERNAL_SURFACE_RLEACCEL) { SDL_UnRLESurface(surface, true); @@ -204,7 +204,7 @@ bool SDL_CalculateBlit(SDL_Surface *surface, SDL_Surface *dst) map->info.dst_fmt = dst->fmt; map->info.dst_pal = dst->palette; -#if SDL_HAVE_RLE +#ifdef SDL_HAVE_RLE // See if we can do RLE acceleration if (map->info.flags & SDL_COPY_RLE_DESIRED) { if (SDL_RLESurface(surface)) { @@ -228,30 +228,30 @@ bool SDL_CalculateBlit(SDL_Surface *surface, SDL_Surface *dst) SDL_ISPIXELFORMAT_10BIT(dst->format)) { blit = SDL_Blit_Slow; } -#if SDL_HAVE_BLIT_0 +#ifdef SDL_HAVE_BLIT_0 else if (SDL_BITSPERPIXEL(surface->format) < 8 && SDL_ISPIXELFORMAT_INDEXED(surface->format)) { blit = SDL_CalculateBlit0(surface); } #endif -#if SDL_HAVE_BLIT_1 +#ifdef SDL_HAVE_BLIT_1 else if (SDL_BYTESPERPIXEL(surface->format) == 1 && SDL_ISPIXELFORMAT_INDEXED(surface->format)) { blit = SDL_CalculateBlit1(surface); } #endif -#if SDL_HAVE_BLIT_A +#ifdef SDL_HAVE_BLIT_A else if (map->info.flags & SDL_COPY_BLEND) { blit = SDL_CalculateBlitA(surface); } #endif -#if SDL_HAVE_BLIT_N +#ifdef SDL_HAVE_BLIT_N else { blit = SDL_CalculateBlitN(surface); } #endif } -#if SDL_HAVE_BLIT_AUTO +#ifdef SDL_HAVE_BLIT_AUTO if (!blit) { SDL_PixelFormat src_format = surface->format; SDL_PixelFormat dst_format = dst->format; diff --git a/libs/SDL3/src/video/SDL_blit_0.c b/libs/SDL3/src/video/SDL_blit_0.c index 0ae78669..6bbefb98 100644 --- a/libs/SDL3/src/video/SDL_blit_0.c +++ b/libs/SDL3/src/video/SDL_blit_0.c @@ -20,7 +20,7 @@ */ #include "SDL_internal.h" -#if SDL_HAVE_BLIT_0 +#ifdef SDL_HAVE_BLIT_0 #include "SDL_surface_c.h" diff --git a/libs/SDL3/src/video/SDL_blit_1.c b/libs/SDL3/src/video/SDL_blit_1.c index 1cc0a8c0..26dec82f 100644 --- a/libs/SDL3/src/video/SDL_blit_1.c +++ b/libs/SDL3/src/video/SDL_blit_1.c @@ -20,7 +20,7 @@ */ #include "SDL_internal.h" -#if SDL_HAVE_BLIT_1 +#ifdef SDL_HAVE_BLIT_1 #include "SDL_surface_c.h" #include "SDL_sysvideo.h" diff --git a/libs/SDL3/src/video/SDL_blit_A.c b/libs/SDL3/src/video/SDL_blit_A.c index 403c482e..a24aa85c 100644 --- a/libs/SDL3/src/video/SDL_blit_A.c +++ b/libs/SDL3/src/video/SDL_blit_A.c @@ -20,7 +20,7 @@ */ #include "SDL_internal.h" -#if SDL_HAVE_BLIT_A +#ifdef SDL_HAVE_BLIT_A #include "SDL_surface_c.h" diff --git a/libs/SDL3/src/video/SDL_blit_N.c b/libs/SDL3/src/video/SDL_blit_N.c index bd4e69e5..54341e93 100644 --- a/libs/SDL3/src/video/SDL_blit_N.c +++ b/libs/SDL3/src/video/SDL_blit_N.c @@ -20,7 +20,7 @@ */ #include "SDL_internal.h" -#if SDL_HAVE_BLIT_N +#ifdef SDL_HAVE_BLIT_N #include "SDL_surface_c.h" #include "SDL_blit_copy.h" @@ -1162,7 +1162,7 @@ static void Blit_XRGB8888_RGB565(SDL_BlitInfo *info) #endif // USE_DUFFS_LOOP } -#if SDL_HAVE_BLIT_N_RGB565 +#ifdef SDL_HAVE_BLIT_N_RGB565 // Special optimized blit for RGB 5-6-5 --> 32-bit RGB surfaces #define RGB565_32(dst, src, map) (map[src[LO] * 2] + map[src[HI] * 2 + 1]) @@ -2753,7 +2753,7 @@ static const struct blit_table normal_blit_2[] = { { 0x00007C00, 0x000003E0, 0x0000001F, 4, 0x00000000, 0x00000000, 0x00000000, BLIT_FEATURE_HAS_ALTIVEC, Blit_RGB555_32Altivec, NO_ALPHA | COPY_ALPHA | SET_ALPHA }, #endif -#if SDL_HAVE_BLIT_N_RGB565 +#ifdef SDL_HAVE_BLIT_N_RGB565 { 0x0000F800, 0x000007E0, 0x0000001F, 4, 0x00FF0000, 0x0000FF00, 0x000000FF, 0, Blit_RGB565_ARGB8888, NO_ALPHA | COPY_ALPHA | SET_ALPHA }, { 0x0000F800, 0x000007E0, 0x0000001F, 4, 0x000000FF, 0x0000FF00, 0x00FF0000, diff --git a/libs/SDL3/src/video/SDL_blit_auto.c b/libs/SDL3/src/video/SDL_blit_auto.c index b695ecc9..8e08991d 100644 --- a/libs/SDL3/src/video/SDL_blit_auto.c +++ b/libs/SDL3/src/video/SDL_blit_auto.c @@ -21,7 +21,7 @@ */ #include "SDL_internal.h" -#if SDL_HAVE_BLIT_AUTO +#ifdef SDL_HAVE_BLIT_AUTO /* *INDENT-OFF* */ // clang-format off diff --git a/libs/SDL3/src/video/SDL_blit_auto.h b/libs/SDL3/src/video/SDL_blit_auto.h index 9d6a48fa..c1011e6b 100644 --- a/libs/SDL3/src/video/SDL_blit_auto.h +++ b/libs/SDL3/src/video/SDL_blit_auto.h @@ -22,7 +22,7 @@ #include "SDL_internal.h" #include "SDL_blit.h" -#if SDL_HAVE_BLIT_AUTO +#ifdef SDL_HAVE_BLIT_AUTO /* *INDENT-OFF* */ // clang-format off diff --git a/libs/SDL3/src/video/SDL_bmp.c b/libs/SDL3/src/video/SDL_bmp.c index ebead13b..b3df4247 100644 --- a/libs/SDL3/src/video/SDL_bmp.c +++ b/libs/SDL3/src/video/SDL_bmp.c @@ -664,17 +664,8 @@ bool SDL_SaveBMP_IO(SDL_Surface *surface, SDL_IOStream *dst, bool closeio) SDL_BITSPERPIXEL(surface->format)); goto done; } - } else if ((SDL_BITSPERPIXEL(surface->format) == 24) && !save32bit && -#if SDL_BYTEORDER == SDL_LIL_ENDIAN - (surface->fmt->Rmask == 0x00FF0000) && - (surface->fmt->Gmask == 0x0000FF00) && - (surface->fmt->Bmask == 0x000000FF) -#else - (surface->fmt->Rmask == 0x000000FF) && - (surface->fmt->Gmask == 0x0000FF00) && - (surface->fmt->Bmask == 0x00FF0000) -#endif - ) { + } else if ((surface->format == SDL_PIXELFORMAT_BGR24 && !save32bit) || + (surface->format == SDL_PIXELFORMAT_BGRA32 && save32bit)) { intermediate_surface = surface; } else { SDL_PixelFormat pixel_format; diff --git a/libs/SDL3/src/video/SDL_fillrect.c b/libs/SDL3/src/video/SDL_fillrect.c index f8879e99..f8b0ce2b 100644 --- a/libs/SDL3/src/video/SDL_fillrect.c +++ b/libs/SDL3/src/video/SDL_fillrect.c @@ -59,6 +59,13 @@ static void SDL_TARGETING("sse") SDL_FillSurfaceRect##bpp##SSE(Uint8 *pixels, in { \ int i, n; \ Uint8 *p = NULL; \ + \ + /* If the number of bytes per row is equal to the pitch, treat */ \ + /* all rows as one long continuous row (for better performance) */ \ + if ((w) * (bpp) == pitch) { \ + w = w * h; \ + h = 1; \ + } \ \ SSE_BEGIN; \ \ diff --git a/libs/SDL3/src/video/SDL_pixels.c b/libs/SDL3/src/video/SDL_pixels.c index 6f3adf64..2859b7c5 100644 --- a/libs/SDL3/src/video/SDL_pixels.c +++ b/libs/SDL3/src/video/SDL_pixels.c @@ -166,7 +166,7 @@ bool SDL_GetMasksForPixelFormat(SDL_PixelFormat format, int *bpp, Uint32 *Rmask, { Uint32 masks[4]; -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV // Partial support for SDL_Surface with FOURCC if (SDL_ISPIXELFORMAT_FOURCC(format)) { // Not a format that uses masks @@ -572,8 +572,8 @@ SDL_PixelFormat SDL_GetPixelFormatForMasks(int bpp, Uint32 Rmask, Uint32 Gmask, return SDL_PIXELFORMAT_UNKNOWN; } +static SDL_InitState SDL_format_details_init; static SDL_HashTable *SDL_format_details; -static SDL_Mutex *SDL_format_details_lock; static bool SDL_InitPixelFormatDetails(SDL_PixelFormatDetails *details, SDL_PixelFormat format) { @@ -646,53 +646,44 @@ const SDL_PixelFormatDetails *SDL_GetPixelFormatDetails(SDL_PixelFormat format) { SDL_PixelFormatDetails *details; - if (!SDL_format_details_lock) { - SDL_format_details_lock = SDL_CreateMutex(); - } - - SDL_LockMutex(SDL_format_details_lock); - - if (!SDL_format_details) { - SDL_format_details = SDL_CreateHashTable(NULL, 8, SDL_HashID, SDL_KeyMatchID, SDL_NukeFreeValue, false); + if (SDL_ShouldInit(&SDL_format_details_init)) { + SDL_format_details = SDL_CreateHashTable(NULL, 8, SDL_HashID, SDL_KeyMatchID, SDL_NukeFreeValue, true, false); + if (!SDL_format_details) { + SDL_SetInitialized(&SDL_format_details_init, false); + return NULL; + } + SDL_SetInitialized(&SDL_format_details_init, true); } if (SDL_FindInHashTable(SDL_format_details, (const void *)(uintptr_t)format, (const void **)&details)) { - goto done; + return details; } // Allocate an empty pixel format structure, and initialize it details = (SDL_PixelFormatDetails *)SDL_malloc(sizeof(*details)); if (!details) { - goto done; + return NULL; } if (!SDL_InitPixelFormatDetails(details, format)) { SDL_free(details); - details = NULL; - goto done; + return NULL; } if (!SDL_InsertIntoHashTable(SDL_format_details, (const void *)(uintptr_t)format, (void *)details)) { SDL_free(details); - details = NULL; - goto done; + return NULL; } -done: - SDL_UnlockMutex(SDL_format_details_lock); - return details; } void SDL_QuitPixelFormatDetails(void) { - if (SDL_format_details) { + if (SDL_ShouldQuit(&SDL_format_details_init)) { SDL_DestroyHashTable(SDL_format_details); SDL_format_details = NULL; - } - if (SDL_format_details_lock) { - SDL_DestroyMutex(SDL_format_details_lock); - SDL_format_details_lock = NULL; + SDL_SetInitialized(&SDL_format_details_init, false); } } @@ -1464,7 +1455,7 @@ bool SDL_MapSurface(SDL_Surface *src, SDL_Surface *dst) // Clear out any previous mapping map = &src->map; -#if SDL_HAVE_RLE +#ifdef SDL_HAVE_RLE if (src->internal_flags & SDL_INTERNAL_SURFACE_RLEACCEL) { SDL_UnRLESurface(src, true); } @@ -1505,7 +1496,7 @@ bool SDL_MapSurface(SDL_Surface *src, SDL_Surface *dst) } else { if (SDL_ISPIXELFORMAT_INDEXED(dstfmt->format)) { // BitField --> Palette - map->info.palette_map = SDL_CreateHashTable(NULL, 32, SDL_HashID, SDL_KeyMatchID, NULL, false); + map->info.palette_map = SDL_CreateHashTable(NULL, 32, SDL_HashID, SDL_KeyMatchID, NULL, false, false); } else { // BitField --> BitField if (srcfmt == dstfmt) { diff --git a/libs/SDL3/src/video/SDL_surface.c b/libs/SDL3/src/video/SDL_surface.c index 657d7f46..f6e4f5e2 100644 --- a/libs/SDL3/src/video/SDL_surface.c +++ b/libs/SDL3/src/video/SDL_surface.c @@ -1254,7 +1254,8 @@ bool SDL_BlitSurfaceUncheckedScaled(SDL_Surface *src, const SDL_Rect *srcrect, S if (scaleMode == SDL_SCALEMODE_NEAREST) { if (!(src->map.info.flags & complex_copy_flags) && src->format == dst->format && - !SDL_ISPIXELFORMAT_INDEXED(src->format)) { + !SDL_ISPIXELFORMAT_INDEXED(src->format) && + SDL_BYTESPERPIXEL(src->format) <= 4) { return SDL_SoftStretch(src, srcrect, dst, dstrect, SDL_SCALEMODE_NEAREST); } else if (SDL_BITSPERPIXEL(src->format) < 8) { // Scaling bitmap not yet supported, convert to RGBA for blit @@ -1722,7 +1723,7 @@ bool SDL_LockSurface(SDL_Surface *surface) } if (!surface->locked) { -#if SDL_HAVE_RLE +#ifdef SDL_HAVE_RLE // Perform the lock if (surface->internal_flags & SDL_INTERNAL_SURFACE_RLEACCEL) { SDL_UnRLESurface(surface, true); @@ -1753,7 +1754,7 @@ void SDL_UnlockSurface(SDL_Surface *surface) return; } -#if SDL_HAVE_RLE +#ifdef SDL_HAVE_RLE // Update RLE encoded surface with new data if (surface->internal_flags & SDL_INTERNAL_SURFACE_RLEACCEL) { surface->internal_flags &= ~SDL_INTERNAL_SURFACE_RLEACCEL; // stop lying @@ -2276,7 +2277,7 @@ bool SDL_ConvertPixelsAndColorspace(int width, int height, dst_colorspace = SDL_GetDefaultColorspaceForFormat(dst_format); } -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV if (SDL_ISPIXELFORMAT_FOURCC(src_format) && SDL_ISPIXELFORMAT_FOURCC(dst_format)) { return SDL_ConvertPixels_YUV_to_YUV(width, height, src_format, src_colorspace, src_properties, src, src_pitch, dst_format, dst_colorspace, dst_properties, dst, dst_pitch); } else if (SDL_ISPIXELFORMAT_FOURCC(src_format)) { @@ -2597,6 +2598,7 @@ bool SDL_ClearSurface(SDL_Surface *surface, float r, float g, float b, float a) goto done; } SDL_SetSurfaceColorspace(tmp, surface->colorspace); + SDL_SetSurfaceBlendMode(tmp, SDL_BLENDMODE_NONE); float *pixels = (float *)tmp->pixels; pixels[0] = r; @@ -2959,7 +2961,7 @@ void SDL_DestroySurface(SDL_Surface *surface) while (surface->locked > 0) { SDL_UnlockSurface(surface); } -#if SDL_HAVE_RLE +#ifdef SDL_HAVE_RLE if (surface->internal_flags & SDL_INTERNAL_SURFACE_RLEACCEL) { SDL_UnRLESurface(surface, false); } diff --git a/libs/SDL3/src/video/SDL_sysvideo.h b/libs/SDL3/src/video/SDL_sysvideo.h index 12c4213d..2633f5e0 100644 --- a/libs/SDL3/src/video/SDL_sysvideo.h +++ b/libs/SDL3/src/video/SDL_sysvideo.h @@ -78,6 +78,9 @@ struct SDL_Window */ SDL_Rect floating; + // The last client requested size and position for the window. + SDL_Rect pending; + /* Toggle for drivers to indicate that the current window state is tiled, * and sizes set non-programmatically shouldn't be cached. */ @@ -96,9 +99,10 @@ struct SDL_Window SDL_Surface *surface; bool surface_valid; - bool is_repositioning; // Set during an SDL_SetWindowPosition() call. bool is_hiding; 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 is_destroying; bool is_dropping; // drag/drop in progress, expecting SDL_SendDropComplete(). @@ -352,6 +356,7 @@ struct SDL_VideoDevice bool (*HasScreenKeyboardSupport)(SDL_VideoDevice *_this); void (*ShowScreenKeyboard)(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props); void (*HideScreenKeyboard)(SDL_VideoDevice *_this, SDL_Window *window); + void (*SetTextInputProperties)(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props); bool (*IsScreenKeyboardShown)(SDL_VideoDevice *_this, SDL_Window *window); // Clipboard @@ -520,7 +525,6 @@ extern VideoBootStrap Wayland_bootstrap; extern VideoBootStrap VIVANTE_bootstrap; extern VideoBootStrap Emscripten_bootstrap; extern VideoBootStrap OFFSCREEN_bootstrap; -extern VideoBootStrap NGAGE_bootstrap; extern VideoBootStrap QNX_bootstrap; extern VideoBootStrap OPENVR_bootstrap; @@ -564,6 +568,7 @@ extern void SDL_OnWindowMoved(SDL_Window *window); extern void SDL_OnWindowResized(SDL_Window *window); extern void SDL_CheckWindowPixelSizeChanged(SDL_Window *window); extern void SDL_OnWindowPixelSizeChanged(SDL_Window *window); +extern void SDL_OnWindowLiveResizeUpdate(SDL_Window *window); extern void SDL_OnWindowMinimized(SDL_Window *window); extern void SDL_OnWindowMaximized(SDL_Window *window); extern void SDL_OnWindowRestored(SDL_Window *window); diff --git a/libs/SDL3/src/video/SDL_video.c b/libs/SDL3/src/video/SDL_video.c index f7ddb304..1a0cb998 100644 --- a/libs/SDL3/src/video/SDL_video.c +++ b/libs/SDL3/src/video/SDL_video.c @@ -35,6 +35,7 @@ #include "../timer/SDL_timer_c.h" #include "../camera/SDL_camera_c.h" #include "../render/SDL_sysrender.h" +#include "../main/SDL_main_callbacks.h" #ifdef SDL_VIDEO_OPENGL #include @@ -128,9 +129,6 @@ static VideoBootStrap *bootstrap[] = { #ifdef SDL_VIDEO_DRIVER_QNX &QNX_bootstrap, #endif -#ifdef SDL_VIDEO_DRIVER_NGAGE - &NGAGE_bootstrap, -#endif #ifdef SDL_VIDEO_DRIVER_OFFSCREEN &OFFSCREEN_bootstrap, #endif @@ -1319,14 +1317,16 @@ SDL_DisplayMode **SDL_GetFullscreenDisplayModes(SDL_DisplayID displayID, int *co bool SDL_GetClosestFullscreenDisplayMode(SDL_DisplayID displayID, int w, int h, float refresh_rate, bool include_high_density_modes, SDL_DisplayMode *result) { + if (!result) { + return SDL_InvalidParamError("closest"); // Parameter `result` is called `closest` in the header. + } + const SDL_DisplayMode *mode, *closest = NULL; float aspect_ratio; int i; SDL_VideoDisplay *display = SDL_GetVideoDisplay(displayID); - if (result) { - SDL_zerop(result); - } + SDL_zerop(result); CHECK_DISPLAY_MAGIC(display, false); @@ -1378,9 +1378,9 @@ bool SDL_GetClosestFullscreenDisplayMode(SDL_DisplayID displayID, int w, int h, if (!closest) { return SDL_SetError("Couldn't find any matching video modes"); } - if (result) { - SDL_copyp(result, closest); - } + + SDL_copyp(result, closest); + return true; } @@ -1594,11 +1594,21 @@ void SDL_GlobalToRelativeForWindow(SDL_Window *window, int abs_x, int abs_y, int SDL_DisplayID SDL_GetDisplayForPoint(const SDL_Point *point) { + if (!point) { + SDL_InvalidParamError("point"); + return 0; + } + return GetDisplayForRect(point->x, point->y, 1, 1); } SDL_DisplayID SDL_GetDisplayForRect(const SDL_Rect *rect) { + if (!rect) { + SDL_InvalidParamError("rect"); + return 0; + } + return GetDisplayForRect(rect->x, rect->y, rect->w, rect->h); } @@ -1648,20 +1658,20 @@ SDL_VideoDisplay *SDL_GetVideoDisplayForFullscreenWindow(SDL_Window *window) displayID = window->current_fullscreen_mode.displayID; } - /* The floating position is used here as a very common pattern is - * SDL_SetWindowPosition() followed by SDL_SetWindowFullscreen() to make the - * window fullscreen desktop on a specific display. If the backend doesn't - * support changing the window position, or the compositor hasn't yet actually - * moved the window, the current position won't be updated at the time of the - * fullscreen call. + /* This is used to handle the very common pattern of SDL_SetWindowPosition() + * followed immediately by SDL_SetWindowFullscreen() to make the window fullscreen + * desktop on a specific display. If the backend doesn't support changing the + * window position, or an async window manager hasn't yet actually moved the window, + * the current position won't be updated at the time of the fullscreen call. */ if (!displayID) { - if (window->flags & SDL_WINDOW_FULLSCREEN && !window->is_repositioning) { - // This was a window manager initiated move, use the current position. - displayID = GetDisplayForRect(window->x, window->y, 1, 1); - } else { - displayID = GetDisplayForRect(window->floating.x, window->floating.y, window->floating.w, window->floating.h); - } + // Use the pending position and dimensions, if available, otherwise, use the current. + const int x = window->last_position_pending ? window->pending.x : window->x; + const int y = window->last_position_pending ? window->pending.y : window->y; + const int w = window->last_size_pending ? window->pending.w : window->w; + const int h = window->last_size_pending ? window->pending.h : window->h; + + displayID = GetDisplayForRect(x, y, w, h); } if (!displayID) { // Use the primary display for a window if we can't find it anywhere else @@ -2325,7 +2335,7 @@ SDL_Window *SDL_CreateWindowWithProperties(SDL_PropertiesID props) SDL_GetDisplayUsableBounds(displayID, &bounds); if (w > bounds.w || h > bounds.h) { // This window is larger than the usable bounds, just center on the display - SDL_GetDisplayUsableBounds(displayID, &bounds); + SDL_GetDisplayBounds(displayID, &bounds); } if (SDL_WINDOWPOS_ISCENTERED(x) || SDL_WINDOWPOS_ISUNDEFINED(x)) { if (SDL_WINDOWPOS_ISUNDEFINED(x)) { @@ -2394,12 +2404,14 @@ SDL_Window *SDL_CreateWindowWithProperties(SDL_PropertiesID props) window->undefined_y = undefined_y; SDL_VideoDisplay *display = SDL_GetVideoDisplayForWindow(window); - SDL_SetWindowHDRProperties(window, &display->HDR, false); + if (display) { + SDL_SetWindowHDRProperties(window, &display->HDR, false); + } if (flags & SDL_WINDOW_FULLSCREEN || IsFullscreenOnly(_this)) { SDL_Rect bounds; - SDL_GetDisplayBounds(display->id, &bounds); + SDL_GetDisplayBounds(display ? display->id : SDL_GetPrimaryDisplay(), &bounds); window->x = bounds.x; window->y = bounds.y; window->w = bounds.w; @@ -2751,6 +2763,9 @@ bool SDL_SetWindowPosition(SDL_Window *window, int x, int y) CHECK_WINDOW_MAGIC(window, false); + const int w = window->last_size_pending ? window->pending.w : window->windowed.w; + const int h = window->last_size_pending ? window->pending.h : window->windowed.h; + original_displayID = SDL_GetDisplayForWindow(window); if (SDL_WINDOWPOS_ISUNDEFINED(x)) { @@ -2773,30 +2788,27 @@ bool SDL_SetWindowPosition(SDL_Window *window, int x, int y) } SDL_zero(bounds); - if (!SDL_GetDisplayUsableBounds(displayID, &bounds) || - window->windowed.w > bounds.w || - window->windowed.h > bounds.h) { + if (!SDL_GetDisplayUsableBounds(displayID, &bounds) || w > bounds.w || h > bounds.h) { if (!SDL_GetDisplayBounds(displayID, &bounds)) { return false; } } if (SDL_WINDOWPOS_ISCENTERED(x)) { - x = bounds.x + (bounds.w - window->windowed.w) / 2; + x = bounds.x + (bounds.w - w) / 2; } if (SDL_WINDOWPOS_ISCENTERED(y)) { - y = bounds.y + (bounds.h - window->windowed.h) / 2; + y = bounds.y + (bounds.h - h) / 2; } } - window->floating.x = x; - window->floating.y = y; + window->pending.x = x; + window->pending.y = y; window->undefined_x = false; window->undefined_y = false; + window->last_position_pending = true; if (_this->SetWindowPosition) { - window->is_repositioning = true; const bool result = _this->SetWindowPosition(_this, window); - window->is_repositioning = false; if (result) { SDL_SyncIfRequired(window); } @@ -2941,8 +2953,9 @@ bool SDL_SetWindowSize(SDL_Window *window, int w, int h) h = window->max_h; } - window->floating.w = w; - window->floating.h = h; + window->last_size_pending = true; + window->pending.w = w; + window->pending.h = h; if (_this->SetWindowSize) { _this->SetWindowSize(_this, window); @@ -3108,7 +3121,8 @@ bool SDL_SetWindowMaximumSize(SDL_Window *window, int max_w, int max_h) return SDL_InvalidParamError("max_h"); } - if (max_w < window->min_w || max_h < window->min_h) { + if ((max_w && max_w < window->min_w) || + (max_h && max_h < window->min_h)) { return SDL_SetError("SDL_SetWindowMaximumSize(): Tried to set maximum size smaller than minimum size"); } @@ -3191,7 +3205,8 @@ bool SDL_HideWindow(SDL_Window *window) } // Store the flags for restoration later. - window->pending_flags = window->flags; + const SDL_WindowFlags pending_mask = (SDL_WINDOW_MAXIMIZED | SDL_WINDOW_MINIMIZED | SDL_WINDOW_FULLSCREEN | SDL_WINDOW_KEYBOARD_GRABBED | SDL_WINDOW_MOUSE_GRABBED); + window->pending_flags = (window->flags & pending_mask); window->is_hiding = true; if (_this->HideWindow) { @@ -3907,6 +3922,16 @@ void SDL_OnWindowPixelSizeChanged(SDL_Window *window) window->surface_valid = false; } +void SDL_OnWindowLiveResizeUpdate(SDL_Window *window) +{ + if (SDL_HasMainCallbacks()) { + SDL_IterateMainCallbacks(false); + } else { + // Send an expose event so the application can redraw + SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_EXPOSED, 0, 0); + } +} + static void SDL_CheckWindowSafeAreaChanged(SDL_Window *window) { SDL_Rect rect; @@ -3992,9 +4017,6 @@ void SDL_OnWindowFocusGained(SDL_Window *window) if (mouse && mouse->relative_mode) { SDL_SetMouseFocus(window); - if (mouse->relative_mode_warp) { - SDL_PerformWarpMouseInWindow(window, (float)window->w / 2.0f, (float)window->h / 2.0f, true); - } } SDL_UpdateWindowGrab(window); @@ -5284,6 +5306,10 @@ bool SDL_StartTextInputWithProperties(SDL_Window *window, SDL_PropertiesID props } } + if (_this->SetTextInputProperties) { + _this->SetTextInputProperties(_this, window, props); + } + // Show the on-screen keyboard, if desired if (AutoShowingScreenKeyboard() && !SDL_ScreenKeyboardShown(window)) { if (_this->ShowScreenKeyboard) { @@ -5439,6 +5465,30 @@ bool SDL_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonID) return SDL_SetError("Invalid number of buttons"); } + // in case either the title or message was a pointer from SDL_GetError(), make a copy + // now, as we'll likely overwrite error state in here. + bool titleisstack = false, msgisstack = false; + char *titlecpy = NULL; + char *msgcpy = NULL; + if (messageboxdata->title) { + const size_t slen = SDL_strlen(messageboxdata->title) + 1; + titlecpy = SDL_small_alloc(char, slen, &titleisstack); + if (!titlecpy) { + return false; + } + SDL_strlcpy(titlecpy, messageboxdata->title, slen); + } + + if (messageboxdata->message) { + const size_t slen = SDL_strlen(messageboxdata->message) + 1; + msgcpy = SDL_small_alloc(char, slen, &msgisstack); + if (!msgcpy) { + SDL_small_free(titlecpy, titleisstack); + return false; + } + SDL_strlcpy(msgcpy, messageboxdata->message, slen); + } + (void)SDL_AtomicIncRef(&SDL_messagebox_count); current_window = SDL_GetKeyboardFocus(); @@ -5453,9 +5503,11 @@ bool SDL_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonID) } SDL_memcpy(&mbdata, messageboxdata, sizeof(*messageboxdata)); + mbdata.title = titlecpy; if (!mbdata.title) { mbdata.title = ""; } + mbdata.message = msgcpy; if (!mbdata.message) { mbdata.message = ""; } @@ -5518,6 +5570,9 @@ bool SDL_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonID) SDL_UpdateRelativeMouseMode(); SDL_UpdateMouseCapture(false); + SDL_small_free(msgcpy, msgisstack); + SDL_small_free(titlecpy, titleisstack); + return result; } diff --git a/libs/SDL3/src/video/SDL_yuv.c b/libs/SDL3/src/video/SDL_yuv.c index 6c7ab1cd..2ab78a03 100644 --- a/libs/SDL3/src/video/SDL_yuv.c +++ b/libs/SDL3/src/video/SDL_yuv.c @@ -26,7 +26,7 @@ #include "yuv2rgb/yuv_rgb.h" -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV static bool IsPlanar2x2Format(SDL_PixelFormat format); #endif @@ -36,7 +36,7 @@ static bool IsPlanar2x2Format(SDL_PixelFormat format); */ bool SDL_CalculateYUVSize(SDL_PixelFormat format, int w, int h, size_t *size, size_t *pitch) { -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV int sz_plane = 0, sz_plane_chroma = 0, sz_plane_packed = 0; if (IsPlanar2x2Format(format) == true) { @@ -155,7 +155,7 @@ bool SDL_CalculateYUVSize(SDL_PixelFormat format, int w, int h, size_t *size, si #endif } -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV static bool GetYUVConversionType(SDL_Colorspace colorspace, YCbCrType *yuv_type) { @@ -2561,7 +2561,7 @@ bool SDL_ConvertPixels_YUV_to_YUV(int width, int height, SDL_PixelFormat src_format, SDL_Colorspace src_colorspace, SDL_PropertiesID src_properties, const void *src, int src_pitch, SDL_PixelFormat dst_format, SDL_Colorspace dst_colorspace, SDL_PropertiesID dst_properties, void *dst, int dst_pitch) { -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV if (src_colorspace != dst_colorspace) { return SDL_SetError("SDL_ConvertPixels_YUV_to_YUV: colorspace conversion not supported"); } diff --git a/libs/SDL3/src/video/android/SDL_androidevents.c b/libs/SDL3/src/video/android/SDL_androidevents.c index 0ffe38f6..056a2024 100644 --- a/libs/SDL3/src/video/android/SDL_androidevents.c +++ b/libs/SDL3/src/video/android/SDL_androidevents.c @@ -36,15 +36,16 @@ static void android_egl_context_restore(SDL_Window *window) { if (window) { - SDL_Event event; SDL_WindowData *data = window->internal; SDL_GL_MakeCurrent(window, NULL); if (!SDL_GL_MakeCurrent(window, (SDL_GLContext)data->egl_context)) { // The context is no longer valid, create a new one data->egl_context = (EGLContext)SDL_GL_CreateContext(window); SDL_GL_MakeCurrent(window, (SDL_GLContext)data->egl_context); + SDL_Event event; + SDL_zero(event); event.type = SDL_EVENT_RENDER_DEVICE_RESET; - event.common.timestamp = 0; + event.render.windowID = SDL_GetWindowID(window); SDL_PushEvent(&event); } data->backup_done = false; diff --git a/libs/SDL3/src/video/android/SDL_androidmouse.c b/libs/SDL3/src/video/android/SDL_androidmouse.c index 42a91800..b243c2b4 100644 --- a/libs/SDL3/src/video/android/SDL_androidmouse.c +++ b/libs/SDL3/src/video/android/SDL_androidmouse.c @@ -75,7 +75,8 @@ static SDL_Cursor *Android_WrapCursor(int custom_cursor, int system_cursor) static SDL_Cursor *Android_CreateDefaultCursor(void) { - return Android_WrapCursor(0, SDL_SYSTEM_CURSOR_DEFAULT); + SDL_SystemCursor id = SDL_GetDefaultSystemCursor(); + return Android_WrapCursor(0, id); } static SDL_Cursor *Android_CreateCursor(SDL_Surface *surface, int hot_x, int hot_y) diff --git a/libs/SDL3/src/video/cocoa/SDL_cocoaevents.m b/libs/SDL3/src/video/cocoa/SDL_cocoaevents.m index 886e88f2..ea7874a3 100644 --- a/libs/SDL3/src/video/cocoa/SDL_cocoaevents.m +++ b/libs/SDL3/src/video/cocoa/SDL_cocoaevents.m @@ -110,7 +110,7 @@ static void Cocoa_DispatchEvent(NSEvent *theEvent) NSDictionary *appDefaults = [[NSDictionary alloc] initWithObjectsAndKeys: [NSNumber numberWithBool:momentumScrollSupported], @"AppleMomentumScrollSupported", - [NSNumber numberWithBool:NO], @"ApplePressAndHoldEnabled", + [NSNumber numberWithBool:YES], @"ApplePressAndHoldEnabled", [NSNumber numberWithBool:YES], @"ApplePersistenceIgnoreState", nil]; [[NSUserDefaults standardUserDefaults] registerDefaults:appDefaults]; diff --git a/libs/SDL3/src/video/cocoa/SDL_cocoakeyboard.m b/libs/SDL3/src/video/cocoa/SDL_cocoakeyboard.m index 34c86c40..72614cb1 100644 --- a/libs/SDL3/src/video/cocoa/SDL_cocoakeyboard.m +++ b/libs/SDL3/src/video/cocoa/SDL_cocoakeyboard.m @@ -59,11 +59,10 @@ - (void)insertText:(id)aString replacementRange:(NSRange)replacementRange { - // TODO: Make use of replacementRange? - const char *str; - DEBUG_IME(@"insertText: %@", aString); + DEBUG_IME(@"insertText: %@ replacementRange: (%d, %d)", aString, + (int)replacementRange.location, (int)replacementRange.length); /* Could be NSString or NSAttributedString, so we have * to test and convert it before return as SDL event */ @@ -81,6 +80,12 @@ // Deliver the raw key event that generated this text [self sendPendingKey]; + if ((int)replacementRange.location != -1) { + // We're replacing the last character + SDL_SendKeyboardKey(0, SDL_GLOBAL_KEYBOARD_ID, 0, SDL_SCANCODE_BACKSPACE, true); + SDL_SendKeyboardKey(0, SDL_GLOBAL_KEYBOARD_ID, 0, SDL_SCANCODE_BACKSPACE, false); + } + SDL_SendKeyboardText(str); } @@ -131,8 +136,9 @@ SDL_SendEditingText([aString UTF8String], (int)selectedRange.location, (int)selectedRange.length); - DEBUG_IME(@"setMarkedText: %@, (%d, %d)", _markedText, - selectedRange.location, selectedRange.length); + DEBUG_IME(@"setMarkedText: %@, (%d, %d) replacement range (%d, %d)", _markedText, + (int)selectedRange.location, (int)selectedRange.length, + (int)replacementRange.location, (int)replacementRange.length); } - (void)unmarkText @@ -158,7 +164,7 @@ } DEBUG_IME(@"firstRectForCharacterRange: (%d, %d): windowHeight = %g, rect = %@", - aRange.location, aRange.length, windowHeight, + (int)aRange.location, (int)aRange.length, windowHeight, NSStringFromRect(rect)); rect = [window convertRectToScreen:rect]; @@ -168,7 +174,7 @@ - (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange { - DEBUG_IME(@"attributedSubstringFromRange: (%d, %d)", aRange.location, aRange.length); + DEBUG_IME(@"attributedSubstringFromRange: (%d, %d)", (int)aRange.location, (int)aRange.length); return nil; } diff --git a/libs/SDL3/src/video/cocoa/SDL_cocoamouse.m b/libs/SDL3/src/video/cocoa/SDL_cocoamouse.m index 24d7863b..8f2cef58 100644 --- a/libs/SDL3/src/video/cocoa/SDL_cocoamouse.m +++ b/libs/SDL3/src/video/cocoa/SDL_cocoamouse.m @@ -63,25 +63,6 @@ } @end -static SDL_Cursor *Cocoa_CreateDefaultCursor(void) -{ - @autoreleasepool { - NSCursor *nscursor; - SDL_Cursor *cursor = NULL; - - nscursor = [NSCursor arrowCursor]; - - if (nscursor) { - cursor = SDL_calloc(1, sizeof(*cursor)); - if (cursor) { - cursor->internal = (void *)CFBridgingRetain(nscursor); - } - } - - return cursor; - } -} - static SDL_Cursor *Cocoa_CreateCursor(SDL_Surface *surface, int hot_x, int hot_y) { @autoreleasepool { @@ -229,6 +210,12 @@ static SDL_Cursor *Cocoa_CreateSystemCursor(SDL_SystemCursor id) } } +static SDL_Cursor *Cocoa_CreateDefaultCursor(void) +{ + SDL_SystemCursor id = SDL_GetDefaultSystemCursor(); + return Cocoa_CreateSystemCursor(id); +} + static void Cocoa_FreeCursor(SDL_Cursor *cursor) { @autoreleasepool { diff --git a/libs/SDL3/src/video/cocoa/SDL_cocoavideo.m b/libs/SDL3/src/video/cocoa/SDL_cocoavideo.m index 93b91531..c2c0bb3b 100644 --- a/libs/SDL3/src/video/cocoa/SDL_cocoavideo.m +++ b/libs/SDL3/src/video/cocoa/SDL_cocoavideo.m @@ -63,6 +63,10 @@ static SDL_VideoDevice *Cocoa_CreateDevice(void) SDL_VideoDevice *device; SDL_CocoaVideoData *data; + if (![NSThread isMainThread]) { + return NULL; // this doesn't SDL_SetError() because SDL_VideoInit is just going to overwrite it. + } + Cocoa_RegisterApp(); // Initialize all variables that we clean on shutdown diff --git a/libs/SDL3/src/video/cocoa/SDL_cocoawindow.h b/libs/SDL3/src/video/cocoa/SDL_cocoawindow.h index dd30c200..83d6fe85 100644 --- a/libs/SDL3/src/video/cocoa/SDL_cocoawindow.h +++ b/libs/SDL3/src/video/cocoa/SDL_cocoawindow.h @@ -38,7 +38,8 @@ typedef enum PENDING_OPERATION_NONE = 0x00, PENDING_OPERATION_ENTER_FULLSCREEN = 0x01, PENDING_OPERATION_LEAVE_FULLSCREEN = 0x02, - PENDING_OPERATION_MINIMIZE = 0x04 + PENDING_OPERATION_MINIMIZE = 0x04, + PENDING_OPERATION_ZOOM = 0x08 } PendingWindowOperation; @interface SDL3Cocoa_WindowListener : NSResponder @@ -58,6 +59,7 @@ typedef enum NSInteger focusClickPending; float pendingWindowWarpX, pendingWindowWarpY; BOOL isDragAreaRunning; + NSTimer *liveResizeTimer; } - (BOOL)isTouchFromTrackpad:(NSEvent *)theEvent; @@ -82,6 +84,9 @@ typedef enum // Window delegate functionality - (BOOL)windowShouldClose:(id)sender; - (void)windowDidExpose:(NSNotification *)aNotification; +- (void)windowDidChangeOcclusionState:(NSNotification *)aNotification; +- (void)windowWillStartLiveResize:(NSNotification *)aNotification; +- (void)windowDidEndLiveResize:(NSNotification *)aNotification; - (void)windowDidMove:(NSNotification *)aNotification; - (void)windowDidResize:(NSNotification *)aNotification; - (void)windowDidMiniaturize:(NSNotification *)aNotification; @@ -144,10 +149,10 @@ typedef enum @property(nonatomic) SDL3Cocoa_WindowListener *listener; @property(nonatomic) NSModalSession modal_session; @property(nonatomic) SDL_CocoaVideoData *videodata; -@property(nonatomic) bool send_floating_size; -@property(nonatomic) bool send_floating_position; +@property(nonatomic) bool pending_size; +@property(nonatomic) bool pending_position; @property(nonatomic) bool border_toggled; -@property(nonatomic) BOOL checking_zoom; + #ifdef SDL_VIDEO_OPENGL_EGL @property(nonatomic) EGLSurface egl_surface; #endif diff --git a/libs/SDL3/src/video/cocoa/SDL_cocoawindow.m b/libs/SDL3/src/video/cocoa/SDL_cocoawindow.m index 7afa943e..c4512033 100644 --- a/libs/SDL3/src/video/cocoa/SDL_cocoawindow.m +++ b/libs/SDL3/src/video/cocoa/SDL_cocoawindow.m @@ -106,11 +106,14 @@ */ if ([menuItem action] == @selector(toggleFullScreen:)) { SDL_Window *window = [self findSDLWindow]; - if (window == NULL) { + if (!window) { return NO; - } else if (window->flags & SDL_WINDOW_FULLSCREEN) { + } + + SDL_CocoaWindowData *data = (__bridge SDL_CocoaWindowData *)window->internal; + if ((window->flags & SDL_WINDOW_FULLSCREEN) && ![data.listener isInFullscreenSpace]) { return NO; - } else if ((window->flags & SDL_WINDOW_RESIZABLE) == 0) { + } else if (!(window->flags & SDL_WINDOW_RESIZABLE)) { return NO; } } @@ -663,17 +666,6 @@ static void Cocoa_WaitForMiniaturizable(SDL_Window *window) } } -static bool Cocoa_IsZoomed(SDL_Window *window) -{ - SDL_CocoaWindowData *data = (__bridge SDL_CocoaWindowData *)window->internal; - - data.checking_zoom = YES; - const bool ret = [data.nswindow isZoomed]; - data.checking_zoom = NO; - - return ret; -} - static NSCursor *Cocoa_GetDesiredCursor(void) { SDL_Mouse *mouse = SDL_GetMouse(); @@ -704,11 +696,15 @@ static NSCursor *Cocoa_GetDesiredCursor(void) isMiniaturizing = NO; isDragAreaRunning = NO; pendingWindowWarpX = pendingWindowWarpY = FLT_MAX; + liveResizeTimer = nil; center = [NSNotificationCenter defaultCenter]; if ([window delegate] != nil) { [center addObserver:self selector:@selector(windowDidExpose:) name:NSWindowDidExposeNotification object:window]; + [center addObserver:self selector:@selector(windowDidChangeOcclusionState:) name:NSWindowDidChangeOcclusionStateNotification object:window]; + [center addObserver:self selector:@selector(windowWillStartLiveResize:) name:NSWindowWillStartLiveResizeNotification object:window]; + [center addObserver:self selector:@selector(windowDidEndLiveResize:) name:NSWindowDidEndLiveResizeNotification object:window]; [center addObserver:self selector:@selector(windowWillMove:) name:NSWindowWillMoveNotification object:window]; [center addObserver:self selector:@selector(windowDidMove:) name:NSWindowDidMoveNotification object:window]; [center addObserver:self selector:@selector(windowDidResize:) name:NSWindowDidResizeNotification object:window]; @@ -726,7 +722,6 @@ static NSCursor *Cocoa_GetDesiredCursor(void) [center addObserver:self selector:@selector(windowDidExitFullScreen:) name:NSWindowDidExitFullScreenNotification object:window]; [center addObserver:self selector:@selector(windowDidFailToEnterFullScreen:) name:@"NSWindowDidFailToEnterFullScreenNotification" object:window]; [center addObserver:self selector:@selector(windowDidFailToExitFullScreen:) name:@"NSWindowDidFailToExitFullScreenNotification" object:window]; - [center addObserver:self selector:@selector(windowDidChangeOcclusionState:) name:NSWindowDidChangeOcclusionStateNotification object:window]; } else { [window setDelegate:self]; } @@ -861,6 +856,9 @@ static NSCursor *Cocoa_GetDesiredCursor(void) if ([window delegate] != self) { [center removeObserver:self name:NSWindowDidExposeNotification object:window]; + [center removeObserver:self name:NSWindowDidChangeOcclusionStateNotification object:window]; + [center removeObserver:self name:NSWindowWillStartLiveResizeNotification object:window]; + [center removeObserver:self name:NSWindowDidEndLiveResizeNotification object:window]; [center removeObserver:self name:NSWindowWillMoveNotification object:window]; [center removeObserver:self name:NSWindowDidMoveNotification object:window]; [center removeObserver:self name:NSWindowDidResizeNotification object:window]; @@ -878,7 +876,6 @@ static NSCursor *Cocoa_GetDesiredCursor(void) [center removeObserver:self name:NSWindowDidExitFullScreenNotification object:window]; [center removeObserver:self name:@"NSWindowDidFailToEnterFullScreenNotification" object:window]; [center removeObserver:self name:@"NSWindowDidFailToExitFullScreenNotification" object:window]; - [center removeObserver:self name:NSWindowDidChangeOcclusionStateNotification object:window]; } else { [window setDelegate:nil]; } @@ -962,7 +959,7 @@ static NSCursor *Cocoa_GetDesiredCursor(void) mouse->WarpMouseGlobal(pendingWindowWarpX, pendingWindowWarpY); pendingWindowWarpX = pendingWindowWarpY = FLT_MAX; } - if (mouse->relative_mode && !mouse->relative_mode_warp && mouse->focus == _data.window) { + if (mouse->relative_mode && mouse->focus == _data.window) { // Move the cursor to the nearest point in the window { float x, y; @@ -1005,6 +1002,26 @@ static NSCursor *Cocoa_GetDesiredCursor(void) } } +- (void)windowWillStartLiveResize:(NSNotification *)aNotification +{ + // We'll try to maintain 60 FPS during live resizing + const NSTimeInterval interval = 1.0 / 60.0; + liveResizeTimer = [NSTimer scheduledTimerWithTimeInterval:interval + repeats:TRUE + block:^(NSTimer *unusedTimer) + { + SDL_OnWindowLiveResizeUpdate(_data.window); + }]; + + [[NSRunLoop currentRunLoop] addTimer:liveResizeTimer forMode:NSRunLoopCommonModes]; +} + +- (void)windowDidEndLiveResize:(NSNotification *)aNotification +{ + [liveResizeTimer invalidate]; + liveResizeTimer = nil; +} + - (void)windowWillMove:(NSNotification *)aNotification { if ([_data.nswindow isKindOfClass:[SDL3Window class]]) { @@ -1040,27 +1057,6 @@ static NSCursor *Cocoa_GetDesiredCursor(void) { SDL_Window *window = _data.window; - /* XXX: Calling [isZoomed] calls this function, and calling [isZoomed] - * from within this function will recurse until the stack overflows, - * so a recursion guard is required. - */ - if (!_data.checking_zoom) { - _data.checking_zoom = YES; - if ([_data.nswindow isZoomed] && !_data.was_zoomed && _data.send_floating_size) { - NSRect rect; - - _data.send_floating_size = NO; - rect.origin.x = window->floating.x; - rect.origin.y = window->floating.y; - rect.size.width = window->floating.w; - rect.size.height = window->floating.h; - ConvertNSRect(&rect); - - frameSize = rect.size; - } - _data.checking_zoom = NO; - } - if (window->min_aspect > 0.0f || window->max_aspect > 0.0f) { NSWindow *nswindow = _data.nswindow; NSRect newContentRect = [nswindow contentRectForFrameRect:NSMakeRect(0, 0, frameSize.width, frameSize.height)]; @@ -1116,7 +1112,7 @@ static NSCursor *Cocoa_GetDesiredCursor(void) /* isZoomed always returns true if the window is not resizable * and fullscreen windows are considered zoomed. */ - if ((window->flags & SDL_WINDOW_RESIZABLE) && Cocoa_IsZoomed(window) && + if ((window->flags & SDL_WINDOW_RESIZABLE) && [nswindow isZoomed] && !(window->flags & SDL_WINDOW_FULLSCREEN) && ![self isInFullscreenSpace]) { zoomed = YES; } else { @@ -1161,7 +1157,7 @@ static NSCursor *Cocoa_GetDesiredCursor(void) SDL_SendWindowEvent(_data.window, SDL_EVENT_WINDOW_RESTORED, 0, 0); // isZoomed always returns true if the window is not resizable. - if ((_data.window->flags & SDL_WINDOW_RESIZABLE) && Cocoa_IsZoomed(_data.window)) { + if ((_data.window->flags & SDL_WINDOW_RESIZABLE) && [_data.nswindow isZoomed]) { SDL_SendWindowEvent(_data.window, SDL_EVENT_WINDOW_MAXIMIZED, 0, 0); } @@ -1203,6 +1199,14 @@ static NSCursor *Cocoa_GetDesiredCursor(void) _data.videodata.modifierFlags = (_data.videodata.modifierFlags & ~NSEventModifierFlagCapsLock) | newflags; SDL_ToggleModState(SDL_KMOD_CAPS, newflags ? true : false); } + + /* Restore fullscreen mode unless the window is deminiaturizing. + * If it is, fullscreen will be restored when deminiaturization is complete. + */ + if (!(window->flags & SDL_WINDOW_MINIMIZED) && + [self windowOperationIsPending:PENDING_OPERATION_ENTER_FULLSCREEN]) { + SDL_UpdateFullscreenMode(window, true, true); + } } - (void)windowDidResignKey:(NSNotification *)aNotification @@ -1313,6 +1317,9 @@ static NSCursor *Cocoa_GetDesiredCursor(void) } SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_ENTER_FULLSCREEN, 0, 0); + _data.pending_position = NO; + _data.pending_size = NO; + /* Force the size change event in case it was delivered earlier while the window was still animating into place. */ @@ -1351,6 +1358,11 @@ static NSCursor *Cocoa_GetDesiredCursor(void) return; } + _data.pending_position = NO; + _data.pending_size = NO; + window->last_position_pending = false; + window->last_size_pending = false; + SetWindowStyle(window, flags); isFullscreenSpace = YES; @@ -1414,18 +1426,32 @@ static NSCursor *Cocoa_GetDesiredCursor(void) } [NSMenu setMenuBarVisible:YES]; - // Restore windowed size and position in case it changed while fullscreen - NSRect rect; - rect.origin.x = _data.was_zoomed ? window->windowed.x : window->floating.x; - rect.origin.y = _data.was_zoomed ? window->windowed.y : window->floating.y; - rect.size.width = _data.was_zoomed ? window->windowed.w : window->floating.w; - rect.size.height = _data.was_zoomed ? window->windowed.h : window->floating.h; - ConvertNSRect(&rect); + // Toggle zoom, if changed while fullscreen. + if ([self windowOperationIsPending:PENDING_OPERATION_ZOOM]) { + [self clearPendingWindowOperation:PENDING_OPERATION_ZOOM]; + [nswindow zoom:nil]; + } - _data.send_floating_position = NO; - _data.send_floating_size = NO; - [nswindow setContentSize:rect.size]; - [nswindow setFrameOrigin:rect.origin]; + if (![nswindow isZoomed]) { + // Apply a pending window size, if not zoomed. + NSRect rect; + rect.origin.x = _data.pending_position ? window->pending.x : window->floating.x; + rect.origin.y = _data.pending_position ? window->pending.y : window->floating.y; + rect.size.width = _data.pending_size ? window->pending.w : window->floating.w; + rect.size.height = _data.pending_size ? window->pending.h : window->floating.h; + ConvertNSRect(&rect); + + if (_data.pending_size) { + [nswindow setContentSize:rect.size]; + } + if (_data.pending_position) { + [nswindow setFrameOrigin:rect.origin]; + } + } + + _data.pending_size = NO; + _data.pending_position = NO; + _data.was_zoomed = NO; /* Force the size change event in case it was delivered earlier * while the window was still animating into place. @@ -1435,8 +1461,6 @@ static NSCursor *Cocoa_GetDesiredCursor(void) [self windowDidMove:aNotification]; [self windowDidResize:aNotification]; - _data.was_zoomed = false; - // FIXME: Why does the window get hidden? if (!(window->flags & SDL_WINDOW_HIDDEN)) { Cocoa_ShowWindow(SDL_GetVideoDevice(), window); @@ -1548,7 +1572,7 @@ static NSCursor *Cocoa_GetDesiredCursor(void) static void Cocoa_SendMouseButtonClicks(SDL_Mouse *mouse, NSEvent *theEvent, SDL_Window *window, Uint8 button, bool down) { SDL_MouseID mouseID = SDL_DEFAULT_MOUSE_ID; - const int clicks = (int)[theEvent clickCount]; + //const int clicks = (int)[theEvent clickCount]; SDL_Window *focus = SDL_GetKeyboardFocus(); // macOS will send non-left clicks to background windows without raising them, so we need to @@ -1557,14 +1581,16 @@ static void Cocoa_SendMouseButtonClicks(SDL_Mouse *mouse, NSEvent *theEvent, SDL // event for the background window, this just makes sure the button is reported at the // correct position in its own event. if (focus && ([theEvent window] == ((__bridge SDL_CocoaWindowData *)focus->internal).nswindow)) { - SDL_SendMouseButtonClicks(Cocoa_GetEventTimestamp([theEvent timestamp]), window, mouseID, button, down, clicks); + //SDL_SendMouseButtonClicks(Cocoa_GetEventTimestamp([theEvent timestamp]), window, mouseID, button, down, clicks); + SDL_SendMouseButton(Cocoa_GetEventTimestamp([theEvent timestamp]), window, mouseID, button, down); } else { const float orig_x = mouse->x; const float orig_y = mouse->y; const NSPoint point = [theEvent locationInWindow]; mouse->x = (int)point.x; mouse->y = (int)(window->h - point.y); - SDL_SendMouseButtonClicks(Cocoa_GetEventTimestamp([theEvent timestamp]), window, mouseID, button, down, clicks); + //SDL_SendMouseButtonClicks(Cocoa_GetEventTimestamp([theEvent timestamp]), window, mouseID, button, down, clicks); + SDL_SendMouseButton(Cocoa_GetEventTimestamp([theEvent timestamp]), window, mouseID, button, down); mouse->x = orig_x; mouse->y = orig_y; } @@ -2070,7 +2096,7 @@ static bool SetupWindowData(SDL_VideoDevice *_this, SDL_Window *window, NSWindow } // isZoomed always returns true if the window is not resizable - if ((window->flags & SDL_WINDOW_RESIZABLE) && Cocoa_IsZoomed(window)) { + if ((window->flags & SDL_WINDOW_RESIZABLE) && [nswindow isZoomed]) { window->flags |= SDL_WINDOW_MAXIMIZED; } else { window->flags &= ~SDL_WINDOW_MAXIMIZED; @@ -2339,9 +2365,9 @@ bool Cocoa_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window *window) BOOL fullscreen = (window->flags & SDL_WINDOW_FULLSCREEN) ? YES : NO; int x, y; - if ([windata.listener windowOperationIsPending:(PENDING_OPERATION_ENTER_FULLSCREEN | PENDING_OPERATION_LEAVE_FULLSCREEN)] || - [windata.listener isInFullscreenSpaceTransition]) { - Cocoa_SyncWindow(_this, window); + if ([windata.listener isInFullscreenSpaceTransition]) { + windata.pending_position = YES; + return true; } if (!(window->flags & SDL_WINDOW_MAXIMIZED)) { @@ -2353,7 +2379,7 @@ bool Cocoa_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window *window) rect.origin.x = r.x; rect.origin.y = r.y; } else { - SDL_RelativeToGlobalForWindow(window, window->floating.x, window->floating.y, &x, &y); + SDL_RelativeToGlobalForWindow(window, window->pending.x, window->pending.y, &x, &y); rect.origin.x = x; rect.origin.y = y; } @@ -2376,8 +2402,6 @@ bool Cocoa_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window *window) [nswindow setFrameOrigin:rect.origin]; ScheduleContextUpdates(windata); - } else { - windata.send_floating_position = true; } } return true; @@ -2389,13 +2413,13 @@ void Cocoa_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window) SDL_CocoaWindowData *windata = (__bridge SDL_CocoaWindowData *)window->internal; NSWindow *nswindow = windata.nswindow; - if ([windata.listener windowOperationIsPending:(PENDING_OPERATION_ENTER_FULLSCREEN | PENDING_OPERATION_LEAVE_FULLSCREEN)] || - [windata.listener isInFullscreenSpaceTransition]) { - Cocoa_SyncWindow(_this, window); + if ([windata.listener isInFullscreenSpaceTransition]) { + windata.pending_size = YES; + return; } // isZoomed always returns true if the window is not resizable - if (!(window->flags & SDL_WINDOW_RESIZABLE) || !Cocoa_IsZoomed(window)) { + if (!(window->flags & SDL_WINDOW_RESIZABLE) || ![nswindow isZoomed]) { if (!(window->flags & SDL_WINDOW_FULLSCREEN)) { int x, y; NSRect rect = [nswindow contentRectForFrameRect:[nswindow frame]]; @@ -2407,17 +2431,19 @@ void Cocoa_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window) SDL_RelativeToGlobalForWindow(window, window->floating.x, window->floating.y, &x, &y); rect.origin.x = x; rect.origin.y = y; - rect.size.width = window->floating.w; - rect.size.height = window->floating.h; + rect.size.width = window->pending.w; + rect.size.height = window->pending.h; ConvertNSRect(&rect); [nswindow setFrame:[nswindow frameRectForContentRect:rect] display:YES]; ScheduleContextUpdates(windata); - } else if (windata.was_zoomed) { - windata.send_floating_size = true; + } else { + // Can't set the window size. + window->last_size_pending = false; } - } else { - windata.send_floating_size = true; + } else { + // Can't set the window size. + window->last_size_pending = false; } } } @@ -2601,6 +2627,10 @@ void Cocoa_MaximizeWindow(SDL_VideoDevice *_this, SDL_Window *window) ![windata.listener isInFullscreenSpace]) { [nswindow zoom:nil]; ScheduleContextUpdates(windata); + } else if (!windata.was_zoomed) { + [windata.listener addPendingWindowOperation:PENDING_OPERATION_ZOOM]; + } else { + [windata.listener clearPendingWindowOperation:PENDING_OPERATION_ZOOM]; } } } @@ -2629,8 +2659,8 @@ void Cocoa_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window) SDL_CocoaWindowData *data = (__bridge SDL_CocoaWindowData *)window->internal; NSWindow *nswindow = data.nswindow; - if ([data.listener windowOperationIsPending:(PENDING_OPERATION_ENTER_FULLSCREEN | PENDING_OPERATION_LEAVE_FULLSCREEN)] || - [data.listener isInFullscreenSpaceTransition]) { + if (([data.listener windowOperationIsPending:(PENDING_OPERATION_ENTER_FULLSCREEN | PENDING_OPERATION_LEAVE_FULLSCREEN)] && + ![data.nswindow isMiniaturized]) || [data.listener isInFullscreenSpaceTransition]) { Cocoa_SyncWindow(_this, window); } @@ -2641,27 +2671,13 @@ void Cocoa_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window) ![data.listener isInFullscreenSpace]) { if ([nswindow isMiniaturized]) { [nswindow deminiaturize:nil]; - } else if ((window->flags & SDL_WINDOW_RESIZABLE) && Cocoa_IsZoomed(window)) { - NSRect rect; - - // Update the floating coordinates - rect.origin.x = window->floating.x; - rect.origin.y = window->floating.y; - - // The floating size will be set in windowWillResize + } else if ((window->flags & SDL_WINDOW_RESIZABLE) && [data.nswindow isZoomed]) { [nswindow zoom:nil]; - - rect.size.width = window->floating.w; - rect.size.height = window->floating.h; - - ConvertNSRect(&rect); - - if (data.send_floating_position) { - data.send_floating_position = false; - [nswindow setFrameOrigin:rect.origin]; - ScheduleContextUpdates(data); - } } + } else if (data.was_zoomed) { + [data.listener addPendingWindowOperation:PENDING_OPERATION_ZOOM]; + } else { + [data.listener clearPendingWindowOperation:PENDING_OPERATION_ZOOM]; } } } @@ -2733,6 +2749,9 @@ SDL_FullscreenResult Cocoa_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Windo NSWindow *nswindow = data.nswindow; NSRect rect; + // This is a synchronous operation, so always clear the pending flags. + [data.listener clearPendingWindowOperation:PENDING_OPERATION_ENTER_FULLSCREEN | PENDING_OPERATION_LEAVE_FULLSCREEN]; + // The view responder chain gets messed with during setStyleMask if ([data.sdlContentView nextResponder] == data.listener) { [data.sdlContentView setNextResponder:nil]; @@ -2801,13 +2820,10 @@ SDL_FullscreenResult Cocoa_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Windo // When the window style changes the title is cleared if (!fullscreen) { Cocoa_SetWindowTitle(_this, window); - data.was_zoomed = NO; - - if ([data.listener windowOperationIsPending:PENDING_OPERATION_MINIMIZE]) { - Cocoa_WaitForMiniaturizable(window); - [data.listener addPendingWindowOperation:PENDING_OPERATION_ENTER_FULLSCREEN]; - [nswindow miniaturize:nil]; + if ([data.listener windowOperationIsPending:PENDING_OPERATION_ZOOM]) { + [data.listener clearPendingWindowOperation:PENDING_OPERATION_ZOOM]; + [nswindow zoom:nil]; } } @@ -2842,6 +2858,16 @@ SDL_FullscreenResult Cocoa_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Windo } } + /* When coming out of fullscreen to minimize, this needs to happen after the window + * is made key again, or it won't minimize on 15.0 (Sequoia). + */ + if (!fullscreen && [data.listener windowOperationIsPending:PENDING_OPERATION_MINIMIZE]) { + Cocoa_WaitForMiniaturizable(window); + [data.listener addPendingWindowOperation:PENDING_OPERATION_ENTER_FULLSCREEN]; + [data.listener clearPendingWindowOperation:PENDING_OPERATION_MINIMIZE]; + [nswindow miniaturize:nil]; + } + ScheduleContextUpdates(data); } diff --git a/libs/SDL3/src/video/dummy/SDL_nullvideo.c b/libs/SDL3/src/video/dummy/SDL_nullvideo.c index 98b485be..295176c6 100644 --- a/libs/SDL3/src/video/dummy/SDL_nullvideo.c +++ b/libs/SDL3/src/video/dummy/SDL_nullvideo.c @@ -57,13 +57,13 @@ static void DUMMY_VideoQuit(SDL_VideoDevice *_this); static bool DUMMY_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window *window) { - SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_MOVED, window->floating.x, window->floating.y); + SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_MOVED, window->pending.x, window->pending.y); return true; } static void DUMMY_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window) { - SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, window->floating.w, window->floating.h); + SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, window->pending.w, window->pending.h); } // DUMMY driver bootstrap functions diff --git a/libs/SDL3/src/video/emscripten/SDL_emscriptenevents.c b/libs/SDL3/src/video/emscripten/SDL_emscriptenevents.c index 18d9117f..aeffef64 100644 --- a/libs/SDL3/src/video/emscripten/SDL_emscriptenevents.c +++ b/libs/SDL3/src/video/emscripten/SDL_emscriptenevents.c @@ -26,6 +26,7 @@ #include #include +#include "../../events/SDL_dropevents_c.h" #include "../../events/SDL_events_c.h" #include "../../events/SDL_keyboard_c.h" #include "../../events/SDL_touch_c.h" @@ -806,6 +807,144 @@ static void Emscripten_unset_pointer_event_callbacks(SDL_WindowData *data) }, data->canvas_id); } +// IF YOU CHANGE THIS STRUCTURE, YOU NEED TO UPDATE THE JAVASCRIPT THAT FILLS IT IN: makeDropEventCStruct, below. +typedef struct Emscripten_DropEvent +{ + int x; + int y; +} Emscripten_DropEvent; + +EMSCRIPTEN_KEEPALIVE void Emscripten_SendDragEvent(SDL_WindowData *window_data, const Emscripten_DropEvent *event) +{ + SDL_SendDropPosition(window_data->window, event->x, event->y); +} + +EMSCRIPTEN_KEEPALIVE void Emscripten_SendDragCompleteEvent(SDL_WindowData *window_data) +{ + SDL_SendDropComplete(window_data->window); +} + +EMSCRIPTEN_KEEPALIVE void Emscripten_SendDragTextEvent(SDL_WindowData *window_data, char *text) +{ + SDL_SendDropText(window_data->window, text); +} + +EMSCRIPTEN_KEEPALIVE void Emscripten_SendDragFileEvent(SDL_WindowData *window_data, char *filename) +{ + SDL_SendDropFile(window_data->window, NULL, filename); +} + +EM_JS_DEPS(dragndrop, "$writeArrayToMemory"); + +static void Emscripten_set_drag_event_callbacks(SDL_WindowData *data) +{ + MAIN_THREAD_EM_ASM({ + var target = document.querySelector(UTF8ToString($1)); + if (target) { + var data = $0; + + if (typeof(Module['SDL3']) === 'undefined') { + Module['SDL3'] = {}; + } + var SDL3 = Module['SDL3']; + + var makeDropEventCStruct = function(event) { + var ptr = 0; + ptr = _SDL_malloc($2); + if (ptr != 0) { + var idx = ptr >> 2; + var rect = target.getBoundingClientRect(); + HEAP32[idx++] = event.clientX - rect.left; + HEAP32[idx++] = event.clientY - rect.top; + } + return ptr; + }; + + SDL3.eventHandlerDropDragover = function(event) { + event.preventDefault(); + var d = makeDropEventCStruct(event); if (d != 0) { _Emscripten_SendDragEvent(data, d); _SDL_free(d); } + }; + target.addEventListener("dragover", SDL3.eventHandlerDropDragover); + + SDL3.drop_count = 0; + FS.mkdir("/tmp/filedrop"); + SDL3.eventHandlerDropDrop = function(event) { + event.preventDefault(); + if (event.dataTransfer.types.includes("text/plain")) { + let plain_text = stringToNewUTF8(event.dataTransfer.getData("text/plain")); + _Emscripten_SendDragTextEvent(data, plain_text); + _free(plain_text); + } else if (event.dataTransfer.types.includes("Files")) { + for (let i = 0; i < event.dataTransfer.files.length; i++) { + const file = event.dataTransfer.files.item(i); + const file_reader = new FileReader(); + file_reader.readAsArrayBuffer(file); + file_reader.onload = function(event) { + const fs_dropdir = `/tmp/filedrop/${SDL3.drop_count}`; + SDL3.drop_count += 1; + + const fs_filepath = `${fs_dropdir}/${file.name}`; + const c_fs_filepath = stringToNewUTF8(fs_filepath); + const contents_array8 = new Uint8Array(event.target.result); + + FS.mkdir(fs_dropdir); + var stream = FS.open(fs_filepath, "w"); + FS.write(stream, contents_array8, 0, contents_array8.length, 0); + FS.close(stream); + + _Emscripten_SendDragFileEvent(data, c_fs_filepath); + _free(c_fs_filepath); + _Emscripten_SendDragCompleteEvent(data); + }; + } + } + _Emscripten_SendDragCompleteEvent(data); + }; + target.addEventListener("drop", SDL3.eventHandlerDropDrop); + + SDL3.eventHandlerDropDragend = function(event) { + event.preventDefault(); + _Emscripten_SendDragCompleteEvent(data); + }; + target.addEventListener("dragend", SDL3.eventHandlerDropDragend); + target.addEventListener("dragleave", SDL3.eventHandlerDropDragend); + } + }, data, data->canvas_id, sizeof (Emscripten_DropEvent)); +} + +static void Emscripten_unset_drag_event_callbacks(SDL_WindowData *data) +{ + MAIN_THREAD_EM_ASM({ + var target = document.querySelector(UTF8ToString($0)); + if (target) { + var SDL3 = Module['SDL3']; + target.removeEventListener("dragleave", SDL3.eventHandlerDropDragend); + target.removeEventListener("dragend", SDL3.eventHandlerDropDragend); + target.removeEventListener("drop", SDL3.eventHandlerDropDrop); + SDL3.drop_count = undefined; + + function recursive_remove(dirpath) { + FS.readdir(dirpath).forEach((filename) => { + const p = `${dirpath}/${filename}`; + const p_s = FS.stat(p); + if (FS.isFile(p_s.mode)) { + FS.unlink(p); + } else if (FS.isDir(p)) { + recursive_remove(p); + } + }); + FS.rmdir(dirpath); + }("/tmp/filedrop"); + + FS.rmdir("/tmp/filedrop"); + target.removeEventListener("dragover", SDL3.eventHandlerDropDragover); + SDL3.eventHandlerDropDragover = undefined; + SDL3.eventHandlerDropDrop = undefined; + SDL3.eventHandlerDropDragend = undefined; + } + }, data->canvas_id); +} + void Emscripten_RegisterEventHandlers(SDL_WindowData *data) { const char *keyElement; @@ -852,12 +991,18 @@ void Emscripten_RegisterEventHandlers(SDL_WindowData *data) // !!! FIXME: currently Emscripten doesn't have a Pointer Events functions like emscripten_set_*_callback, but we should use those when they do: // !!! FIXME: https://github.com/emscripten-core/emscripten/issues/7278#issuecomment-2280024621 Emscripten_set_pointer_event_callbacks(data); + + // !!! FIXME: currently Emscripten doesn't have a Drop Events functions like emscripten_set_*_callback, but we should use those when they do: + Emscripten_set_drag_event_callbacks(data); } void Emscripten_UnregisterEventHandlers(SDL_WindowData *data) { const char *target; + // !!! FIXME: currently Emscripten doesn't have a Drop Events functions like emscripten_set_*_callback, but we should use those when they do: + Emscripten_unset_drag_event_callbacks(data); + // !!! FIXME: currently Emscripten doesn't have a Pointer Events functions like emscripten_set_*_callback, but we should use those when they do: // !!! FIXME: https://github.com/emscripten-core/emscripten/issues/7278#issuecomment-2280024621 Emscripten_unset_pointer_event_callbacks(data); diff --git a/libs/SDL3/src/video/emscripten/SDL_emscriptenmouse.c b/libs/SDL3/src/video/emscripten/SDL_emscriptenmouse.c index 44ca4dd7..64f49a5e 100644 --- a/libs/SDL3/src/video/emscripten/SDL_emscriptenmouse.c +++ b/libs/SDL3/src/video/emscripten/SDL_emscriptenmouse.c @@ -62,7 +62,9 @@ static SDL_Cursor *Emscripten_CreateCursorFromString(const char *cursor_str, boo static SDL_Cursor *Emscripten_CreateDefaultCursor(void) { - return Emscripten_CreateCursorFromString("default", false); + SDL_SystemCursor id = SDL_GetDefaultSystemCursor(); + const char *cursor_name = SDL_GetCSSCursorName(id, NULL); + return Emscripten_CreateCursorFromString(cursor_name, false); } EM_JS_DEPS(sdlmouse, "$stringToUTF8,$UTF8ToString"); diff --git a/libs/SDL3/src/video/emscripten/SDL_emscriptenvideo.c b/libs/SDL3/src/video/emscripten/SDL_emscriptenvideo.c index 02b193c7..45a6e9a1 100644 --- a/libs/SDL3/src/video/emscripten/SDL_emscriptenvideo.c +++ b/libs/SDL3/src/video/emscripten/SDL_emscriptenvideo.c @@ -55,6 +55,77 @@ static void Emscripten_DeleteDevice(SDL_VideoDevice *device) SDL_free(device); } +static SDL_SystemTheme Emscripten_GetSystemTheme(void) +{ + /* Technically, light theme can mean explicit light theme or no preference. + https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme#syntax */ + + int theme_code = EM_ASM_INT({ + if (!window.matchMedia) { + return -1; + } + + if (window.matchMedia('(prefers-color-scheme: light)').matches) { + return 0; + } + + if (window.matchMedia('(prefers-color-scheme: dark)').matches) { + return 1; + } + + return -1; + }); + + switch (theme_code) { + case 0: + return SDL_SYSTEM_THEME_LIGHT; + + case 1: + return SDL_SYSTEM_THEME_DARK; + + default: + return SDL_SYSTEM_THEME_UNKNOWN; + } +} + +static void Emscripten_ListenSystemTheme(void) +{ + MAIN_THREAD_EM_ASM({ + if (window.matchMedia) { + if (typeof(Module['SDL3']) === 'undefined') { + Module['SDL3'] = {}; + } + + var SDL3 = Module['SDL3']; + + SDL3.eventHandlerThemeChanged = function(event) { + _Emscripten_SendSystemThemeChangedEvent(); + }; + + SDL3.themeChangedMatchMedia = window.matchMedia('(prefers-color-scheme: dark)'); + SDL3.themeChangedMatchMedia.addEventListener('change', SDL3.eventHandlerThemeChanged); + } + }); +} + +static void Emscripten_UnlistenSystemTheme(void) +{ + MAIN_THREAD_EM_ASM({ + if (typeof(Module['SDL3']) !== 'undefined') { + var SDL3 = Module['SDL3']; + + SDL3.themeChangedMatchMedia.removeEventListener('change', SDL3.eventHandlerThemeChanged); + SDL3.themeChangedMatchMedia = undefined; + SDL3.eventHandlerThemeChanged = undefined; + } + }); +} + +EMSCRIPTEN_KEEPALIVE void Emscripten_SendSystemThemeChangedEvent(void) +{ + SDL_SetSystemTheme(Emscripten_GetSystemTheme()); +} + static SDL_VideoDevice *Emscripten_CreateDevice(void) { SDL_VideoDevice *device; @@ -111,6 +182,9 @@ static SDL_VideoDevice *Emscripten_CreateDevice(void) device->free = Emscripten_DeleteDevice; + Emscripten_ListenSystemTheme(); + device->system_theme = Emscripten_GetSystemTheme(); + return device; } @@ -153,6 +227,7 @@ static bool Emscripten_SetDisplayMode(SDL_VideoDevice *_this, SDL_VideoDisplay * static void Emscripten_VideoQuit(SDL_VideoDevice *_this) { Emscripten_QuitMouse(); + Emscripten_UnlistenSystemTheme(); } static bool Emscripten_GetDisplayUsableBounds(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_Rect *rect) @@ -252,14 +327,14 @@ static void Emscripten_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window) if (window->flags & SDL_WINDOW_HIGH_PIXEL_DENSITY) { data->pixel_ratio = emscripten_get_device_pixel_ratio(); } - emscripten_set_canvas_element_size(data->canvas_id, SDL_lroundf(window->floating.w * data->pixel_ratio), SDL_lroundf(window->floating.h * data->pixel_ratio)); + emscripten_set_canvas_element_size(data->canvas_id, SDL_lroundf(window->pending.w * data->pixel_ratio), SDL_lroundf(window->pending.h * data->pixel_ratio)); // scale canvas down if (!data->external_size && data->pixel_ratio != 1.0f) { - emscripten_set_element_css_size(data->canvas_id, window->floating.w, window->floating.h); + emscripten_set_element_css_size(data->canvas_id, window->pending.w, window->pending.h); } - SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, window->floating.w, window->floating.h); + SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, window->pending.w, window->pending.h); } } diff --git a/libs/SDL3/src/video/haiku/SDL_bvideo.cc b/libs/SDL3/src/video/haiku/SDL_bvideo.cc index 23521f73..854c1b39 100644 --- a/libs/SDL3/src/video/haiku/SDL_bvideo.cc +++ b/libs/SDL3/src/video/haiku/SDL_bvideo.cc @@ -180,7 +180,8 @@ static SDL_Cursor * HAIKU_CreateSystemCursor(SDL_SystemCursor id) static SDL_Cursor * HAIKU_CreateDefaultCursor() { - return HAIKU_CreateSystemCursor(SDL_SYSTEM_CURSOR_DEFAULT); + SDL_SystemCursor id = SDL_GetDefaultSystemCursor(); + return HAIKU_CreateSystemCursor(id); } static void HAIKU_FreeCursor(SDL_Cursor * cursor) diff --git a/libs/SDL3/src/video/haiku/SDL_bwindow.cc b/libs/SDL3/src/video/haiku/SDL_bwindow.cc index de9bc248..902676a5 100644 --- a/libs/SDL3/src/video/haiku/SDL_bwindow.cc +++ b/libs/SDL3/src/video/haiku/SDL_bwindow.cc @@ -100,8 +100,8 @@ void HAIKU_SetWindowTitle(SDL_VideoDevice *_this, SDL_Window * window) bool HAIKU_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window * window) { BMessage msg(BWIN_MOVE_WINDOW); - msg.AddInt32("window-x", window->floating.x); - msg.AddInt32("window-y", window->floating.y); + msg.AddInt32("window-x", window->pending.x); + msg.AddInt32("window-y", window->pending.y); _ToBeWin(window)->PostMessage(&msg); return true; } @@ -109,8 +109,8 @@ bool HAIKU_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window * window) void HAIKU_SetWindowSize(SDL_VideoDevice *_this, SDL_Window * window) { BMessage msg(BWIN_RESIZE_WINDOW); - msg.AddInt32("window-w", window->floating.w - 1); - msg.AddInt32("window-h", window->floating.h - 1); + msg.AddInt32("window-w", window->pending.w - 1); + msg.AddInt32("window-h", window->pending.h - 1); _ToBeWin(window)->PostMessage(&msg); } diff --git a/libs/SDL3/src/video/kmsdrm/SDL_kmsdrmopengles.h b/libs/SDL3/src/video/kmsdrm/SDL_kmsdrmopengles.h index 69e6fb7d..e1c11e89 100644 --- a/libs/SDL3/src/video/kmsdrm/SDL_kmsdrmopengles.h +++ b/libs/SDL3/src/video/kmsdrm/SDL_kmsdrmopengles.h @@ -23,9 +23,6 @@ #ifndef SDL_kmsdrmopengles_h_ #define SDL_kmsdrmopengles_h_ -#ifdef SDL_VIDEO_DRIVER_KMSDRM - -#include "../SDL_sysvideo.h" #include "../SDL_egl_c.h" // OpenGLES functions @@ -37,10 +34,9 @@ extern void KMSDRM_GLES_DefaultProfileConfig(SDL_VideoDevice *_this, int *mask, int *major, int *minor); extern bool KMSDRM_GLES_SetSwapInterval(SDL_VideoDevice *_this, int interval); extern bool KMSDRM_GLES_LoadLibrary(SDL_VideoDevice *_this, const char *path); +extern void KMSDRM_GLES_UnloadLibrary(SDL_VideoDevice *_this); extern SDL_GLContext KMSDRM_GLES_CreateContext(SDL_VideoDevice *_this, SDL_Window *window); extern bool KMSDRM_GLES_SwapWindow(SDL_VideoDevice *_this, SDL_Window *window); extern bool KMSDRM_GLES_MakeCurrent(SDL_VideoDevice *_this, SDL_Window *window, SDL_GLContext context); -#endif // SDL_VIDEO_DRIVER_KMSDRM - #endif // SDL_kmsdrmopengles_h_ diff --git a/libs/SDL3/src/video/kmsdrm/SDL_kmsdrmvideo.c b/libs/SDL3/src/video/kmsdrm/SDL_kmsdrmvideo.c index ae9b6235..6c2e7a69 100644 --- a/libs/SDL3/src/video/kmsdrm/SDL_kmsdrmvideo.c +++ b/libs/SDL3/src/video/kmsdrm/SDL_kmsdrmvideo.c @@ -23,9 +23,11 @@ #ifdef SDL_VIDEO_DRIVER_KMSDRM -/* include this here before SDL_sysvideo.h to avoid vulkan type - * redefinition errors. it already includes SDL_sysvideo.h. */ -#include "SDL_kmsdrmvulkan.h" +/* Include this first, as some system headers may pull in EGL headers that + * define EGL types as native types for other enabled platforms, which can + * result in type-mismatch warnings when building with LTO. + */ +#include "../SDL_egl_c.h" // SDL internals #include "../../events/SDL_events_c.h" @@ -44,6 +46,7 @@ #include "SDL_kmsdrmmouse.h" #include "SDL_kmsdrmvideo.h" #include "SDL_kmsdrmopengles.h" +#include "SDL_kmsdrmvulkan.h" #include #include #include @@ -95,7 +98,7 @@ static int get_driindex(void) } SDL_strlcpy(device + kmsdrm_dri_pathsize, kmsdrm_dri_devname, - sizeof(device) - kmsdrm_dri_devnamesize); + sizeof(device) - kmsdrm_dri_pathsize); while((res = readdir(folder)) != NULL && available < 0) { if (SDL_memcmp(res->d_name, kmsdrm_dri_devname, kmsdrm_dri_devnamesize) == 0) { diff --git a/libs/SDL3/src/video/kmsdrm/SDL_kmsdrmvideo.h b/libs/SDL3/src/video/kmsdrm/SDL_kmsdrmvideo.h index 16571ecf..2ef3ae52 100644 --- a/libs/SDL3/src/video/kmsdrm/SDL_kmsdrmvideo.h +++ b/libs/SDL3/src/video/kmsdrm/SDL_kmsdrmvideo.h @@ -173,15 +173,4 @@ extern void KMSDRM_MinimizeWindow(SDL_VideoDevice *_this, SDL_Window *window); extern void KMSDRM_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window); extern void KMSDRM_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window); -// OpenGL/OpenGL ES functions -extern bool KMSDRM_GLES_LoadLibrary(SDL_VideoDevice *_this, const char *path); -extern SDL_FunctionPointer KMSDRM_GLES_GetProcAddress(SDL_VideoDevice *_this, const char *proc); -extern void KMSDRM_GLES_UnloadLibrary(SDL_VideoDevice *_this); -extern SDL_GLContext KMSDRM_GLES_CreateContext(SDL_VideoDevice *_this, SDL_Window *window); -extern bool KMSDRM_GLES_MakeCurrent(SDL_VideoDevice *_this, SDL_Window *window, SDL_GLContext context); -extern bool KMSDRM_GLES_SetSwapInterval(SDL_VideoDevice *_this, int interval); -extern bool KMSDRM_GLES_GetSwapInterval(SDL_VideoDevice *_this); -extern bool KMSDRM_GLES_SwapWindow(SDL_VideoDevice *_this, SDL_Window *window); -extern bool KMSDRM_GLES_DestroyContext(SDL_VideoDevice *_this, SDL_GLContext context); - #endif // SDL_kmsdrmvideo_h diff --git a/libs/SDL3/src/video/kmsdrm/SDL_kmsdrmvulkan.h b/libs/SDL3/src/video/kmsdrm/SDL_kmsdrmvulkan.h index 41ca95a7..c2cb7ffe 100644 --- a/libs/SDL3/src/video/kmsdrm/SDL_kmsdrmvulkan.h +++ b/libs/SDL3/src/video/kmsdrm/SDL_kmsdrmvulkan.h @@ -29,8 +29,7 @@ #ifndef SDL_kmsdrm_vulkan_h_ #define SDL_kmsdrm_vulkan_h_ -#include "../SDL_vulkan_internal.h" -#include "../SDL_sysvideo.h" +#include #if defined(SDL_VIDEO_VULKAN) && defined(SDL_VIDEO_DRIVER_KMSDRM) diff --git a/libs/SDL3/src/video/ngage/SDL_ngageevents.cpp b/libs/SDL3/src/video/ngage/SDL_ngageevents.cpp deleted file mode 100644 index bfe3b7fc..00000000 --- a/libs/SDL3/src/video/ngage/SDL_ngageevents.cpp +++ /dev/null @@ -1,192 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2024 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ - -#include "SDL_internal.h" - -#ifdef SDL_VIDEO_DRIVER_NGAGE - -/* Being a ngage driver, there's no event stream. We just define stubs for - most of the API. */ - -#ifdef __cplusplus -extern "C" { -#endif - -#include "../../events/SDL_events_c.h" -#include "../../events/SDL_keyboard_c.h" - -#ifdef __cplusplus -} -#endif - -#include "SDL_ngagevideo.h" -#include "SDL_ngageevents_c.h" - -static void HandleWsEvent(SDL_VideoDevice *_this, const TWsEvent &aWsEvent); - -void NGAGE_PumpEvents(SDL_VideoDevice *_this) -{ - SDL_VideoData *data = _this->internal; - - while (data->NGAGE_WsEventStatus != KRequestPending) { - data->NGAGE_WsSession.GetEvent(data->NGAGE_WsEvent); - - HandleWsEvent(_this, data->NGAGE_WsEvent); - - data->NGAGE_WsEventStatus = KRequestPending; - data->NGAGE_WsSession.EventReady(&data->NGAGE_WsEventStatus); - } -} - -/*****************************************************************************/ -// Internal -/*****************************************************************************/ - -#include -#include - -extern void DisableKeyBlocking(SDL_VideoDevice *_this); -extern void RedrawWindowL(SDL_VideoDevice *_this); - -TBool isCursorVisible = EFalse; - -static SDL_Scancode ConvertScancode(SDL_VideoDevice *_this, int key) -{ - SDL_Keycode scancode; - - switch (key) { - case EStdKeyBackspace: // Clear key - scancode = SDL_SCANCODE_BACKSPACE; - break; - case 0x31: // 1 - scancode = SDL_SCANCODE_1; - break; - case 0x32: // 2 - scancode = SDL_SCANCODE_2; - break; - case 0x33: // 3 - scancode = SDL_SCANCODE_3; - break; - case 0x34: // 4 - scancode = SDL_SCANCODE_4; - break; - case 0x35: // 5 - scancode = SDL_SCANCODE_5; - break; - case 0x36: // 6 - scancode = SDL_SCANCODE_6; - break; - case 0x37: // 7 - scancode = SDL_SCANCODE_7; - break; - case 0x38: // 8 - scancode = SDL_SCANCODE_8; - break; - case 0x39: // 9 - scancode = SDL_SCANCODE_9; - break; - case 0x30: // 0 - scancode = SDL_SCANCODE_0; - break; - case 0x2a: // Asterisk - scancode = SDL_SCANCODE_ASTERISK; - break; - case EStdKeyHash: // Hash - scancode = SDL_SCANCODE_HASH; - break; - case EStdKeyDevice0: // Left softkey - scancode = SDL_SCANCODE_SOFTLEFT; - break; - case EStdKeyDevice1: // Right softkey - scancode = SDL_SCANCODE_SOFTRIGHT; - break; - case EStdKeyApplication0: // Call softkey - scancode = SDL_SCANCODE_CALL; - break; - case EStdKeyApplication1: // End call softkey - scancode = SDL_SCANCODE_ENDCALL; - break; - case EStdKeyDevice3: // Middle softkey - scancode = SDL_SCANCODE_SELECT; - break; - case EStdKeyUpArrow: // Up arrow - scancode = SDL_SCANCODE_UP; - break; - case EStdKeyDownArrow: // Down arrow - scancode = SDL_SCANCODE_DOWN; - break; - case EStdKeyLeftArrow: // Left arrow - scancode = SDL_SCANCODE_LEFT; - break; - case EStdKeyRightArrow: // Right arrow - scancode = SDL_SCANCODE_RIGHT; - break; - default: - scancode = SDL_SCANCODE_UNKNOWN; - break; - } - - return scancode; -} - -static void HandleWsEvent(SDL_VideoDevice *_this, const TWsEvent &aWsEvent) -{ - SDL_VideoData *data = _this->internal; - - switch (aWsEvent.Type()) { - case EEventKeyDown: // Key events - SDL_SendKeyboardKey(0, SDL_GLOBAL_KEYBOARD_ID, aWsEvent.Key()->iScanCode, ConvertScancode(_this, aWsEvent.Key()->iScanCode), true); - break; - case EEventKeyUp: // Key events - SDL_SendKeyboardKey(0, SDL_GLOBAL_KEYBOARD_ID, aWsEvent.Key()->iScanCode, ConvertScancode(_this, aWsEvent.Key()->iScanCode), false); - break; - case EEventFocusGained: // SDL window got focus - data->NGAGE_IsWindowFocused = ETrue; - // Draw window background and screen buffer - DisableKeyBlocking(_this); - RedrawWindowL(_this); - break; - case EEventFocusLost: // SDL window lost focus - { - data->NGAGE_IsWindowFocused = EFalse; - RWsSession s; - s.Connect(); - RWindowGroup g(s); - g.Construct(TUint32(&g), EFalse); - g.EnableReceiptOfFocus(EFalse); - RWindow w(s); - w.Construct(g, TUint32(&w)); - w.SetExtent(TPoint(0, 0), data->NGAGE_WsWindow.Size()); - w.SetOrdinalPosition(0); - w.Activate(); - w.Close(); - g.Close(); - s.Close(); - break; - } - case EEventModifiersChanged: - break; - default: - break; - } -} - -#endif // SDL_VIDEO_DRIVER_NGAGE diff --git a/libs/SDL3/src/video/ngage/SDL_ngageframebuffer.cpp b/libs/SDL3/src/video/ngage/SDL_ngageframebuffer.cpp deleted file mode 100644 index b854c73a..00000000 --- a/libs/SDL3/src/video/ngage/SDL_ngageframebuffer.cpp +++ /dev/null @@ -1,380 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2024 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ - -#include "SDL_internal.h" - -#ifdef SDL_VIDEO_DRIVER_NGAGE - -#include - -#include "../SDL_sysvideo.h" -#include "SDL_ngagevideo.h" -#include "SDL_ngageframebuffer_c.h" - -#define NGAGE_SURFACE "NGAGE_FrameBuffer" - -/* For 12 bit screen HW. Table for fast conversion from 8 bit to 12 bit - * - * TUint16 is enough, but using TUint32 so we can use better instruction - * selection on ARMI. - */ -static TUint32 NGAGE_HWPalette_256_to_Screen[256]; - -bool GetBpp(TDisplayMode displaymode); -void DirectUpdate(SDL_VideoDevice *_this, int numrects, SDL_Rect *rects); -void DrawBackground(SDL_VideoDevice *_this); -void DirectDraw(SDL_VideoDevice *_this, int numrects, SDL_Rect *rects, TUint16 *screenBuffer); -void RedrawWindowL(SDL_VideoDevice *_this); - -bool SDL_NGAGE_CreateWindowFramebuffer(SDL_VideoDevice *_this, SDL_Window *window, SDL_PixelFormat *format, void **pixels, int *pitch) -{ - SDL_VideoData *phdata = _this->internal; - SDL_Surface *surface; - const SDL_PixelFormat surface_format = SDL_PIXELFORMAT_XRGB4444; - int w, h; - - // Free the old framebuffer surface - SDL_NGAGE_DestroyWindowFramebuffer(_this, window); - - // Create a new one - SDL_GetWindowSizeInPixels(window, &w, &h); - surface = SDL_CreateSurface(w, h, surface_format); - if (!surface) { - return false; - } - - // Save the info and return! - SDL_SetWindowData(window, NGAGE_SURFACE, surface); - *format = surface_format; - *pixels = surface->pixels; - *pitch = surface->pitch; - - // Initialise Epoc frame buffer - - TDisplayMode displayMode = phdata->NGAGE_WsScreen->DisplayMode(); - - TScreenInfoV01 screenInfo; - TPckg sInfo(screenInfo); - UserSvr::ScreenInfo(sInfo); - - phdata->NGAGE_ScreenSize = screenInfo.iScreenSize; - phdata->NGAGE_DisplayMode = displayMode; - phdata->NGAGE_HasFrameBuffer = screenInfo.iScreenAddressValid; - phdata->NGAGE_FrameBuffer = phdata->NGAGE_HasFrameBuffer ? (TUint8 *)screenInfo.iScreenAddress : NULL; - phdata->NGAGE_BytesPerPixel = ((GetBpp(displayMode) - 1) / 8) + 1; - - phdata->NGAGE_BytesPerScanLine = screenInfo.iScreenSize.iWidth * phdata->NGAGE_BytesPerPixel; - phdata->NGAGE_BytesPerScreen = phdata->NGAGE_BytesPerScanLine * phdata->NGAGE_ScreenSize.iHeight; - - SDL_Log("Screen width %d", screenInfo.iScreenSize.iWidth); - SDL_Log("Screen height %d", screenInfo.iScreenSize.iHeight); - SDL_Log("Screen dmode %d", displayMode); - SDL_Log("Screen valid %d", screenInfo.iScreenAddressValid); - - SDL_Log("Bytes per pixel %d", phdata->NGAGE_BytesPerPixel); - SDL_Log("Bytes per scan line %d", phdata->NGAGE_BytesPerScanLine); - SDL_Log("Bytes per screen %d", phdata->NGAGE_BytesPerScreen); - - /* It seems that in SA1100 machines for 8bpp displays there is a 512 - * palette table at the beginning of the frame buffer. - * - * In 12 bpp machines the table has 16 entries. - */ - if (phdata->NGAGE_HasFrameBuffer && GetBpp(displayMode) == 8) { - phdata->NGAGE_FrameBuffer += 512; - } else { - phdata->NGAGE_FrameBuffer += 32; - } -#if 0 - if (phdata->NGAGE_HasFrameBuffer && GetBpp(displayMode) == 12) { - phdata->NGAGE_FrameBuffer += 16 * 2; - } - if (phdata->NGAGE_HasFrameBuffer && GetBpp(displayMode) == 16) { - phdata->NGAGE_FrameBuffer += 16 * 2; - } -#endif - - // Get draw device for updating the screen - TScreenInfoV01 screenInfo2; - - NGAGE_Runtime::GetScreenInfo(screenInfo2); - - TRAPD(status, phdata->NGAGE_DrawDevice = CFbsDrawDevice::NewScreenDeviceL(screenInfo2, displayMode)); - User::LeaveIfError(status); - - // Activate events for me - phdata->NGAGE_WsEventStatus = KRequestPending; - phdata->NGAGE_WsSession.EventReady(&phdata->NGAGE_WsEventStatus); - - SDL_Log("SDL:WsEventStatus"); - User::WaitForRequest(phdata->NGAGE_WsEventStatus); - - phdata->NGAGE_RedrawEventStatus = KRequestPending; - phdata->NGAGE_WsSession.RedrawReady(&phdata->NGAGE_RedrawEventStatus); - - SDL_Log("SDL:RedrawEventStatus"); - User::WaitForRequest(phdata->NGAGE_RedrawEventStatus); - - phdata->NGAGE_WsWindow.PointerFilter(EPointerFilterDrag, 0); - - phdata->NGAGE_ScreenOffset = TPoint(0, 0); - - SDL_Log("SDL:DrawBackground"); - DrawBackground(_this); // Clear screen - - return true; -} - -bool SDL_NGAGE_UpdateWindowFramebuffer(SDL_VideoDevice *_this, SDL_Window *window, const SDL_Rect *rects, int numrects) -{ - DirectUpdate(_this, numrects, (SDL_Rect *)rects); - return true; -} - -void SDL_NGAGE_DestroyWindowFramebuffer(SDL_VideoDevice *_this, SDL_Window *window) -{ - SDL_Surface *surface; - - surface = (SDL_Surface *)SDL_SetWindowData(window, NGAGE_SURFACE, NULL); - SDL_DestroySurface(surface); -} - -/*****************************************************************************/ -// Runtime -/*****************************************************************************/ - -#include -#include -#include - -EXPORT_C void NGAGE_Runtime::GetScreenInfo(TScreenInfoV01 &screenInfo2) -{ - TPckg sInfo2(screenInfo2); - UserSvr::ScreenInfo(sInfo2); -} - -/*****************************************************************************/ -// Internal -/*****************************************************************************/ - -bool GetBpp(TDisplayMode displaymode) -{ - return TDisplayModeUtils::NumDisplayModeBitsPerPixel(displaymode); -} - -void DrawBackground(SDL_VideoDevice *_this) -{ - SDL_VideoData *phdata = _this->internal; - // Draw background - TUint16 *screenBuffer = (TUint16 *)phdata->NGAGE_FrameBuffer; - // Draw black background - Mem::FillZ(screenBuffer, phdata->NGAGE_BytesPerScreen); -} - -void DirectDraw(SDL_VideoDevice *_this, int numrects, SDL_Rect *rects, TUint16 *screenBuffer) -{ - SDL_VideoData *phdata = _this->internal; - SDL_Surface *screen = (SDL_Surface *)SDL_GetWindowData(_this->windows, NGAGE_SURFACE); - - TInt i; - - TDisplayMode displayMode = phdata->NGAGE_DisplayMode; - const TInt sourceNumBytesPerPixel = ((GetBpp(displayMode) - 1) / 8) + 1; - - const TPoint fixedOffset = phdata->NGAGE_ScreenOffset; - const TInt screenW = screen->w; - const TInt screenH = screen->h; - const TInt sourceScanlineLength = screenW; - const TInt targetScanlineLength = phdata->NGAGE_ScreenSize.iWidth; - - // Render the rectangles in the list - - for (i = 0; i < numrects; ++i) { - const SDL_Rect ¤tRect = rects[i]; - SDL_Rect rect2; - rect2.x = currentRect.x; - rect2.y = currentRect.y; - rect2.w = currentRect.w; - rect2.h = currentRect.h; - - if (rect2.w <= 0 || rect2.h <= 0) /* Sanity check */ { - continue; - } - - // All variables are measured in pixels - - // Check rects validity, i.e. upper and lower bounds - TInt maxX = Min(screenW - 1, rect2.x + rect2.w - 1); - TInt maxY = Min(screenH - 1, rect2.y + rect2.h - 1); - if (maxX < 0 || maxY < 0) /* sanity check */ { - continue; - } - // Clip from bottom - - maxY = Min(maxY, phdata->NGAGE_ScreenSize.iHeight - 1); - // TODO: Clip from the right side - - const TInt sourceRectWidth = maxX - rect2.x + 1; - const TInt sourceRectWidthInBytes = sourceRectWidth * sourceNumBytesPerPixel; - const TInt sourceRectHeight = maxY - rect2.y + 1; - const TInt sourceStartOffset = rect2.x + rect2.y * sourceScanlineLength; - const TUint skipValue = 1; // 1 = No skip - - TInt targetStartOffset = fixedOffset.iX + rect2.x + (fixedOffset.iY + rect2.y) * targetScanlineLength; - - switch (screen->format->bits_per_pixel) { - case 12: - { - TUint16 *bitmapLine = (TUint16 *)screen->pixels + sourceStartOffset; - TUint16 *screenMemory = screenBuffer + targetStartOffset; - - if (skipValue == 1) { - for (TInt y = 0; y < sourceRectHeight; y++) { - Mem::Copy(screenMemory, bitmapLine, sourceRectWidthInBytes); - bitmapLine += sourceScanlineLength; - screenMemory += targetScanlineLength; - } - } else { - for (TInt y = 0; y < sourceRectHeight; y++) { - TUint16 *bitmapPos = bitmapLine; // 2 bytes per pixel - TUint16 *screenMemoryLinePos = screenMemory; // 2 bytes per pixel - for (TInt x = 0; x < sourceRectWidth; x++) { - __ASSERT_DEBUG(screenMemory < (screenBuffer + phdata->NGAGE_ScreenSize.iWidth * phdata->NGAGE_ScreenSize.iHeight), User::Panic(_L("SDL"), KErrCorrupt)); - __ASSERT_DEBUG(screenMemory >= screenBuffer, User::Panic(_L("SDL"), KErrCorrupt)); - __ASSERT_DEBUG(bitmapLine < ((TUint16 *)screen->pixels + (screen->w * screen->h)), User::Panic(_L("SDL"), KErrCorrupt)); - __ASSERT_DEBUG(bitmapLine >= (TUint16 *)screen->pixels, User::Panic(_L("SDL"), KErrCorrupt)); - - *screenMemoryLinePos++ = *bitmapPos; - bitmapPos += skipValue; - } - bitmapLine += sourceScanlineLength; - screenMemory += targetScanlineLength; - } - } - } break; - // 256 color paletted mode: 8 bpp --> 12 bpp - default: - { - if (phdata->NGAGE_BytesPerPixel <= 2) { - TUint8 *bitmapLine = (TUint8 *)screen->pixels + sourceStartOffset; - TUint16 *screenMemory = screenBuffer + targetStartOffset; - - for (TInt y = 0; y < sourceRectHeight; y++) { - TUint8 *bitmapPos = bitmapLine; // 1 byte per pixel - TUint16 *screenMemoryLinePos = screenMemory; // 2 bytes per pixel - // Convert each pixel from 256 palette to 4k color values - for (TInt x = 0; x < sourceRectWidth; x++) { - __ASSERT_DEBUG(screenMemoryLinePos < (screenBuffer + (phdata->NGAGE_ScreenSize.iWidth * phdata->NGAGE_ScreenSize.iHeight)), User::Panic(_L("SDL"), KErrCorrupt)); - __ASSERT_DEBUG(screenMemoryLinePos >= screenBuffer, User::Panic(_L("SDL"), KErrCorrupt)); - __ASSERT_DEBUG(bitmapPos < ((TUint8 *)screen->pixels + (screen->w * screen->h)), User::Panic(_L("SDL"), KErrCorrupt)); - __ASSERT_DEBUG(bitmapPos >= (TUint8 *)screen->pixels, User::Panic(_L("SDL"), KErrCorrupt)); - *screenMemoryLinePos++ = NGAGE_HWPalette_256_to_Screen[*bitmapPos++]; - } - bitmapLine += sourceScanlineLength; - screenMemory += targetScanlineLength; - } - } else { - TUint8 *bitmapLine = (TUint8 *)screen->pixels + sourceStartOffset; - TUint32 *screenMemory = reinterpret_cast(screenBuffer + targetStartOffset); - for (TInt y = 0; y < sourceRectHeight; y++) { - TUint8 *bitmapPos = bitmapLine; // 1 byte per pixel - TUint32 *screenMemoryLinePos = screenMemory; // 2 bytes per pixel - // Convert each pixel from 256 palette to 4k color values - for (TInt x = 0; x < sourceRectWidth; x++) { - __ASSERT_DEBUG(screenMemoryLinePos < (reinterpret_cast(screenBuffer) + (phdata->NGAGE_ScreenSize.iWidth * phdata->NGAGE_ScreenSize.iHeight)), User::Panic(_L("SDL"), KErrCorrupt)); - __ASSERT_DEBUG(screenMemoryLinePos >= reinterpret_cast(screenBuffer), User::Panic(_L("SDL"), KErrCorrupt)); - __ASSERT_DEBUG(bitmapPos < ((TUint8 *)screen->pixels + (screen->w * screen->h)), User::Panic(_L("SDL"), KErrCorrupt)); - __ASSERT_DEBUG(bitmapPos >= (TUint8 *)screen->pixels, User::Panic(_L("SDL"), KErrCorrupt)); - *screenMemoryLinePos++ = NGAGE_HWPalette_256_to_Screen[*bitmapPos++]; - } - bitmapLine += sourceScanlineLength; - screenMemory += targetScanlineLength; - } - } - } - } - } -} - -void DirectUpdate(SDL_VideoDevice *_this, int numrects, SDL_Rect *rects) -{ - SDL_VideoData *phdata = _this->internal; - - if (!phdata->NGAGE_IsWindowFocused) { - SDL_PauseAudio(1); - SDL_Delay(1000); - return; - } - - SDL_PauseAudio(0); - - TUint16 *screenBuffer = (TUint16 *)phdata->NGAGE_FrameBuffer; - -#if 0 - if (phdata->NGAGE_ScreenOrientation == CFbsBitGc::EGraphicsOrientationRotated270) { - // ... - } else -#endif - { - DirectDraw(_this, numrects, rects, screenBuffer); - } - - for (int i = 0; i < numrects; ++i) { - TInt aAx = rects[i].x; - TInt aAy = rects[i].y; - TInt aBx = rects[i].w; - TInt aBy = rects[i].h; - TRect rect2 = TRect(aAx, aAy, aBx, aBy); - - phdata->NGAGE_DrawDevice->UpdateRegion(rect2); // Should we update rects parameter area only? - phdata->NGAGE_DrawDevice->Update(); - } -} - -void RedrawWindowL(SDL_VideoDevice *_this) -{ - SDL_VideoData *phdata = _this->internal; - SDL_Surface *screen = (SDL_Surface *)SDL_GetWindowData(_this->windows, NGAGE_SURFACE); - - int w = screen->w; - int h = screen->h; - if (phdata->NGAGE_ScreenOrientation == CFbsBitGc::EGraphicsOrientationRotated270) { - w = screen->h; - h = screen->w; - } - if ((w < phdata->NGAGE_ScreenSize.iWidth) || (h < phdata->NGAGE_ScreenSize.iHeight)) { - DrawBackground(_this); - } - - // Tell the system that something has been drawn - TRect rect = TRect(phdata->NGAGE_WsWindow.Size()); - phdata->NGAGE_WsWindow.Invalidate(rect); - - // Draw current buffer - SDL_Rect fullScreen; - fullScreen.x = 0; - fullScreen.y = 0; - fullScreen.w = screen->w; - fullScreen.h = screen->h; - DirectUpdate(_this, 1, &fullScreen); -} - -#endif // SDL_VIDEO_DRIVER_NGAGE diff --git a/libs/SDL3/src/video/ngage/SDL_ngageframebuffer_c.h b/libs/SDL3/src/video/ngage/SDL_ngageframebuffer_c.h deleted file mode 100644 index 578cc568..00000000 --- a/libs/SDL3/src/video/ngage/SDL_ngageframebuffer_c.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2024 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ - -#include "SDL_internal.h" - -extern bool SDL_NGAGE_CreateWindowFramebuffer(SDL_VideoDevice *_this, SDL_Window *window, SDL_PixelFormat *format, void **pixels, int *pitch); -extern bool SDL_NGAGE_UpdateWindowFramebuffer(SDL_VideoDevice *_this, SDL_Window *window, const SDL_Rect *rects, int numrects); -extern void SDL_NGAGE_DestroyWindowFramebuffer(SDL_VideoDevice *_this, SDL_Window *window); - -/****************************************************************************/ -// Runtime -/****************************************************************************/ - -class NGAGE_Runtime -{ - public: - IMPORT_C static void GetScreenInfo(TScreenInfoV01 &screenInfo2); -}; diff --git a/libs/SDL3/src/video/ngage/SDL_ngagevideo.cpp b/libs/SDL3/src/video/ngage/SDL_ngagevideo.cpp deleted file mode 100644 index 1694a9d7..00000000 --- a/libs/SDL3/src/video/ngage/SDL_ngagevideo.cpp +++ /dev/null @@ -1,164 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2024 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ - -#include -#ifdef NULL -#undef NULL -#endif -#include "SDL_internal.h" - -#ifdef SDL_VIDEO_DRIVER_NGAGE - -#ifdef __cplusplus -extern "C" { -#endif - -#include "../SDL_sysvideo.h" -#include "../SDL_pixels_c.h" -#include "../../events/SDL_events_c.h" - -#ifdef __cplusplus -} -#endif - -#include "SDL_ngagevideo.h" -#include "SDL_ngagewindow.h" -#include "SDL_ngageevents_c.h" -#include "SDL_ngageframebuffer_c.h" - -#define NGAGEVID_DRIVER_NAME "ngage" - -// Initialization/Query functions -static bool NGAGE_VideoInit(SDL_VideoDevice *_this); -static void NGAGE_VideoQuit(SDL_VideoDevice *_this); - -// NGAGE driver bootstrap functions - -static void NGAGE_DeleteDevice(SDL_VideoDevice *device) -{ - SDL_VideoData *phdata = device->internal; - - if (phdata) { - // Free Epoc resources - - // Disable events for me - if (phdata->NGAGE_WsEventStatus != KRequestPending) { - phdata->NGAGE_WsSession.EventReadyCancel(); - } - if (phdata->NGAGE_RedrawEventStatus != KRequestPending) { - phdata->NGAGE_WsSession.RedrawReadyCancel(); - } - - free(phdata->NGAGE_DrawDevice); // This should NOT be SDL_free() - - if (phdata->NGAGE_WsWindow.WsHandle()) { - phdata->NGAGE_WsWindow.Close(); - } - - if (phdata->NGAGE_WsWindowGroup.WsHandle()) { - phdata->NGAGE_WsWindowGroup.Close(); - } - - delete phdata->NGAGE_WindowGc; - phdata->NGAGE_WindowGc = NULL; - - delete phdata->NGAGE_WsScreen; - phdata->NGAGE_WsScreen = NULL; - - if (phdata->NGAGE_WsSession.WsHandle()) { - phdata->NGAGE_WsSession.Close(); - } - - SDL_free(phdata); - phdata = NULL; - } - - if (device) { - SDL_free(device); - device = NULL; - } -} - -static SDL_VideoDevice *NGAGE_CreateDevice(void) -{ - SDL_VideoDevice *device; - SDL_VideoData *phdata; - - // Initialize all variables that we clean on shutdown - device = (SDL_VideoDevice *)SDL_calloc(1, sizeof(SDL_VideoDevice)); - if (!device) { - return NULL; - } - - // Initialize internal N-Gage specific data - phdata = (SDL_VideoData *)SDL_calloc(1, sizeof(SDL_VideoData)); - if (!phdata) { - SDL_free(device); - return NULL; - } - - // General video - device->VideoInit = NGAGE_VideoInit; - device->VideoQuit = NGAGE_VideoQuit; - device->PumpEvents = NGAGE_PumpEvents; - device->CreateWindowFramebuffer = SDL_NGAGE_CreateWindowFramebuffer; - device->UpdateWindowFramebuffer = SDL_NGAGE_UpdateWindowFramebuffer; - device->DestroyWindowFramebuffer = SDL_NGAGE_DestroyWindowFramebuffer; - device->free = NGAGE_DeleteDevice; - - // "Window" - device->CreateSDLWindow = NGAGE_CreateWindow; - device->DestroyWindow = NGAGE_DestroyWindow; - - // N-Gage specific data - device->internal = phdata; - - return device; -} - -VideoBootStrap NGAGE_bootstrap = { - NGAGEVID_DRIVER_NAME, "SDL ngage video driver", - NGAGE_CreateDevice, - NULL // no ShowMessageBox implementation -}; - -static bool NGAGE_VideoInit(SDL_VideoDevice *_this) -{ - SDL_DisplayMode mode; - - // Use 12-bpp desktop mode - SDL_zero(mode); - mode.format = SDL_PIXELFORMAT_XRGB4444; - mode.w = 176; - mode.h = 208; - if (SDL_AddBasicVideoDisplay(&mode) == 0) { - return false; - } - - // We're done! - return true; -} - -static void NGAGE_VideoQuit(SDL_VideoDevice *_this) -{ -} - -#endif // SDL_VIDEO_DRIVER_NGAGE diff --git a/libs/SDL3/src/video/ngage/SDL_ngagevideo.h b/libs/SDL3/src/video/ngage/SDL_ngagevideo.h deleted file mode 100644 index 8f1b07b7..00000000 --- a/libs/SDL3/src/video/ngage/SDL_ngagevideo.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2024 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ - -#include "SDL_internal.h" - -#ifndef SDL_ngagevideo_h -#define SDL_ngagevideo_h - -#include "../SDL_sysvideo.h" - -#include -#include -#include -#include -#include "bitdraw.h" // CFbsDrawDevice - -#define SDL_VideoDevice *_this SDL_VideoDevice *_this - -struct SDL_VideoData -{ - // Epoc window server info - RWsSession NGAGE_WsSession; - RWindowGroup NGAGE_WsWindowGroup; - TInt NGAGE_WsWindowGroupID; - RWindow NGAGE_WsWindow; - CWsScreenDevice *NGAGE_WsScreen; - CWindowGc *NGAGE_WindowGc; - TRequestStatus NGAGE_WsEventStatus; - TRequestStatus NGAGE_RedrawEventStatus; - TWsEvent NGAGE_WsEvent; - CFbsDrawDevice *NGAGE_DrawDevice; - TBool NGAGE_IsWindowFocused; // Not used yet - - // Screen hardware frame buffer info - TBool NGAGE_HasFrameBuffer; - TInt NGAGE_BytesPerPixel; - TInt NGAGE_BytesPerScanLine; - TInt NGAGE_BytesPerScreen; - TDisplayMode NGAGE_DisplayMode; - TSize NGAGE_ScreenSize; - TUint8 *NGAGE_FrameBuffer; - TPoint NGAGE_ScreenOffset; - - CFbsBitGc::TGraphicsOrientation NGAGE_ScreenOrientation; -}; - -#endif // SDL_ngagevideo_h diff --git a/libs/SDL3/src/video/ngage/SDL_ngagewindow.cpp b/libs/SDL3/src/video/ngage/SDL_ngagewindow.cpp deleted file mode 100644 index f916e1fa..00000000 --- a/libs/SDL3/src/video/ngage/SDL_ngagewindow.cpp +++ /dev/null @@ -1,125 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2024 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ - -#include "SDL_internal.h" - -#ifdef SDL_VIDEO_DRIVER_NGAGE - -#include "../SDL_sysvideo.h" - -#include "SDL_ngagewindow.h" - -const TUint32 WindowClientHandle = 9210; - -void DisableKeyBlocking(SDL_VideoDevice *_this); -void ConstructWindowL(SDL_VideoDevice *_this); - -bool NGAGE_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID create_props) -{ - NGAGE_Window *ngage_window = (NGAGE_Window *)SDL_calloc(1, sizeof(NGAGE_Window)); - - if (!ngage_window) { - return false; - } - - window->internal = ngage_window; - - if (window->x == SDL_WINDOWPOS_UNDEFINED) { - window->x = 0; - } - - if (window->y == SDL_WINDOWPOS_UNDEFINED) { - window->y = 0; - } - - ngage_window->sdl_window = window; - - ConstructWindowL(_this); - - return true; -} - -void NGAGE_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window) -{ - NGAGE_Window *ngage_window = (NGAGE_Window *)window->internal; - - if (ngage_window) { - SDL_free(ngage_window); - } - - window->internal = NULL; -} - -/*****************************************************************************/ -// Internal -/*****************************************************************************/ - -void DisableKeyBlocking(SDL_VideoDevice *_this) -{ - SDL_VideoData *phdata = _this->internal; - TRawEvent event; - - event.Set((TRawEvent::TType) /*EDisableKeyBlock*/ 51); - phdata->NGAGE_WsSession.SimulateRawEvent(event); -} - -void ConstructWindowL(SDL_VideoDevice *_this) -{ - SDL_VideoData *phdata = _this->internal; - TInt error; - - error = phdata->NGAGE_WsSession.Connect(); - User::LeaveIfError(error); - phdata->NGAGE_WsScreen = new (ELeave) CWsScreenDevice(phdata->NGAGE_WsSession); - User::LeaveIfError(phdata->NGAGE_WsScreen->Construct()); - User::LeaveIfError(phdata->NGAGE_WsScreen->CreateContext(phdata->NGAGE_WindowGc)); - - phdata->NGAGE_WsWindowGroup = RWindowGroup(phdata->NGAGE_WsSession); - User::LeaveIfError(phdata->NGAGE_WsWindowGroup.Construct(WindowClientHandle)); - phdata->NGAGE_WsWindowGroup.SetOrdinalPosition(0); - - RProcess thisProcess; - TParse exeName; - exeName.Set(thisProcess.FileName(), NULL, NULL); - TBuf<32> winGroupName; - winGroupName.Append(0); - winGroupName.Append(0); - winGroupName.Append(0); // UID - winGroupName.Append(0); - winGroupName.Append(exeName.Name()); // Caption - winGroupName.Append(0); - winGroupName.Append(0); // DOC name - phdata->NGAGE_WsWindowGroup.SetName(winGroupName); - - phdata->NGAGE_WsWindow = RWindow(phdata->NGAGE_WsSession); - User::LeaveIfError(phdata->NGAGE_WsWindow.Construct(phdata->NGAGE_WsWindowGroup, WindowClientHandle - 1)); - phdata->NGAGE_WsWindow.SetBackgroundColor(KRgbWhite); - phdata->NGAGE_WsWindow.Activate(); - phdata->NGAGE_WsWindow.SetSize(phdata->NGAGE_WsScreen->SizeInPixels()); - phdata->NGAGE_WsWindow.SetVisible(ETrue); - - phdata->NGAGE_WsWindowGroupID = phdata->NGAGE_WsWindowGroup.Identifier(); - phdata->NGAGE_IsWindowFocused = EFalse; - - DisableKeyBlocking(_this); -} - -#endif // SDL_VIDEO_DRIVER_NGAGE diff --git a/libs/SDL3/src/video/offscreen/SDL_offscreenwindow.c b/libs/SDL3/src/video/offscreen/SDL_offscreenwindow.c index c76028e4..9abc85cf 100644 --- a/libs/SDL3/src/video/offscreen/SDL_offscreenwindow.c +++ b/libs/SDL3/src/video/offscreen/SDL_offscreenwindow.c @@ -85,6 +85,6 @@ void OFFSCREEN_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window) void OFFSCREEN_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window) { - SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, window->floating.w, window->floating.h); + SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, window->pending.w, window->pending.h); } #endif // SDL_VIDEO_DRIVER_OFFSCREEN diff --git a/libs/SDL3/src/video/openvr/SDL_openvrvideo.c b/libs/SDL3/src/video/openvr/SDL_openvrvideo.c index eafd71ac..271ce93c 100644 --- a/libs/SDL3/src/video/openvr/SDL_openvrvideo.c +++ b/libs/SDL3/src/video/openvr/SDL_openvrvideo.c @@ -1138,12 +1138,12 @@ static void OPENVR_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window) { SDL_VideoData *data = (SDL_VideoData *)_this->internal; - if (window->floating.w != window->w) { - window->w = window->floating.w; + if (window->pending.w != window->w) { + window->w = window->pending.w; } - if (window->floating.h != window->h) { - window->h = window->floating.h; + if (window->pending.h != window->h) { + window->h = window->pending.h; } if (data->targh != window->h || data->targw != window->w) { @@ -1210,9 +1210,11 @@ static bool OPENVR_GL_SwapWindow(SDL_VideoDevice *_this, SDL_Window *window) static void OPENVR_HandleMouse(float x, float y, int btn, int evt) { if (evt == 2) { - SDL_SendMouseMotion(0, 0, 0, false, (int)x, (int)y); + SDL_SendMouseMotion(0, NULL, SDL_GLOBAL_MOUSE_ID, false, x, y); } else { - SDL_SendMouseButton(0, 0, 0, btn + 1, (evt != 0)); + const Uint8 button = SDL_BUTTON_LEFT + btn; + const bool down = (evt != 0); + SDL_SendMouseButton(0, NULL, SDL_GLOBAL_MOUSE_ID, button, down); } } diff --git a/libs/SDL3/src/video/openvr/openvr_capi.h b/libs/SDL3/src/video/openvr/openvr_capi.h index 7130422e..a772626c 100644 --- a/libs/SDL3/src/video/openvr/openvr_capi.h +++ b/libs/SDL3/src/video/openvr/openvr_capi.h @@ -1,4 +1,33 @@ -//======= Copyright (c) Valve Corporation, All rights reserved. =============== +/* + Copyright (c) 2015, Valve Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + 3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +//============================================================================= // // Purpose: Header for flatted SteamAPI. Use this for binding to other languages. // This file is auto-generated, do not edit it. diff --git a/libs/SDL3/src/video/qnx/SDL_qnxvideo.c b/libs/SDL3/src/video/qnx/SDL_qnxvideo.c index 6e7d5dc8..db231439 100644 --- a/libs/SDL3/src/video/qnx/SDL_qnxvideo.c +++ b/libs/SDL3/src/video/qnx/SDL_qnxvideo.c @@ -246,8 +246,8 @@ static void setWindowSize(SDL_VideoDevice *_this, SDL_Window *window) window_impl_t *impl = (window_impl_t *)window->internal; int size[2]; - size[0] = window->floating.w; - size[1] = window->floating.h; + size[0] = window->pending.w; + size[1] = window->pending.h; screen_set_window_property_iv(impl->window, SCREEN_PROPERTY_SIZE, size); screen_set_window_property_iv(impl->window, SCREEN_PROPERTY_SOURCE_SIZE, size); diff --git a/libs/SDL3/src/video/sdlgenblit.pl b/libs/SDL3/src/video/sdlgenblit.pl index 52d470d1..25ef5d76 100644 --- a/libs/SDL3/src/video/sdlgenblit.pl +++ b/libs/SDL3/src/video/sdlgenblit.pl @@ -114,7 +114,7 @@ sub open_file { #include "SDL_internal.h" #include "SDL_surface_c.h" -#if SDL_HAVE_BLIT_AUTO +#ifdef SDL_HAVE_BLIT_AUTO /* *INDENT-OFF* */ // clang-format off diff --git a/libs/SDL3/src/video/uikit/SDL_uikitevents.m b/libs/SDL3/src/video/uikit/SDL_uikitevents.m index e3f9450d..8e34129b 100644 --- a/libs/SDL3/src/video/uikit/SDL_uikitevents.m +++ b/libs/SDL3/src/video/uikit/SDL_uikitevents.m @@ -187,7 +187,8 @@ static void OnGCKeyboardConnected(GCKeyboard *keyboard) API_AVAILABLE(macos(11.0 SDL_AddKeyboard(keyboardID, NULL, true); keyboard.keyboardInput.keyChangedHandler = ^(GCKeyboardInput *kbrd, GCControllerButtonInput *key, GCKeyCode keyCode, BOOL pressed) { - SDL_SendKeyboardKey(0, keyboardID, 0, (SDL_Scancode)keyCode, pressed); + Uint64 timestamp = SDL_GetTicksNS(); + SDL_SendKeyboardKey(timestamp, keyboardID, 0, (SDL_Scancode)keyCode, pressed); }; dispatch_queue_t queue = dispatch_queue_create("org.libsdl.input.keyboard", DISPATCH_QUEUE_SERIAL); @@ -318,7 +319,8 @@ static bool SetGCMouseRelativeMode(bool enabled) static void OnGCMouseButtonChanged(SDL_MouseID mouseID, Uint8 button, BOOL pressed) { - SDL_SendMouseButton(0, SDL_GetMouseFocus(), mouseID, button, pressed); + Uint64 timestamp = SDL_GetTicksNS(); + SDL_SendMouseButton(timestamp, SDL_GetMouseFocus(), mouseID, button, pressed); } static void OnGCMouseConnected(GCMouse *mouse) API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) @@ -346,12 +348,16 @@ static void OnGCMouseConnected(GCMouse *mouse) API_AVAILABLE(macos(11.0), ios(14 } mouse.mouseInput.mouseMovedHandler = ^(GCMouseInput *mouseInput, float deltaX, float deltaY) { - if (SDL_GCMouseRelativeMode()) { - SDL_SendMouseMotion(0, SDL_GetMouseFocus(), mouseID, 1, deltaX, -deltaY); - } + Uint64 timestamp = SDL_GetTicksNS(); + + if (SDL_GCMouseRelativeMode()) { + SDL_SendMouseMotion(timestamp, SDL_GetMouseFocus(), mouseID, true, deltaX, -deltaY); + } }; mouse.mouseInput.scroll.valueChangedHandler = ^(GCControllerDirectionPad *dpad, float xValue, float yValue) { + Uint64 timestamp = SDL_GetTicksNS(); + /* Raw scroll values come in here, vertical values in the first axis, horizontal values in the second axis. * The vertical values are negative moving the mouse wheel up and positive moving it down. * The horizontal values are negative moving the mouse wheel left and positive moving it right. @@ -359,12 +365,13 @@ static void OnGCMouseConnected(GCMouse *mouse) API_AVAILABLE(macos(11.0), ios(14 */ float vertical = -xValue; float horizontal = yValue; + if (mouse_scroll_direction == SDL_MOUSEWHEEL_FLIPPED) { // Since these are raw values, we need to flip them ourselves vertical = -vertical; horizontal = -horizontal; } - SDL_SendMouseWheel(0, SDL_GetMouseFocus(), mouseID, horizontal, vertical, mouse_scroll_direction); + SDL_SendMouseWheel(timestamp, SDL_GetMouseFocus(), mouseID, horizontal, vertical, mouse_scroll_direction); }; UpdateScrollDirection(); diff --git a/libs/SDL3/src/video/uikit/SDL_uikitvideo.m b/libs/SDL3/src/video/uikit/SDL_uikitvideo.m index 4866de97..de134147 100644 --- a/libs/SDL3/src/video/uikit/SDL_uikitvideo.m +++ b/libs/SDL3/src/video/uikit/SDL_uikitvideo.m @@ -97,8 +97,9 @@ static SDL_VideoDevice *UIKit_CreateDevice(void) #ifdef SDL_IPHONE_KEYBOARD device->HasScreenKeyboardSupport = UIKit_HasScreenKeyboardSupport; - device->ShowScreenKeyboard = UIKit_ShowScreenKeyboard; - device->HideScreenKeyboard = UIKit_HideScreenKeyboard; + device->StartTextInput = UIKit_StartTextInput; + device->StopTextInput = UIKit_StopTextInput; + device->SetTextInputProperties = UIKit_SetTextInputProperties; device->IsScreenKeyboardShown = UIKit_IsScreenKeyboardShown; device->UpdateTextInputArea = UIKit_UpdateTextInputArea; #endif diff --git a/libs/SDL3/src/video/uikit/SDL_uikitviewcontroller.h b/libs/SDL3/src/video/uikit/SDL_uikitviewcontroller.h index b66258c1..997dc977 100644 --- a/libs/SDL3/src/video/uikit/SDL_uikitviewcontroller.h +++ b/libs/SDL3/src/video/uikit/SDL_uikitviewcontroller.h @@ -69,8 +69,8 @@ #endif #ifdef SDL_IPHONE_KEYBOARD -- (void)showKeyboard; -- (void)hideKeyboard; +- (bool)startTextInput; +- (bool)stopTextInput; - (void)initKeyboard; - (void)deinitKeyboard; @@ -79,7 +79,7 @@ - (void)updateKeyboard; -@property(nonatomic, assign, getter=isKeyboardVisible) BOOL keyboardVisible; +@property(nonatomic, assign, getter=isTextFieldFocused) BOOL textFieldFocused; @property(nonatomic, assign) SDL_Rect textInputRect; @property(nonatomic, assign) int keyboardHeight; #endif @@ -88,8 +88,9 @@ #ifdef SDL_IPHONE_KEYBOARD bool UIKit_HasScreenKeyboardSupport(SDL_VideoDevice *_this); -void UIKit_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props); -void UIKit_HideScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window); +bool UIKit_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props); +bool UIKit_StopTextInput(SDL_VideoDevice *_this, SDL_Window *window); +void UIKit_SetTextInputProperties(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props); bool UIKit_IsScreenKeyboardShown(SDL_VideoDevice *_this, SDL_Window *window); bool UIKit_UpdateTextInputArea(SDL_VideoDevice *_this, SDL_Window *window); #endif diff --git a/libs/SDL3/src/video/uikit/SDL_uikitviewcontroller.m b/libs/SDL3/src/video/uikit/SDL_uikitviewcontroller.m index 57f4888d..c4345578 100644 --- a/libs/SDL3/src/video/uikit/SDL_uikitviewcontroller.m +++ b/libs/SDL3/src/video/uikit/SDL_uikitviewcontroller.m @@ -80,8 +80,6 @@ static void SDLCALL SDL_HideHomeIndicatorHintChanged(void *userdata, const char #ifdef SDL_IPHONE_KEYBOARD SDLUITextField *textField; - BOOL hardwareKeyboard; - BOOL showingKeyboard; BOOL hidingKeyboard; BOOL rotatingOrientation; NSString *committedText; @@ -98,8 +96,6 @@ static void SDLCALL SDL_HideHomeIndicatorHintChanged(void *userdata, const char #ifdef SDL_IPHONE_KEYBOARD [self initKeyboard]; - hardwareKeyboard = NO; - showingKeyboard = NO; hidingKeyboard = NO; rotatingOrientation = NO; #endif @@ -115,6 +111,19 @@ static void SDLCALL SDL_HideHomeIndicatorHintChanged(void *userdata, const char SDL_HideHomeIndicatorHintChanged, (__bridge void *)self); #endif + + // Enable high refresh rates on iOS + // To enable this on phones, you should add the following line to Info.plist: + // CADisableMinimumFrameDurationOnPhone + if (@available(iOS 15.0, tvOS 15.0, *)) { + const SDL_DisplayMode *mode = SDL_GetDesktopDisplayMode(SDL_GetPrimaryDisplay()); + if (mode && mode->refresh_rate > 60.0f) { + int frame_rate = (int)mode->refresh_rate; + displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(doLoop:)]; + displayLink.preferredFrameRateRange = CAFrameRateRangeMake((frame_rate * 2) / 3, frame_rate, frame_rate); + [displayLink addToRunLoop:NSRunLoop.currentRunLoop forMode:NSDefaultRunLoopMode]; + } + } } return self; } @@ -149,6 +158,9 @@ static void SDLCALL SDL_HideHomeIndicatorHintChanged(void *userdata, const char { [self stopAnimation]; + if (interval <= 0) { + interval = 1; + } animationInterval = interval; animationCallback = callback; animationCallbackParam = callbackParam; @@ -189,7 +201,7 @@ static void SDLCALL SDL_HideHomeIndicatorHintChanged(void *userdata, const char - (void)doLoop:(CADisplayLink *)sender { // Don't run the game loop while a messagebox is up - if (!UIKit_ShowingMessageBox()) { + if (animationCallback && !UIKit_ShowingMessageBox()) { // See the comment in the function definition. #if defined(SDL_VIDEO_OPENGL_ES) || defined(SDL_VIDEO_OPENGL_ES2) UIKit_GL_RestoreCurrentContext(); @@ -266,7 +278,7 @@ static void SDLCALL SDL_HideHomeIndicatorHintChanged(void *userdata, const char @synthesize textInputRect; @synthesize keyboardHeight; -@synthesize keyboardVisible; +@synthesize textFieldFocused; // Set ourselves up as a UITextFieldDelegate - (void)initKeyboard @@ -279,7 +291,7 @@ static void SDLCALL SDL_HideHomeIndicatorHintChanged(void *userdata, const char committedText = textField.text; textField.hidden = YES; - keyboardVisible = NO; + textFieldFocused = NO; NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; #ifndef SDL_PLATFORM_TVOS @@ -287,10 +299,6 @@ static void SDLCALL SDL_HideHomeIndicatorHintChanged(void *userdata, const char selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil]; - [center addObserver:self - selector:@selector(keyboardDidShow:) - name:UIKeyboardDidShowNotification - object:nil]; [center addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification @@ -345,8 +353,10 @@ static void SDLCALL SDL_HideHomeIndicatorHintChanged(void *userdata, const char [view addSubview:textField]; - if (keyboardVisible) { - [self showKeyboard]; + if (textFieldFocused) { + /* startTextInput has been called before the text field was added to the view, + * call it again for the text field to actually become first responder. */ + [self startTextInput]; } } @@ -369,9 +379,6 @@ static void SDLCALL SDL_HideHomeIndicatorHintChanged(void *userdata, const char [center removeObserver:self name:UIKeyboardWillShowNotification object:nil]; - [center removeObserver:self - name:UIKeyboardDidShowNotification - object:nil]; [center removeObserver:self name:UIKeyboardWillHideNotification object:nil]; @@ -384,7 +391,7 @@ static void SDLCALL SDL_HideHomeIndicatorHintChanged(void *userdata, const char object:nil]; } -- (void)setKeyboardProperties:(SDL_PropertiesID) props +- (void)setTextFieldProperties:(SDL_PropertiesID) props { textField.secureTextEntry = NO; @@ -479,45 +486,53 @@ static void SDLCALL SDL_HideHomeIndicatorHintChanged(void *userdata, const char } else { textField.enablesReturnKeyAutomatically = NO; } -} -// reveal onscreen virtual keyboard -- (void)showKeyboard -{ - if (keyboardVisible) { + if (!textField.window) { + /* textField has not been added to the view yet, + we don't have to do anything. */ return; } - keyboardVisible = YES; - if (textField.window) { - showingKeyboard = YES; + // the text field needs to be re-added to the view in order to update correctly. + UIView *superview = textField.superview; + [textField removeFromSuperview]; + [superview addSubview:textField]; + + if (SDL_TextInputActive(window)) { [textField becomeFirstResponder]; } } -// hide onscreen virtual keyboard -- (void)hideKeyboard +/* requests the SDL text field to become focused and accept text input. + * also shows the onscreen virtual keyboard if no hardware keyboard is attached. */ +- (bool)startTextInput { - if (!keyboardVisible) { - return; + textFieldFocused = YES; + if (!textField.window) { + /* textField has not been added to the view yet, + * we will try again when that happens. */ + return true; } - keyboardVisible = NO; - if (textField.window) { - hidingKeyboard = YES; - [textField resignFirstResponder]; + return [textField becomeFirstResponder]; +} + +/* requests the SDL text field to lose focus and stop accepting text input. + * also hides the onscreen virtual keyboard if no hardware keyboard is attached. */ +- (bool)stopTextInput +{ + textFieldFocused = NO; + if (!textField.window) { + /* textField has not been added to the view yet, + * we will try again when that happens. */ + return true; } + + return [textField resignFirstResponder]; } - (void)keyboardWillShow:(NSNotification *)notification { - BOOL shouldStartTextInput = NO; - - if (!SDL_TextInputActive(window) && !hidingKeyboard && !rotatingOrientation) { - shouldStartTextInput = YES; - } - - showingKeyboard = YES; #ifndef SDL_PLATFORM_TVOS CGRect kbrect = [[notification userInfo][UIKeyboardFrameEndUserInfoKey] CGRectValue]; @@ -528,28 +543,29 @@ static void SDLCALL SDL_HideHomeIndicatorHintChanged(void *userdata, const char [self setKeyboardHeight:(int)kbrect.size.height]; #endif - if (shouldStartTextInput) { + /* A keyboard hide transition has been interrupted with a show (keyboardWillHide has been called but keyboardDidHide didn't). + * since text input was stopped by the hide, we have to start it again. */ + if (hidingKeyboard) { SDL_StartTextInput(window); + hidingKeyboard = NO; } } -- (void)keyboardDidShow:(NSNotification *)notification -{ - showingKeyboard = NO; -} - - (void)keyboardWillHide:(NSNotification *)notification { - BOOL shouldStopTextInput = NO; - - if (SDL_TextInputActive(window) && !showingKeyboard && !rotatingOrientation) { - shouldStopTextInput = YES; - } - hidingKeyboard = YES; [self setKeyboardHeight:0]; - if (shouldStopTextInput) { + /* When the user dismisses the software keyboard by the "hide" button in the bottom right corner, + * we want to reflect that on SDL_TextInputActive by calling SDL_StopTextInput...on certain conditions */ + if (SDL_TextInputActive(window) + /* keyboardWillHide gets called when a hardware keyboard is attached, + * keep text input state active if hiding while there is a hardware keyboard. + * if the hardware keyboard gets detached, the software keyboard will appear anyway. */ + && !SDL_HasKeyboard() + /* When the device changes orientation, a sequence of hide and show transitions are triggered. + * keep text input state active in this case. */ + && !rotatingOrientation) { SDL_StopTextInput(window); } } @@ -632,7 +648,6 @@ static void SDLCALL SDL_HideHomeIndicatorHintChanged(void *userdata, const char - (void)setKeyboardHeight:(int)height { - keyboardVisible = height > 0; keyboardHeight = height; [self updateKeyboard]; } @@ -653,7 +668,7 @@ static void SDLCALL SDL_HideHomeIndicatorHintChanged(void *userdata, const char - (BOOL)textFieldShouldReturn:(UITextField *)_textField { SDL_SendKeyboardKeyAutoRelease(0, SDL_SCANCODE_RETURN); - if (keyboardVisible && + if (textFieldFocused && SDL_GetHintBoolean(SDL_HINT_RETURN_KEY_HIDES_IME, false)) { SDL_StopTextInput(window); } @@ -684,20 +699,27 @@ bool UIKit_HasScreenKeyboardSupport(SDL_VideoDevice *_this) return true; } -void UIKit_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props) +bool UIKit_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props) { @autoreleasepool { SDL_uikitviewcontroller *vc = GetWindowViewController(window); - [vc setKeyboardProperties:props]; - [vc showKeyboard]; + return [vc startTextInput]; } } -void UIKit_HideScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window) +bool UIKit_StopTextInput(SDL_VideoDevice *_this, SDL_Window *window) { @autoreleasepool { SDL_uikitviewcontroller *vc = GetWindowViewController(window); - [vc hideKeyboard]; + return [vc stopTextInput]; + } +} + +void UIKit_SetTextInputProperties(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props) +{ + @autoreleasepool { + SDL_uikitviewcontroller *vc = GetWindowViewController(window); + [vc setTextFieldProperties:props]; } } @@ -706,7 +728,7 @@ bool UIKit_IsScreenKeyboardShown(SDL_VideoDevice *_this, SDL_Window *window) @autoreleasepool { SDL_uikitviewcontroller *vc = GetWindowViewController(window); if (vc != nil) { - return vc.keyboardVisible; + return vc.textFieldFocused; } return false; } @@ -719,7 +741,7 @@ bool UIKit_UpdateTextInputArea(SDL_VideoDevice *_this, SDL_Window *window) if (vc != nil) { vc.textInputRect = window->text_input_rect; - if (vc.keyboardVisible) { + if (vc.textFieldFocused) { [vc updateKeyboard]; } } diff --git a/libs/SDL3/src/video/vita/SDL_vitamouse.c b/libs/SDL3/src/video/vita/SDL_vitamouse.c index eedb90c0..facda1b5 100644 --- a/libs/SDL3/src/video/vita/SDL_vitamouse.c +++ b/libs/SDL3/src/video/vita/SDL_vitamouse.c @@ -75,12 +75,28 @@ void VITA_PollMouse(void) else SDL_SendMouseButton(0, Vita_Window, mouseID, SDL_BUTTON_MIDDLE, true); } + if (changed_buttons & 0x8) { + if (prev_buttons & 0x8) + SDL_SendMouseButton(0, Vita_Window, mouseID, SDL_BUTTON_X1, false); + else + SDL_SendMouseButton(0, Vita_Window, mouseID, SDL_BUTTON_X1, true); + } + if (changed_buttons & 0x10) { + if (prev_buttons & 0x10) + SDL_SendMouseButton(0, Vita_Window, mouseID, SDL_BUTTON_X2, false); + else + SDL_SendMouseButton(0, Vita_Window, mouseID, SDL_BUTTON_X2, true); + } prev_buttons = m_reports[i].buttons; if (m_reports[i].rel_x || m_reports[i].rel_y) { SDL_SendMouseMotion(0, Vita_Window, mouseID, true, (float)m_reports[i].rel_x, (float)m_reports[i].rel_y); } + + if (m_reports[i].tilt != 0 || m_reports[i].wheel != 0) { + SDL_SendMouseWheel(0, Vita_Window, mouseID, m_reports[i].tilt, m_reports[i].wheel, SDL_MOUSEWHEEL_NORMAL); + } } } } diff --git a/libs/SDL3/src/video/wayland/SDL_waylandevents.c b/libs/SDL3/src/video/wayland/SDL_waylandevents.c index 1de96fbf..14cc9f39 100644 --- a/libs/SDL3/src/video/wayland/SDL_waylandevents.c +++ b/libs/SDL3/src/video/wayland/SDL_waylandevents.c @@ -696,32 +696,37 @@ static void pointer_handle_button_common(struct SDL_WaylandInput *input, uint32_ { SDL_WindowData *window = input->pointer_focus; enum wl_pointer_button_state state = state_w; - uint32_t sdl_button; - bool ignore_click = false; + Uint64 timestamp = Wayland_GetPointerTimestamp(input, time); + Uint8 sdl_button; + const bool down = (state != 0); + + switch (button) { + case BTN_LEFT: + sdl_button = SDL_BUTTON_LEFT; + break; + case BTN_MIDDLE: + sdl_button = SDL_BUTTON_MIDDLE; + break; + case BTN_RIGHT: + sdl_button = SDL_BUTTON_RIGHT; + break; + case BTN_SIDE: + sdl_button = SDL_BUTTON_X1; + break; + case BTN_EXTRA: + sdl_button = SDL_BUTTON_X2; + break; + default: + return; + } if (window) { SDL_VideoData *viddata = window->waylandData; - switch (button) { - case BTN_LEFT: - sdl_button = SDL_BUTTON_LEFT; - if (ProcessHitTest(input->pointer_focus, input->seat, input->sx_w, input->sy_w, serial)) { - return; // don't pass this event on to app. - } - break; - case BTN_MIDDLE: - sdl_button = SDL_BUTTON_MIDDLE; - break; - case BTN_RIGHT: - sdl_button = SDL_BUTTON_RIGHT; - break; - case BTN_SIDE: - sdl_button = SDL_BUTTON_X1; - break; - case BTN_EXTRA: - sdl_button = SDL_BUTTON_X2; - break; - default: - return; + bool ignore_click = false; + + if (sdl_button == SDL_BUTTON_LEFT && + ProcessHitTest(input->pointer_focus, input->seat, input->sx_w, input->sy_w, serial)) { + return; // don't pass this event on to app. } // Possibly ignore this click if it was to gain focus. @@ -759,8 +764,7 @@ static void pointer_handle_button_common(struct SDL_WaylandInput *input, uint32_ } if (!ignore_click) { - SDL_SendMouseButton(Wayland_GetPointerTimestamp(input, time), window->sdlwindow, input->pointer_id, - sdl_button, (state != 0)); + SDL_SendMouseButton(timestamp, window->sdlwindow, input->pointer_id, sdl_button, down); } } } @@ -777,10 +781,12 @@ static void pointer_handle_axis_common_v1(struct SDL_WaylandInput *input, uint32_t time, uint32_t axis, wl_fixed_t value) { SDL_WindowData *window = input->pointer_focus; - enum wl_pointer_axis a = axis; - float x, y; + const Uint64 timestamp = Wayland_GetPointerTimestamp(input, time); + const enum wl_pointer_axis a = axis; if (input->pointer_focus) { + float x, y; + switch (a) { case WL_POINTER_AXIS_VERTICAL_SCROLL: x = 0; @@ -797,14 +803,14 @@ static void pointer_handle_axis_common_v1(struct SDL_WaylandInput *input, x /= WAYLAND_WHEEL_AXIS_UNIT; y /= WAYLAND_WHEEL_AXIS_UNIT; - SDL_SendMouseWheel(Wayland_GetPointerTimestamp(input, time), window->sdlwindow, input->pointer_id, x, y, SDL_MOUSEWHEEL_NORMAL); + SDL_SendMouseWheel(timestamp, window->sdlwindow, input->pointer_id, x, y, SDL_MOUSEWHEEL_NORMAL); } } static void pointer_handle_axis_common(struct SDL_WaylandInput *input, enum SDL_WaylandAxisEvent type, uint32_t axis, wl_fixed_t value) { - enum wl_pointer_axis a = axis; + const enum wl_pointer_axis a = axis; if (input->pointer_focus) { switch (a) { @@ -992,6 +998,123 @@ static const struct wl_pointer_listener pointer_listener = { pointer_handle_axis_relative_direction // Version 9 }; +static void relative_pointer_handle_relative_motion(void *data, + struct zwp_relative_pointer_v1 *pointer, + uint32_t time_hi, + uint32_t time_lo, + wl_fixed_t dx_w, + wl_fixed_t dy_w, + wl_fixed_t dx_unaccel_w, + wl_fixed_t dy_unaccel_w) +{ + struct SDL_WaylandInput *input = data; + SDL_VideoData *d = input->display; + SDL_WindowData *window = input->pointer_focus; + + // Relative pointer event times are in microsecond granularity. + const Uint64 timestamp = Wayland_GetEventTimestamp(SDL_US_TO_NS(((Uint64)time_hi << 32) | (Uint64)time_lo)); + + if (input->pointer_focus && d->relative_mouse_mode) { + double dx; + double dy; + if (!SDL_GetMouse()->enable_relative_system_scale) { + dx = wl_fixed_to_double(dx_unaccel_w); + dy = wl_fixed_to_double(dy_unaccel_w); + } else { + dx = wl_fixed_to_double(dx_w); + dy = wl_fixed_to_double(dy_w); + } + + SDL_SendMouseMotion(timestamp, window->sdlwindow, input->pointer_id, true, (float)dx, (float)dy); + } +} + +static const struct zwp_relative_pointer_v1_listener relative_pointer_listener = { + relative_pointer_handle_relative_motion, +}; + +static void locked_pointer_locked(void *data, + struct zwp_locked_pointer_v1 *locked_pointer) +{ +} + +static void locked_pointer_unlocked(void *data, + struct zwp_locked_pointer_v1 *locked_pointer) +{ +} + +static const struct zwp_locked_pointer_v1_listener locked_pointer_listener = { + locked_pointer_locked, + locked_pointer_unlocked, +}; + +bool Wayland_input_lock_pointer(struct SDL_WaylandInput *input, SDL_Window *window) +{ + SDL_WindowData *w = window->internal; + SDL_VideoData *d = input->display; + + if (!d->pointer_constraints || !input->pointer) { + return false; + } + + if (!w->locked_pointer) { + if (w->confined_pointer) { + // If the pointer is already confined to the surface, the lock will fail with a protocol error. + Wayland_input_unconfine_pointer(input, window); + } + + w->locked_pointer = zwp_pointer_constraints_v1_lock_pointer(d->pointer_constraints, + w->surface, + input->pointer, + NULL, + ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT); + zwp_locked_pointer_v1_add_listener(w->locked_pointer, + &locked_pointer_listener, + window); + } + + return true; +} + +bool Wayland_input_unlock_pointer(struct SDL_WaylandInput *input, SDL_Window *window) +{ + SDL_WindowData *w = window->internal; + + if (w->locked_pointer) { + zwp_locked_pointer_v1_destroy(w->locked_pointer); + w->locked_pointer = NULL; + } + + // Restore existing pointer confinement. + Wayland_input_confine_pointer(input, window); + + return true; +} + +static void pointer_confine_destroy(SDL_Window *window) +{ + SDL_WindowData *w = window->internal; + if (w->confined_pointer) { + zwp_confined_pointer_v1_destroy(w->confined_pointer); + w->confined_pointer = NULL; + } +} + +static void confined_pointer_confined(void *data, + struct zwp_confined_pointer_v1 *confined_pointer) +{ +} + +static void confined_pointer_unconfined(void *data, + struct zwp_confined_pointer_v1 *confined_pointer) +{ +} + +static const struct zwp_confined_pointer_v1_listener confined_pointer_listener = { + confined_pointer_confined, + confined_pointer_unconfined, +}; + static void touch_handler_down(void *data, struct wl_touch *touch, uint32_t serial, uint32_t timestamp, struct wl_surface *surface, int id, wl_fixed_t fx, wl_fixed_t fy) @@ -1567,7 +1690,8 @@ static void keyboard_handle_enter(void *data, struct wl_keyboard *keyboard, } #endif - window->last_focus_event_time_ns = SDL_GetTicksNS(); + Uint64 timestamp = SDL_GetTicksNS(); + window->last_focus_event_time_ns = timestamp; wl_array_for_each (key, keys) { const SDL_Scancode scancode = Wayland_get_scancode_from_key(input, *key + 8); @@ -1584,7 +1708,7 @@ static void keyboard_handle_enter(void *data, struct wl_keyboard *keyboard, case SDLK_RGUI: case SDLK_MODE: Wayland_HandleModifierKeys(input, scancode, true); - SDL_SendKeyboardKeyIgnoreModifiers(0, input->keyboard_id, *key, scancode, true); + SDL_SendKeyboardKeyIgnoreModifiers(timestamp, input->keyboard_id, *key, scancode, true); break; default: break; @@ -1720,7 +1844,8 @@ static void keyboard_handle_key(void *data, struct wl_keyboard *keyboard, scancode = Wayland_get_scancode_from_key(input, key + 8); Wayland_HandleModifierKeys(input, scancode, state == WL_KEYBOARD_KEY_STATE_PRESSED); - SDL_SendKeyboardKeyIgnoreModifiers(Wayland_GetKeyboardTimestamp(input, time), input->keyboard_id, key, scancode, (state == WL_KEYBOARD_KEY_STATE_PRESSED)); + Uint64 timestamp = Wayland_GetKeyboardTimestamp(input, time); + SDL_SendKeyboardKeyIgnoreModifiers(timestamp, input->keyboard_id, key, scancode, (state == WL_KEYBOARD_KEY_STATE_PRESSED)); if (state == WL_KEYBOARD_KEY_STATE_PRESSED) { if (has_text && !(SDL_GetModState() & SDL_KMOD_CTRL)) { @@ -1792,6 +1917,22 @@ static const struct wl_keyboard_listener keyboard_listener = { keyboard_handle_repeat_info, // Version 4 }; +void Wayland_input_init_relative_pointer(SDL_VideoData *d) +{ + struct SDL_WaylandInput *input = d->input; + + if (!d->relative_pointer_manager) { + return; + } + + if (input->pointer && !input->relative_pointer) { + input->relative_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer(input->display->relative_pointer_manager, input->pointer); + zwp_relative_pointer_v1_add_listener(input->relative_pointer, + &relative_pointer_listener, + input); + } +} + static void seat_handle_capabilities(void *data, struct wl_seat *seat, enum wl_seat_capability caps) { @@ -1807,14 +1948,24 @@ static void seat_handle_capabilities(void *data, struct wl_seat *seat, wl_pointer_set_user_data(input->pointer, input); wl_pointer_add_listener(input->pointer, &pointer_listener, input); + Wayland_input_init_relative_pointer(input->display); + input->pointer_id = SDL_GetNextObjectID(); SDL_AddMouse(input->pointer_id, WAYLAND_DEFAULT_POINTER_NAME, true); } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && input->pointer) { + if (input->relative_pointer) { + zwp_relative_pointer_v1_destroy(input->relative_pointer); + input->relative_pointer = NULL; + } if (input->cursor_shape) { wp_cursor_shape_device_v1_destroy(input->cursor_shape); input->cursor_shape = NULL; } - wl_pointer_destroy(input->pointer); + if (wl_pointer_get_version(input->pointer) >= WL_POINTER_RELEASE_SINCE_VERSION) { + wl_pointer_release(input->pointer); + } else { + wl_pointer_destroy(input->pointer); + } input->pointer = NULL; input->display->pointer = NULL; @@ -2864,6 +3015,8 @@ void Wayland_input_init_tablet_support(struct SDL_WaylandInput *input, struct zw tablet_input->seat = zwp_tablet_manager_v2_get_tablet_seat(tablet_manager, input->seat); zwp_tablet_seat_v2_add_listener(tablet_input->seat, &tablet_seat_listener, tablet_input); + + input->tablet_input = tablet_input; } static void Wayland_remove_all_pens_callback(SDL_PenID instance_id, void *handle, void *userdata) @@ -2968,6 +3121,10 @@ void Wayland_display_destroy_input(SDL_VideoData *d) } } + if (input->relative_pointer) { + zwp_relative_pointer_v1_destroy(input->relative_pointer); + } + if (input->cursor_shape) { wp_cursor_shape_device_v1_destroy(input->cursor_shape); } @@ -3028,109 +3185,11 @@ void Wayland_display_destroy_input(SDL_VideoData *d) d->input = NULL; } -static void relative_pointer_handle_relative_motion(void *data, - struct zwp_relative_pointer_v1 *pointer, - uint32_t time_hi, - uint32_t time_lo, - wl_fixed_t dx_w, - wl_fixed_t dy_w, - wl_fixed_t dx_unaccel_w, - wl_fixed_t dy_unaccel_w) -{ - struct SDL_WaylandInput *input = data; - SDL_VideoData *d = input->display; - SDL_WindowData *window = input->pointer_focus; - double dx_unaccel; - double dy_unaccel; - - // Relative pointer event times are in microsecond granularity. - const Uint64 timestamp = SDL_US_TO_NS(((Uint64)time_hi << 32) | (Uint64)time_lo); - - dx_unaccel = wl_fixed_to_double(dx_unaccel_w); - dy_unaccel = wl_fixed_to_double(dy_unaccel_w); - - if (input->pointer_focus && d->relative_mouse_mode) { - SDL_SendMouseMotion(Wayland_GetEventTimestamp(timestamp), window->sdlwindow, input->pointer_id, true, (float)dx_unaccel, (float)dy_unaccel); - } -} - -static const struct zwp_relative_pointer_v1_listener relative_pointer_listener = { - relative_pointer_handle_relative_motion, -}; - -static void locked_pointer_locked(void *data, - struct zwp_locked_pointer_v1 *locked_pointer) -{ -} - -static void locked_pointer_unlocked(void *data, - struct zwp_locked_pointer_v1 *locked_pointer) -{ -} - -static const struct zwp_locked_pointer_v1_listener locked_pointer_listener = { - locked_pointer_locked, - locked_pointer_unlocked, -}; - -bool Wayland_input_lock_pointer(struct SDL_WaylandInput *input, SDL_Window *window) -{ - SDL_WindowData *w = window->internal; - SDL_VideoData *d = input->display; - - if (!d->pointer_constraints || !input->pointer) { - return false; - } - - if (!w->locked_pointer) { - if (w->confined_pointer) { - // If the pointer is already confined to the surface, the lock will fail with a protocol error. - Wayland_input_unconfine_pointer(input, window); - } - - w->locked_pointer = zwp_pointer_constraints_v1_lock_pointer(d->pointer_constraints, - w->surface, - input->pointer, - NULL, - ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT); - zwp_locked_pointer_v1_add_listener(w->locked_pointer, - &locked_pointer_listener, - window); - } - - return true; -} - -bool Wayland_input_unlock_pointer(struct SDL_WaylandInput *input, SDL_Window *window) -{ - SDL_WindowData *w = window->internal; - - if (w->locked_pointer) { - zwp_locked_pointer_v1_destroy(w->locked_pointer); - w->locked_pointer = NULL; - } - - // Restore existing pointer confinement. - Wayland_input_confine_pointer(input, window); - - return true; -} - -static void pointer_confine_destroy(SDL_Window *window) -{ - SDL_WindowData *w = window->internal; - if (w->confined_pointer) { - zwp_confined_pointer_v1_destroy(w->confined_pointer); - w->confined_pointer = NULL; - } -} - bool Wayland_input_enable_relative_pointer(struct SDL_WaylandInput *input) { SDL_VideoDevice *vd = SDL_GetVideoDevice(); SDL_VideoData *d = input->display; SDL_Window *window; - struct zwp_relative_pointer_v1 *relative_pointer; if (!d->relative_pointer_manager) { return false; @@ -3151,14 +3210,6 @@ bool Wayland_input_enable_relative_pointer(struct SDL_WaylandInput *input) pointer_confine_destroy(window); } - if (!input->relative_pointer) { - relative_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer(d->relative_pointer_manager, input->pointer); - zwp_relative_pointer_v1_add_listener(relative_pointer, - &relative_pointer_listener, - input); - input->relative_pointer = relative_pointer; - } - for (window = vd->windows; window; window = window->next) { Wayland_input_lock_pointer(input, window); } @@ -3178,11 +3229,6 @@ bool Wayland_input_disable_relative_pointer(struct SDL_WaylandInput *input) Wayland_input_unlock_pointer(input, window); } - if (input->relative_pointer) { - zwp_relative_pointer_v1_destroy(input->relative_pointer); - input->relative_pointer = NULL; - } - d->relative_mouse_mode = 0; for (window = vd->windows; window; window = window->next) { @@ -3192,26 +3238,10 @@ bool Wayland_input_disable_relative_pointer(struct SDL_WaylandInput *input) return true; } -static void confined_pointer_confined(void *data, - struct zwp_confined_pointer_v1 *confined_pointer) -{ -} - -static void confined_pointer_unconfined(void *data, - struct zwp_confined_pointer_v1 *confined_pointer) -{ -} - -static const struct zwp_confined_pointer_v1_listener confined_pointer_listener = { - confined_pointer_confined, - confined_pointer_unconfined, -}; - bool Wayland_input_confine_pointer(struct SDL_WaylandInput *input, SDL_Window *window) { SDL_WindowData *w = window->internal; SDL_VideoData *d = input->display; - struct zwp_confined_pointer_v1 *confined_pointer; struct wl_region *confine_rect; if (!d->pointer_constraints) { @@ -3257,7 +3287,7 @@ bool Wayland_input_confine_pointer(struct SDL_WaylandInput *input, SDL_Window *w scaled_mouse_rect.h); } - confined_pointer = + struct zwp_confined_pointer_v1 *confined_pointer = zwp_pointer_constraints_v1_confine_pointer(d->pointer_constraints, w->surface, input->pointer, diff --git a/libs/SDL3/src/video/wayland/SDL_waylandevents_c.h b/libs/SDL3/src/video/wayland/SDL_waylandevents_c.h index c5741a5e..f101baa0 100644 --- a/libs/SDL3/src/video/wayland/SDL_waylandevents_c.h +++ b/libs/SDL3/src/video/wayland/SDL_waylandevents_c.h @@ -81,8 +81,8 @@ struct SDL_WaylandInput SDL_WindowData *pointer_focus; SDL_WindowData *keyboard_focus; SDL_CursorData *current_cursor; - Uint32 keyboard_id; - Uint32 pointer_id; + SDL_KeyboardID keyboard_id; + SDL_MouseID pointer_id; uint32_t pointer_enter_serial; // High-resolution event timestamps @@ -162,6 +162,7 @@ extern void Wayland_create_text_input(SDL_VideoData *d); extern void Wayland_input_initialize_seat(SDL_VideoData *d); extern void Wayland_display_destroy_input(SDL_VideoData *d); +extern void Wayland_input_init_relative_pointer(SDL_VideoData *d); extern bool Wayland_input_enable_relative_pointer(struct SDL_WaylandInput *input); extern bool Wayland_input_disable_relative_pointer(struct SDL_WaylandInput *input); diff --git a/libs/SDL3/src/video/wayland/SDL_waylandmessagebox.c b/libs/SDL3/src/video/wayland/SDL_waylandmessagebox.c index c0c37f9a..ca855231 100644 --- a/libs/SDL3/src/video/wayland/SDL_waylandmessagebox.c +++ b/libs/SDL3/src/video/wayland/SDL_waylandmessagebox.c @@ -123,14 +123,14 @@ bool Wayland_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *butto argv[argc++] = "--title"; argv[argc++] = messageboxdata->title; } else { - argv[argc++] = "--title=\"\""; + argv[argc++] = "--title="; } if (messageboxdata->message && messageboxdata->message[0]) { argv[argc++] = "--text"; argv[argc++] = messageboxdata->message; } else { - argv[argc++] = "--text=\"\""; + argv[argc++] = "--text="; } for (i = 0; i < messageboxdata->numbuttons; ++i) { @@ -143,9 +143,12 @@ bool Wayland_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *butto argv[argc++] = "--extra-button"; argv[argc++] = messageboxdata->buttons[i].text; } else { - argv[argc++] = "--extra-button=\"\""; + argv[argc++] = "--extra-button="; } } + if (messageboxdata->numbuttons == 0) { + argv[argc++] = "--extra-button=OK"; + } argv[argc] = NULL; SDL_PropertiesID props = SDL_CreateProperties(); diff --git a/libs/SDL3/src/video/wayland/SDL_waylandmouse.c b/libs/SDL3/src/video/wayland/SDL_waylandmouse.c index 3c35424a..cdce66b1 100644 --- a/libs/SDL3/src/video/wayland/SDL_waylandmouse.c +++ b/libs/SDL3/src/video/wayland/SDL_waylandmouse.c @@ -592,7 +592,8 @@ static SDL_Cursor *Wayland_CreateSystemCursor(SDL_SystemCursor id) static SDL_Cursor *Wayland_CreateDefaultCursor(void) { - return Wayland_CreateSystemCursor(SDL_SYSTEM_CURSOR_DEFAULT); + SDL_SystemCursor id = SDL_GetDefaultSystemCursor(); + return Wayland_CreateSystemCursor(id); } static void Wayland_FreeCursorData(SDL_CursorData *d) diff --git a/libs/SDL3/src/video/wayland/SDL_waylandvideo.c b/libs/SDL3/src/video/wayland/SDL_waylandvideo.c index 02952a16..14f16a7e 100644 --- a/libs/SDL3/src/video/wayland/SDL_waylandvideo.c +++ b/libs/SDL3/src/video/wayland/SDL_waylandvideo.c @@ -449,7 +449,6 @@ static void Wayland_DeleteDevice(SDL_VideoDevice *device) typedef struct { bool has_fifo_v1; - bool has_commit_timing_v1; } SDL_WaylandPreferredData; static void wayland_preferred_check_handle_global(void *data, struct wl_registry *registry, uint32_t id, @@ -459,8 +458,6 @@ static void wayland_preferred_check_handle_global(void *data, struct wl_registry if (SDL_strcmp(interface, "wp_fifo_manager_v1") == 0) { d->has_fifo_v1 = true; - } else if (SDL_strcmp(interface, "wp_commit_timing_manager_v1") == 0) { - d->has_commit_timing_v1 = true; } } @@ -490,7 +487,7 @@ static bool Wayland_IsPreferred(struct wl_display *display) wl_registry_destroy(registry); - return preferred_data.has_fifo_v1 && preferred_data.has_commit_timing_v1; + return preferred_data.has_fifo_v1; } static SDL_VideoDevice *Wayland_CreateDevice(bool require_preferred_protocols) @@ -524,19 +521,22 @@ static SDL_VideoDevice *Wayland_CreateDevice(bool require_preferred_protocols) /* * If we are checking for preferred Wayland, then let's query for - * fifo-v1 and commit-timing-v1's existence, so we don't regress - * GPU-bound performance and frame-pacing by default due to - * swapchain starvation. + * fifo-v1's existence, so we don't regress GPU-bound performance + * and frame-pacing by default due to swapchain starvation. */ if (require_preferred_protocols && !Wayland_IsPreferred(display)) { - WAYLAND_wl_display_disconnect(display); + if (!display_is_external) { + WAYLAND_wl_display_disconnect(display); + } SDL_WAYLAND_UnloadSymbols(); return NULL; } data = SDL_calloc(1, sizeof(*data)); if (!data) { - WAYLAND_wl_display_disconnect(display); + if (!display_is_external) { + WAYLAND_wl_display_disconnect(display); + } SDL_WAYLAND_UnloadSymbols(); return NULL; } @@ -544,7 +544,9 @@ static SDL_VideoDevice *Wayland_CreateDevice(bool require_preferred_protocols) input = SDL_calloc(1, sizeof(*input)); if (!input) { SDL_free(data); - WAYLAND_wl_display_disconnect(display); + if (!display_is_external) { + WAYLAND_wl_display_disconnect(display); + } SDL_WAYLAND_UnloadSymbols(); return NULL; } @@ -566,7 +568,9 @@ static SDL_VideoDevice *Wayland_CreateDevice(bool require_preferred_protocols) if (!device) { SDL_free(input); SDL_free(data); - WAYLAND_wl_display_disconnect(display); + if (!display_is_external) { + WAYLAND_wl_display_disconnect(display); + } SDL_WAYLAND_UnloadSymbols(); return NULL; } @@ -705,8 +709,8 @@ static void xdg_output_handle_logical_size(void *data, struct zxdg_output_v1 *xd { SDL_DisplayData *internal = (SDL_DisplayData *)data; - internal->screen_width = width; - internal->screen_height = height; + internal->logical_width = width; + internal->logical_height = height; internal->has_logical_size = true; } @@ -854,8 +858,8 @@ static void display_handle_geometry(void *data, internal->x = x; internal->y = y; } - internal->physical_width = physical_width; - internal->physical_height = physical_height; + internal->physical_width_mm = physical_width; + internal->physical_height_mm = physical_height; // The model is only used for the output name if wl_output or xdg-output haven't provided a description. if (internal->display == 0 && !internal->placeholder.name) { @@ -867,7 +871,7 @@ static void display_handle_geometry(void *data, case WL_OUTPUT_TRANSFORM_##in: \ internal->orientation = SDL_ORIENTATION_##out; \ break; - if (internal->physical_width >= internal->physical_height) { + if (internal->physical_width_mm >= internal->physical_height_mm) { switch (transform) { TF_CASE(NORMAL, LANDSCAPE) TF_CASE(90, PORTRAIT) @@ -911,8 +915,8 @@ static void display_handle_mode(void *data, * handle_done and xdg-output coordinates are pre-transformed. */ if (!internal->has_logical_size) { - internal->screen_width = width; - internal->screen_height = height; + internal->logical_width = width; + internal->logical_height = height; } internal->refresh = refresh; @@ -926,7 +930,6 @@ static void display_handle_done(void *data, SDL_DisplayData *internal = (SDL_DisplayData *)data; SDL_VideoData *video = internal->videodata; SDL_DisplayMode native_mode, desktop_mode; - SDL_VideoDisplay *dpy; /* * When using xdg-output, two wl-output.done events will be emitted: @@ -943,7 +946,7 @@ static void display_handle_done(void *data, } // If the display was already created, reset and rebuild the mode list. - dpy = SDL_GetVideoDisplay(internal->display); + SDL_VideoDisplay *dpy = SDL_GetVideoDisplay(internal->display); if (dpy) { SDL_ResetFullscreenDisplayModes(dpy); } @@ -964,29 +967,29 @@ static void display_handle_done(void *data, native_mode.refresh_rate_denominator = 1000; if (internal->has_logical_size) { // If xdg-output is present... - if (native_mode.w != internal->screen_width || native_mode.h != internal->screen_height) { + if (native_mode.w != internal->logical_width || native_mode.h != internal->logical_height) { // ...and the compositor scales the logical viewport... if (video->viewporter) { // ...and viewports are supported, calculate the true scale of the output. - internal->scale_factor = (double)native_mode.w / (double)internal->screen_width; + internal->scale_factor = (double)native_mode.w / (double)internal->logical_width; } else { // ...otherwise, the 'native' pixel values are a multiple of the logical screen size. - internal->pixel_width = internal->screen_width * (int)internal->scale_factor; - internal->pixel_height = internal->screen_height * (int)internal->scale_factor; + internal->pixel_width = internal->logical_width * (int)internal->scale_factor; + internal->pixel_height = internal->logical_height * (int)internal->scale_factor; } } else { /* ...and the output viewport is not scaled in the global compositing * space, the output dimensions need to be divided by the scale factor. */ - internal->screen_width /= (int)internal->scale_factor; - internal->screen_height /= (int)internal->scale_factor; + internal->logical_width /= (int)internal->scale_factor; + internal->logical_height /= (int)internal->scale_factor; } } else { /* Calculate the points from the pixel values, if xdg-output isn't present. * Use the native mode pixel values since they are pre-transformed. */ - internal->screen_width = native_mode.w / (int)internal->scale_factor; - internal->screen_height = native_mode.h / (int)internal->scale_factor; + internal->logical_width = native_mode.w / (int)internal->scale_factor; + internal->logical_height = native_mode.h / (int)internal->scale_factor; } // The scaled desktop mode @@ -994,8 +997,8 @@ static void display_handle_done(void *data, desktop_mode.format = SDL_PIXELFORMAT_XRGB8888; if (!video->scale_to_display_enabled) { - desktop_mode.w = internal->screen_width; - desktop_mode.h = internal->screen_height; + desktop_mode.w = internal->logical_width; + desktop_mode.h = internal->logical_height; desktop_mode.pixel_density = (float)internal->scale_factor; } else { desktop_mode.w = native_mode.w; @@ -1029,8 +1032,8 @@ static void display_handle_done(void *data, desktop_mode.pixel_density = 1.0f; for (i = (int)internal->scale_factor; i > 0; --i) { - desktop_mode.w = internal->screen_width * i; - desktop_mode.h = internal->screen_height * i; + desktop_mode.w = internal->logical_width * i; + desktop_mode.h = internal->logical_height * i; SDL_AddFullscreenDisplayMode(dpy, &desktop_mode); } } @@ -1043,7 +1046,7 @@ static void display_handle_done(void *data, if (internal->display == 0) { // First time getting display info, initialize the VideoDisplay - if (internal->physical_width >= internal->physical_height) { + if (internal->physical_width_mm >= internal->physical_height_mm) { internal->placeholder.natural_orientation = SDL_ORIENTATION_LANDSCAPE; } else { internal->placeholder.natural_orientation = SDL_ORIENTATION_PORTRAIT; @@ -1130,7 +1133,7 @@ static bool Wayland_add_display(SDL_VideoData *d, uint32_t id, uint32_t version) return true; } -static void Wayland_free_display(SDL_VideoDisplay *display) +static void Wayland_free_display(SDL_VideoDisplay *display, bool send_event) { if (display) { SDL_DisplayData *display_data = display->internal; @@ -1154,7 +1157,7 @@ static void Wayland_free_display(SDL_VideoDisplay *display) wl_output_destroy(display_data->output); } - SDL_DelVideoDisplay(display->id, false); + SDL_DelVideoDisplay(display->id, send_event); } } @@ -1221,6 +1224,7 @@ static void display_handle_global(void *data, struct wl_registry *registry, uint d->shm = wl_registry_bind(registry, id, &wl_shm_interface, 1); } else if (SDL_strcmp(interface, "zwp_relative_pointer_manager_v1") == 0) { d->relative_pointer_manager = wl_registry_bind(d->registry, id, &zwp_relative_pointer_manager_v1_interface, 1); + Wayland_input_init_relative_pointer(d); } else if (SDL_strcmp(interface, "zwp_pointer_constraints_v1") == 0) { d->pointer_constraints = wl_registry_bind(d->registry, id, &zwp_pointer_constraints_v1_interface, 1); } else if (SDL_strcmp(interface, "zwp_keyboard_shortcuts_inhibit_manager_v1") == 0) { @@ -1282,7 +1286,7 @@ static void display_remove_global(void *data, struct wl_registry *registry, uint for (int i = 0; i < d->output_count; ++i) { SDL_DisplayData *disp = d->output_list[i]; if (disp->registry_id == id) { - Wayland_free_display(SDL_GetVideoDisplay(disp->display)); + Wayland_free_display(SDL_GetVideoDisplay(disp->display), true); if (i < d->output_count) { SDL_memmove(&d->output_list[i], &d->output_list[i + 1], sizeof(SDL_DisplayData *) * (d->output_count - i - 1)); @@ -1429,7 +1433,7 @@ static void Wayland_VideoCleanup(SDL_VideoDevice *_this) for (i = _this->num_displays - 1; i >= 0; --i) { SDL_VideoDisplay *display = _this->displays[i]; - Wayland_free_display(display); + Wayland_free_display(display, false); } SDL_free(data->output_list); diff --git a/libs/SDL3/src/video/wayland/SDL_waylandvideo.h b/libs/SDL3/src/video/wayland/SDL_waylandvideo.h index a64ea43b..1f0b3230 100644 --- a/libs/SDL3/src/video/wayland/SDL_waylandvideo.h +++ b/libs/SDL3/src/video/wayland/SDL_waylandvideo.h @@ -105,10 +105,11 @@ struct SDL_DisplayData char *wl_output_name; double scale_factor; uint32_t registry_id; + int logical_width, logical_height; int pixel_width, pixel_height; - int x, y, screen_width, screen_height, refresh, transform; + int x, y, refresh, transform; SDL_DisplayOrientation orientation; - int physical_width, physical_height; + int physical_width_mm, physical_height_mm; bool has_logical_position, has_logical_size; SDL_DisplayID display; SDL_VideoDisplay placeholder; diff --git a/libs/SDL3/src/video/wayland/SDL_waylandwindow.c b/libs/SDL3/src/video/wayland/SDL_waylandwindow.c index 5beafb0a..31aa805c 100644 --- a/libs/SDL3/src/video/wayland/SDL_waylandwindow.c +++ b/libs/SDL3/src/video/wayland/SDL_waylandwindow.c @@ -264,8 +264,8 @@ static void RepositionPopup(SDL_Window *window, bool use_current_position) if (wind->shell_surface_type == WAYLAND_SHELL_SURFACE_TYPE_XDG_POPUP && wind->shell_surface.xdg.popup.xdg_positioner && xdg_popup_get_version(wind->shell_surface.xdg.popup.xdg_popup) >= XDG_POPUP_REPOSITION_SINCE_VERSION) { - int x = use_current_position ? window->x : window->floating.x; - int y = use_current_position ? window->y : window->floating.y; + int x = use_current_position ? window->x : window->pending.x; + int y = use_current_position ? window->y : window->pending.y; EnsurePopupPositionIsValid(window, &x, &y); if (wind->scale_to_display) { @@ -499,28 +499,35 @@ static struct wl_callback_listener fullscreen_deadline_listener = { fullscreen_deadline_handler }; -static void maximized_deadline_handler(void *data, struct wl_callback *callback, uint32_t callback_data) +static void maximized_restored_deadline_handler(void *data, struct wl_callback *callback, uint32_t callback_data) { // Get the window from the ID as it may have been destroyed SDL_WindowID windowID = (SDL_WindowID)((uintptr_t)data); SDL_Window *window = SDL_GetWindowFromID(windowID); if (window && window->internal) { - window->internal->maximized_deadline_count--; + window->internal->maximized_restored_deadline_count--; } wl_callback_destroy(callback); } -static struct wl_callback_listener maximized_deadline_listener = { - maximized_deadline_handler +static struct wl_callback_listener maximized_restored_deadline_listener = { + maximized_restored_deadline_handler }; static void FlushPendingEvents(SDL_Window *window) { - while (window->internal->fullscreen_deadline_count || window->internal->maximized_deadline_count) { + // Serialize and restore the pending flags, as they may be overwritten while flushing. + const bool last_position_pending = window->last_position_pending; + const bool last_size_pending = window->last_size_pending; + + while (window->internal->fullscreen_deadline_count || window->internal->maximized_restored_deadline_count) { WAYLAND_wl_display_roundtrip(window->internal->waylandData->display); } + + window->last_position_pending = last_position_pending; + window->last_size_pending = last_size_pending; } /* While we can't get window position from the compositor, we do at least know @@ -819,14 +826,11 @@ static void handle_configure_xdg_toplevel(void *data, * Ignore if less than or greater than max/min size. */ if (window->flags & SDL_WINDOW_RESIZABLE) { - if ((floating && wind->pending_restored_size) || - width == 0 || height == 0) { - /* This happens when we're being restored from a non-floating state - * with a pending floating client size, or the compositor indicates - * that the size is up to the client, so use the cached window size here. + if (width == 0 || height == 0) { + /* This happens when the compositor indicates that the size is + * up to the client, so use the cached window size here. */ if (floating) { - wind->pending_restored_size = false; width = window->floating.w; height = window->floating.h; } else { @@ -1234,14 +1238,13 @@ static void decoration_frame_configure(struct libdecor_frame *frame, * * https://gitlab.freedesktop.org/libdecor/libdecor/-/issues/34 */ - if ((floating && (wind->pending_restored_size || (!wind->floating && !(window->flags & SDL_WINDOW_BORDERLESS)))) || + if ((floating && (!wind->floating && !(window->flags & SDL_WINDOW_BORDERLESS))) || !libdecor_configuration_get_content_size(configuration, frame, &width, &height)) { /* This happens when we're being restored from a non-floating state, * or the compositor indicates that the size is up to the client, so * used the cached window size here. */ if (floating) { - wind->pending_restored_size = false; width = window->floating.w; height = window->floating.h; } else { @@ -2268,12 +2271,22 @@ void Wayland_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window) { SDL_WindowData *wind = window->internal; + // Not currently fullscreen or maximized, and no state pending; nothing to do. + if (!(window->flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_MAXIMIZED)) && + !wind->fullscreen_deadline_count && !wind->maximized_restored_deadline_count) { + return; + } + #ifdef HAVE_LIBDECOR_H if (wind->shell_surface_type == WAYLAND_SHELL_SURFACE_TYPE_LIBDECOR) { if (!wind->shell_surface.libdecor.frame) { return; // Can't do anything yet, wait for ShowWindow } libdecor_frame_unset_maximized(wind->shell_surface.libdecor.frame); + + ++wind->maximized_restored_deadline_count; + struct wl_callback *cb = wl_display_sync(_this->internal->display); + wl_callback_add_listener(cb, &maximized_restored_deadline_listener, (void *)((uintptr_t)window->id)); } else #endif // Note that xdg-shell does NOT provide a way to unset minimize! @@ -2282,6 +2295,10 @@ void Wayland_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window) return; // Can't do anything yet, wait for ShowWindow } xdg_toplevel_unset_maximized(wind->shell_surface.xdg.toplevel.xdg_toplevel); + + ++wind->maximized_restored_deadline_count; + struct wl_callback *cb = wl_display_sync(_this->internal->display); + wl_callback_add_listener(cb, &maximized_restored_deadline_listener, (void *)((uintptr_t)window->id)); } } @@ -2340,6 +2357,12 @@ void Wayland_MaximizeWindow(SDL_VideoDevice *_this, SDL_Window *window) WAYLAND_wl_display_roundtrip(_this->internal->display); } + // Not fullscreen, already maximized, and no state pending; nothing to do. + if (!(window->flags & SDL_WINDOW_FULLSCREEN) && (window->flags & SDL_WINDOW_MAXIMIZED) && + !wind->fullscreen_deadline_count && !wind->maximized_restored_deadline_count) { + return; + } + #ifdef HAVE_LIBDECOR_H if (wind->shell_surface_type == WAYLAND_SHELL_SURFACE_TYPE_LIBDECOR) { if (!wind->shell_surface.libdecor.frame) { @@ -2349,6 +2372,10 @@ void Wayland_MaximizeWindow(SDL_VideoDevice *_this, SDL_Window *window) // Commit to preserve any pending size data. wl_surface_commit(wind->surface); libdecor_frame_set_maximized(wind->shell_surface.libdecor.frame); + + ++wind->maximized_restored_deadline_count; + struct wl_callback *cb = wl_display_sync(viddata->display); + wl_callback_add_listener(cb, &maximized_restored_deadline_listener, (void *)((uintptr_t)window->id)); } else #endif if (wind->shell_surface_type == WAYLAND_SHELL_SURFACE_TYPE_XDG_TOPLEVEL) { @@ -2359,11 +2386,11 @@ void Wayland_MaximizeWindow(SDL_VideoDevice *_this, SDL_Window *window) // Commit to preserve any pending size data. wl_surface_commit(wind->surface); xdg_toplevel_set_maximized(wind->shell_surface.xdg.toplevel.xdg_toplevel); - } - ++wind->maximized_deadline_count; - struct wl_callback *cb = wl_display_sync(viddata->display); - wl_callback_add_listener(cb, &maximized_deadline_listener, (void *)((uintptr_t)window->id)); + ++wind->maximized_restored_deadline_count; + struct wl_callback *cb = wl_display_sync(viddata->display); + wl_callback_add_listener(cb, &maximized_restored_deadline_listener, (void *)((uintptr_t)window->id)); + } } void Wayland_MinimizeWindow(SDL_VideoDevice *_this, SDL_Window *window) @@ -2617,6 +2644,7 @@ bool Wayland_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_Proper SDL_PropertiesID props = SDL_GetWindowProperties(window); SDL_SetPointerProperty(props, SDL_PROP_WINDOW_WAYLAND_DISPLAY_POINTER, data->waylandData->display); SDL_SetPointerProperty(props, SDL_PROP_WINDOW_WAYLAND_SURFACE_POINTER, data->surface); + SDL_SetPointerProperty(props, SDL_PROP_WINDOW_WAYLAND_VIEWPORT_POINTER, data->viewport); SDL_SetPointerProperty(props, SDL_PROP_WINDOW_WAYLAND_EGL_WINDOW_POINTER, data->egl_window); data->hit_test_result = SDL_HITTEST_NORMAL; @@ -2650,29 +2678,12 @@ bool Wayland_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window *window) RepositionPopup(window, false); return true; } else if (wind->shell_surface_type == WAYLAND_SHELL_SURFACE_TYPE_LIBDECOR || wind->shell_surface_type == WAYLAND_SHELL_SURFACE_TYPE_XDG_TOPLEVEL) { - const int x = window->floating.x; - const int y = window->floating.y; - /* Catch up on any pending state before attempting to change the fullscreen window * display via a set fullscreen call to make sure the window doesn't have a pending * leave fullscreen event that it might override. */ FlushPendingEvents(window); - /* XXX: Need to restore this after the roundtrip, as the requested coordinates might - * have been overwritten by the 'real' coordinates if a display enter/leave event - * occurred. - * - * The common pattern: - * - * SDL_SetWindowPosition(); - * SDL_SetWindowFullscreen(); - * - * for positioning a desktop fullscreen window won't work without this. - */ - window->floating.x = x; - window->floating.y = y; - if (wind->is_fullscreen) { SDL_VideoDisplay *display = SDL_GetVideoDisplayForFullscreenWindow(window); if (display && wind->last_displayID != display->id) { @@ -2690,30 +2701,33 @@ void Wayland_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window) { SDL_WindowData *wind = window->internal; - /* Fullscreen windows do not get explicitly resized, and not strictly - * obeying the size of maximized windows is a protocol violation, so - * it is necessary to flush any of these pending state operations. + /* Flush any pending state operations, as fullscreen windows do not get + * explicitly resized, not strictly obeying the size of a maximized window + * is a protocol violation, and pending restore events might result in a + * configure event overwriting the requested size. * * Calling this on a custom surface is informative, so the size must * always be passed through. */ FlushPendingEvents(window); + // Maximized and fullscreen windows don't get resized. if (!(window->flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_MAXIMIZED)) || wind->shell_surface_type == WAYLAND_SHELL_SURFACE_TYPE_CUSTOM) { if (!wind->scale_to_display) { - wind->requested.logical_width = window->floating.w; - wind->requested.logical_height = window->floating.h; + wind->requested.logical_width = window->pending.w; + wind->requested.logical_height = window->pending.h; } else { - wind->requested.logical_width = PixelToPoint(window, window->floating.w); - wind->requested.logical_height = PixelToPoint(window, window->floating.h); - wind->requested.pixel_width = window->floating.w; - wind->requested.pixel_height = window->floating.h; + wind->requested.logical_width = PixelToPoint(window, window->pending.w); + wind->requested.logical_height = PixelToPoint(window, window->pending.h); + wind->requested.pixel_width = window->pending.w; + wind->requested.pixel_height = window->pending.h; } ConfigureWindowGeometry(window); } else { - wind->pending_restored_size = true; + // Can't resize the window. + window->last_size_pending = false; } // Always commit, as this may be in response to a min/max limit change. @@ -2830,7 +2844,7 @@ bool Wayland_SyncWindow(SDL_VideoDevice *_this, SDL_Window *window) do { WAYLAND_wl_display_roundtrip(_this->internal->display); - } while (wind->fullscreen_deadline_count || wind->maximized_deadline_count); + } while (wind->fullscreen_deadline_count || wind->maximized_restored_deadline_count); return true; } diff --git a/libs/SDL3/src/video/wayland/SDL_waylandwindow.h b/libs/SDL3/src/video/wayland/SDL_waylandwindow.h index 4cf0ceec..0c43a791 100644 --- a/libs/SDL3/src/video/wayland/SDL_waylandwindow.h +++ b/libs/SDL3/src/video/wayland/SDL_waylandwindow.h @@ -176,7 +176,7 @@ struct SDL_WindowData SDL_DisplayID last_displayID; int fullscreen_deadline_count; - int maximized_deadline_count; + int maximized_restored_deadline_count; Uint64 last_focus_event_time_ns; bool floating; bool suspended; @@ -190,7 +190,6 @@ struct SDL_WindowData bool show_hide_sync_required; bool scale_to_display; bool reparenting_required; - bool pending_restored_size; bool double_buffer; SDL_HitTestResult hit_test_result; diff --git a/libs/SDL3/src/video/windows/SDL_surface_utils.c b/libs/SDL3/src/video/windows/SDL_surface_utils.c new file mode 100644 index 00000000..bdcac106 --- /dev/null +++ b/libs/SDL3/src/video/windows/SDL_surface_utils.c @@ -0,0 +1,95 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2024 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_internal.h" + +#include "SDL_surface_utils.h" + +#include "../SDL_surface_c.h" + +HICON CreateIconFromSurface(SDL_Surface *surface) +{ + SDL_Surface *s = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_RGBA32); + if (!s) { + return NULL; + } + + /* The dimensions will be needed after s is freed */ + const int width = s->w; + const int height = s->h; + + BITMAPINFO bmpInfo; + ZeroMemory(&bmpInfo, sizeof(BITMAPINFO)); + bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmpInfo.bmiHeader.biWidth = width; + bmpInfo.bmiHeader.biHeight = -height; /* Top-down bitmap */ + bmpInfo.bmiHeader.biPlanes = 1; + bmpInfo.bmiHeader.biBitCount = 32; + bmpInfo.bmiHeader.biCompression = BI_RGB; + + HDC hdc = GetDC(NULL); + void* pBits = NULL; + HBITMAP hBitmap = CreateDIBSection(hdc, &bmpInfo, DIB_RGB_COLORS, &pBits, NULL, 0); + if (!hBitmap) { + ReleaseDC(NULL, hdc); + SDL_DestroySurface(s); + return NULL; + } + + SDL_memcpy(pBits, s->pixels, width * height * 4); + + SDL_DestroySurface(s); + + HBITMAP hMask = CreateBitmap(width, height, 1, 1, NULL); + if (!hMask) { + DeleteObject(hBitmap); + ReleaseDC(NULL, hdc); + return NULL; + } + + HDC hdcMem = CreateCompatibleDC(hdc); + HGDIOBJ oldBitmap = SelectObject(hdcMem, hMask); + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + BYTE* pixel = (BYTE*)pBits + (y * width + x) * 4; + BYTE alpha = pixel[3]; + COLORREF maskColor = (alpha == 0) ? RGB(0, 0, 0) : RGB(255, 255, 255); + SetPixel(hdcMem, x, y, maskColor); + } + } + + ICONINFO iconInfo; + iconInfo.fIcon = TRUE; + iconInfo.xHotspot = 0; + iconInfo.yHotspot = 0; + iconInfo.hbmMask = hMask; + iconInfo.hbmColor = hBitmap; + + HICON hIcon = CreateIconIndirect(&iconInfo); + + SelectObject(hdcMem, oldBitmap); + DeleteDC(hdcMem); + DeleteObject(hBitmap); + DeleteObject(hMask); + ReleaseDC(NULL, hdc); + + return hIcon; +} diff --git a/libs/SDL3/src/thread/ngage/SDL_systhread_c.h b/libs/SDL3/src/video/windows/SDL_surface_utils.h similarity index 81% rename from libs/SDL3/src/thread/ngage/SDL_systhread_c.h rename to libs/SDL3/src/video/windows/SDL_surface_utils.h index c0b69728..5c05a4c8 100644 --- a/libs/SDL3/src/thread/ngage/SDL_systhread_c.h +++ b/libs/SDL3/src/video/windows/SDL_surface_utils.h @@ -20,4 +20,19 @@ */ #include "SDL_internal.h" -typedef int SYS_ThreadHandle; +#ifndef SDL_surface_utils_h_ +#define SDL_surface_utils_h_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern HICON CreateIconFromSurface(SDL_Surface *surface); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libs/SDL3/src/video/windows/SDL_windowsclipboard.c b/libs/SDL3/src/video/windows/SDL_windowsclipboard.c index 6a0cc35c..e62936bb 100644 --- a/libs/SDL3/src/video/windows/SDL_windowsclipboard.c +++ b/libs/SDL3/src/video/windows/SDL_windowsclipboard.c @@ -41,6 +41,51 @@ // Assume we can directly read and write BMP fields without byte swapping SDL_COMPILE_TIME_ASSERT(verify_byte_order, SDL_BYTEORDER == SDL_LIL_ENDIAN); +static int WIN_GetPixelDataOffset(BITMAPINFOHEADER bih) +{ + int offset = 0; + // biSize Specifies the number of bytes required by the structure + // We expect to always be 40 because it should be packed + if (40 == bih.biSize && 40 == sizeof(BITMAPINFOHEADER)) + { + // + // biBitCount Specifies the number of bits per pixel. + // Might exist some bit masks *after* the header and *before* the pixel offset + // we're looking, but only if we have more than + // 8 bits per pixel, so we need to ajust for that + // + if (bih.biBitCount > 8) + { + // If bih.biCompression is RBG we should NOT offset more + + if (bih.biCompression == BI_BITFIELDS) + { + offset += 3 * sizeof(RGBQUAD); + } else if (bih.biCompression == 6 /* BI_ALPHABITFIELDS */) { + // Not common, but still right + offset += 4 * sizeof(RGBQUAD); + } + } + } + + // + // biClrUsed Specifies the number of color indices in the color table that are actually used by the bitmap. + // If this value is zero, the bitmap uses the maximum number of colors + // corresponding to the value of the biBitCount member for the compression mode specified by biCompression. + // If biClrUsed is nonzero and the biBitCount member is less than 16 + // the biClrUsed member specifies the actual number of colors + // + if (bih.biClrUsed > 0) { + offset += bih.biClrUsed * sizeof(RGBQUAD); + } else { + if (bih.biBitCount < 16) { + offset = offset + (sizeof(RGBQUAD) << bih.biBitCount); + } + } + return bih.biSize + offset; +} + + static BOOL WIN_OpenClipboard(SDL_VideoDevice *_this) { // Retry to open the clipboard in case another application has it open @@ -114,8 +159,9 @@ static void *WIN_ConvertDIBtoBMP(HANDLE hMem, size_t *size) BITMAPINFOHEADER *pbih = (BITMAPINFOHEADER *)dib; size_t bih_size = pbih->biSize + pbih->biClrUsed * sizeof(RGBQUAD); size_t dib_size = bih_size + pbih->biSizeImage; + int pixel_offset = WIN_GetPixelDataOffset(*pbih); if (dib_size <= mem_size) { - size_t bmp_size = sizeof(BITMAPFILEHEADER) + dib_size; + size_t bmp_size = sizeof(BITMAPFILEHEADER) + mem_size; bmp = SDL_malloc(bmp_size); if (bmp) { BITMAPFILEHEADER *pbfh = (BITMAPFILEHEADER *)bmp; @@ -123,7 +169,7 @@ static void *WIN_ConvertDIBtoBMP(HANDLE hMem, size_t *size) pbfh->bfSize = (DWORD)bmp_size; pbfh->bfReserved1 = 0; pbfh->bfReserved2 = 0; - pbfh->bfOffBits = (DWORD)(sizeof(BITMAPFILEHEADER) + bih_size); + pbfh->bfOffBits = (DWORD)(sizeof(BITMAPFILEHEADER) + pixel_offset); SDL_memcpy((Uint8 *)bmp + sizeof(BITMAPFILEHEADER), dib, dib_size); *size = bmp_size; } diff --git a/libs/SDL3/src/video/windows/SDL_windowsevents.c b/libs/SDL3/src/video/windows/SDL_windowsevents.c index b78422e8..4802303c 100644 --- a/libs/SDL3/src/video/windows/SDL_windowsevents.c +++ b/libs/SDL3/src/video/windows/SDL_windowsevents.c @@ -350,8 +350,6 @@ static void WIN_UpdateFocus(SDL_Window *window, bool expect_focus) WIN_UpdateWindowICCProfile(data->window, true); } else { - RECT rect; - data->in_window_deactivation = true; SDL_SetKeyboardFocus(NULL); @@ -361,10 +359,7 @@ static void WIN_UpdateFocus(SDL_Window *window, bool expect_focus) } WIN_ResetDeadKeys(); - if (GetClipCursor(&rect) && SDL_memcmp(&rect, &data->cursor_clipped_rect, sizeof(rect)) == 0) { - ClipCursor(NULL); - SDL_zero(data->cursor_clipped_rect); - } + WIN_UnclipCursorForWindow(window); data->in_window_deactivation = false; } @@ -526,6 +521,31 @@ static bool WIN_SwapButtons(HANDLE hDevice) static void WIN_HandleRawMouseInput(Uint64 timestamp, SDL_VideoData *data, HANDLE hDevice, RAWMOUSE *rawmouse) { + static struct { + USHORT usButtonFlags; + Uint8 button; + bool down; + } raw_buttons[] = { + { RI_MOUSE_LEFT_BUTTON_DOWN, SDL_BUTTON_LEFT, true }, + { RI_MOUSE_LEFT_BUTTON_UP, SDL_BUTTON_LEFT, false }, + { RI_MOUSE_RIGHT_BUTTON_DOWN, SDL_BUTTON_RIGHT, true }, + { RI_MOUSE_RIGHT_BUTTON_UP, SDL_BUTTON_RIGHT, false }, + { RI_MOUSE_MIDDLE_BUTTON_DOWN, SDL_BUTTON_MIDDLE, true }, + { RI_MOUSE_MIDDLE_BUTTON_UP, SDL_BUTTON_MIDDLE, false }, + { RI_MOUSE_BUTTON_4_DOWN, SDL_BUTTON_X1, true }, + { RI_MOUSE_BUTTON_4_UP, SDL_BUTTON_X1, false }, + { RI_MOUSE_BUTTON_5_DOWN, SDL_BUTTON_X2, true }, + { RI_MOUSE_BUTTON_5_UP, SDL_BUTTON_X2, false } + }; + + int dx = (int)rawmouse->lLastX; + int dy = (int)rawmouse->lLastY; + bool haveMotion = (dx || dy); + bool haveButton = (rawmouse->usButtonFlags != 0); + bool isAbsolute = ((rawmouse->usFlags & MOUSE_MOVE_ABSOLUTE) != 0); + SDL_MouseID mouseID = (SDL_MouseID)(uintptr_t)hDevice; + + // Check whether relative mode should also receive events from the rawinput stream if (!data->raw_mouse_enabled) { return; } @@ -540,104 +560,86 @@ static void WIN_HandleRawMouseInput(Uint64 timestamp, SDL_VideoData *data, HANDL return; } - SDL_MouseID mouseID = (SDL_MouseID)(uintptr_t)hDevice; SDL_WindowData *windowdata = window->internal; - if ((rawmouse->usFlags & 0x01) == MOUSE_MOVE_RELATIVE) { - if (rawmouse->lLastX || rawmouse->lLastY) { - SDL_SendMouseMotion(timestamp, window, mouseID, true, (float)rawmouse->lLastX, (float)rawmouse->lLastY); - } - } else if (rawmouse->lLastX || rawmouse->lLastY) { - /* This is absolute motion, either using a tablet or mouse over RDP + if (haveMotion) { + if (!isAbsolute) { + SDL_SendMouseMotion(timestamp, window, mouseID, true, (float)dx, (float)dy); + } else { + /* This is absolute motion, either using a tablet or mouse over RDP - Notes on how RDP appears to work, as of Windows 10 2004: - - SetCursorPos() calls are cached, with multiple calls coalesced into a single call that's sent to the RDP client. If the last call to SetCursorPos() has the same value as the last one that was sent to the client, it appears to be ignored and not sent. This means that we need to jitter the SetCursorPos() position slightly in order for the recentering to work correctly. - - User mouse motion is coalesced with SetCursorPos(), so the WM_INPUT positions we see will not necessarily match the position we requested with SetCursorPos(). - - SetCursorPos() outside of the bounds of the focus window appears not to do anything. - - SetCursorPos() while the cursor is NULL doesn't do anything + Notes on how RDP appears to work, as of Windows 10 2004: + - SetCursorPos() calls are cached, with multiple calls coalesced into a single call that's sent to the RDP client. If the last call to SetCursorPos() has the same value as the last one that was sent to the client, it appears to be ignored and not sent. This means that we need to jitter the SetCursorPos() position slightly in order for the recentering to work correctly. + - User mouse motion is coalesced with SetCursorPos(), so the WM_INPUT positions we see will not necessarily match the position we requested with SetCursorPos(). + - SetCursorPos() outside of the bounds of the focus window appears not to do anything. + - SetCursorPos() while the cursor is NULL doesn't do anything - We handle this by creating a safe area within the application window, and when the mouse leaves that safe area, we warp back to the opposite side. Any single motion > 50% of the safe area is assumed to be a warp and ignored. - */ - bool remote_desktop = GetSystemMetrics(SM_REMOTESESSION) ? true : false; - bool virtual_desktop = (rawmouse->usFlags & MOUSE_VIRTUAL_DESKTOP) ? true : false; - bool normalized_coordinates = !(rawmouse->usFlags & 0x40) ? true : false; - int w = GetSystemMetrics(virtual_desktop ? SM_CXVIRTUALSCREEN : SM_CXSCREEN); - int h = GetSystemMetrics(virtual_desktop ? SM_CYVIRTUALSCREEN : SM_CYSCREEN); - int x = normalized_coordinates ? (int)(((float)rawmouse->lLastX / 65535.0f) * w) : (int)rawmouse->lLastX; - int y = normalized_coordinates ? (int)(((float)rawmouse->lLastY / 65535.0f) * h) : (int)rawmouse->lLastY; - int relX, relY; + We handle this by creating a safe area within the application window, and when the mouse leaves that safe area, we warp back to the opposite side. Any single motion > 50% of the safe area is assumed to be a warp and ignored. + */ + bool remote_desktop = (GetSystemMetrics(SM_REMOTESESSION) == TRUE); + bool virtual_desktop = ((rawmouse->usFlags & MOUSE_VIRTUAL_DESKTOP) != 0); + bool raw_coordinates = ((rawmouse->usFlags & 0x40) != 0); + int w = GetSystemMetrics(virtual_desktop ? SM_CXVIRTUALSCREEN : SM_CXSCREEN); + int h = GetSystemMetrics(virtual_desktop ? SM_CYVIRTUALSCREEN : SM_CYSCREEN); + int x = raw_coordinates ? dx : (int)(((float)dx / 65535.0f) * w); + int y = raw_coordinates ? dy : (int)(((float)dy / 65535.0f) * h); + int relX, relY; + + /* Calculate relative motion */ + if (data->last_raw_mouse_position.x == 0 && data->last_raw_mouse_position.y == 0) { + data->last_raw_mouse_position.x = x; + data->last_raw_mouse_position.y = y; + } + relX = x - data->last_raw_mouse_position.x; + relY = y - data->last_raw_mouse_position.y; + + if (remote_desktop) { + if (!windowdata->in_title_click && !windowdata->focus_click_pending) { + static int wobble; + float floatX = (float)x / w; + float floatY = (float)y / h; + + /* See if the mouse is at the edge of the screen, or in the RDP title bar area */ + if (floatX <= 0.01f || floatX >= 0.99f || floatY <= 0.01f || floatY >= 0.99f || y < 32) { + /* Wobble the cursor position so it's not ignored if the last warp didn't have any effect */ + RECT rect = windowdata->cursor_clipped_rect; + int warpX = rect.left + ((rect.right - rect.left) / 2) + wobble; + int warpY = rect.top + ((rect.bottom - rect.top) / 2); + + WIN_SetCursorPos(warpX, warpY); + + ++wobble; + if (wobble > 1) { + wobble = -1; + } + } else { + /* Send relative motion if we didn't warp last frame (had good position data) + We also sometimes get large deltas due to coalesced mouse motion and warping, + so ignore those. + */ + const int MAX_RELATIVE_MOTION = (h / 6); + if (SDL_abs(relX) < MAX_RELATIVE_MOTION && + SDL_abs(relY) < MAX_RELATIVE_MOTION) { + SDL_SendMouseMotion(timestamp, window, mouseID, true, (float)relX, (float)relY); + } + } + } + } else { + const int MAXIMUM_TABLET_RELATIVE_MOTION = 32; + if (SDL_abs(relX) > MAXIMUM_TABLET_RELATIVE_MOTION || + SDL_abs(relY) > MAXIMUM_TABLET_RELATIVE_MOTION) { + /* Ignore this motion, probably a pen lift and drop */ + } else { + SDL_SendMouseMotion(timestamp, window, mouseID, true, (float)relX, (float)relY); + } + } - // Calculate relative motion - if (data->last_raw_mouse_position.x == 0 && data->last_raw_mouse_position.y == 0) { data->last_raw_mouse_position.x = x; data->last_raw_mouse_position.y = y; } - relX = x - data->last_raw_mouse_position.x; - relY = y - data->last_raw_mouse_position.y; - - if (remote_desktop) { - if (!windowdata->in_title_click && !windowdata->focus_click_pending) { - static int wobble; - float floatX = (float)x / w; - float floatY = (float)y / h; - - // See if the mouse is at the edge of the screen, or in the RDP title bar area - if (floatX <= 0.01f || floatX >= 0.99f || floatY <= 0.01f || floatY >= 0.99f || y < 32) { - // Wobble the cursor position so it's not ignored if the last warp didn't have any effect - RECT rect = windowdata->cursor_clipped_rect; - int warpX = rect.left + ((rect.right - rect.left) / 2) + wobble; - int warpY = rect.top + ((rect.bottom - rect.top) / 2); - - WIN_SetCursorPos(warpX, warpY); - - ++wobble; - if (wobble > 1) { - wobble = -1; - } - } else { - /* Send relative motion if we didn't warp last frame (had good position data) - We also sometimes get large deltas due to coalesced mouse motion and warping, - so ignore those. - */ - const int MAX_RELATIVE_MOTION = (h / 6); - if (SDL_abs(relX) < MAX_RELATIVE_MOTION && - SDL_abs(relY) < MAX_RELATIVE_MOTION) { - SDL_SendMouseMotion(timestamp, window, mouseID, true, (float)relX, (float)relY); - } - } - } - } else { - const int MAXIMUM_TABLET_RELATIVE_MOTION = 32; - if (SDL_abs(relX) > MAXIMUM_TABLET_RELATIVE_MOTION || - SDL_abs(relY) > MAXIMUM_TABLET_RELATIVE_MOTION) { - // Ignore this motion, probably a pen lift and drop - } else { - SDL_SendMouseMotion(timestamp, window, mouseID, true, (float)relX, (float)relY); - } - } - - data->last_raw_mouse_position.x = x; - data->last_raw_mouse_position.y = y; } - if (rawmouse->usButtonFlags) { - static struct { - USHORT usButtonFlags; - Uint8 button; - bool down; - } raw_buttons[] = { - { RI_MOUSE_LEFT_BUTTON_DOWN, SDL_BUTTON_LEFT, true }, - { RI_MOUSE_LEFT_BUTTON_UP, SDL_BUTTON_LEFT, false }, - { RI_MOUSE_RIGHT_BUTTON_DOWN, SDL_BUTTON_RIGHT, true }, - { RI_MOUSE_RIGHT_BUTTON_UP, SDL_BUTTON_RIGHT, false }, - { RI_MOUSE_MIDDLE_BUTTON_DOWN, SDL_BUTTON_MIDDLE, true }, - { RI_MOUSE_MIDDLE_BUTTON_UP, SDL_BUTTON_MIDDLE, false }, - { RI_MOUSE_BUTTON_4_DOWN, SDL_BUTTON_X1, true }, - { RI_MOUSE_BUTTON_4_UP, SDL_BUTTON_X1, false }, - { RI_MOUSE_BUTTON_5_DOWN, SDL_BUTTON_X2, true }, - { RI_MOUSE_BUTTON_5_UP, SDL_BUTTON_X2, false } - }; - + if (haveButton) { for (int i = 0; i < SDL_arraysize(raw_buttons); ++i) { if (rawmouse->usButtonFlags & raw_buttons[i].usButtonFlags) { Uint8 button = raw_buttons[i].button; @@ -669,11 +671,11 @@ static void WIN_HandleRawMouseInput(Uint64 timestamp, SDL_VideoData *data, HANDL } if (rawmouse->usButtonFlags & RI_MOUSE_WHEEL) { - short amount = (short)rawmouse->usButtonData; + SHORT amount = (SHORT)rawmouse->usButtonData; float fAmount = (float)amount / WHEEL_DELTA; SDL_SendMouseWheel(WIN_GetEventTimestamp(), window, mouseID, 0.0f, fAmount, SDL_MOUSEWHEEL_NORMAL); } else if (rawmouse->usButtonFlags & RI_MOUSE_HWHEEL) { - short amount = (short)rawmouse->usButtonData; + SHORT amount = (SHORT)rawmouse->usButtonData; float fAmount = (float)amount / WHEEL_DELTA; SDL_SendMouseWheel(WIN_GetEventTimestamp(), window, mouseID, fAmount, 0.0f, SDL_MOUSEWHEEL_NORMAL); } @@ -730,6 +732,7 @@ static void WIN_HandleRawKeyboardInput(Uint64 timestamp, SDL_VideoData *data, HA } code = windows_scancode_table[index]; } + if (down) { SDL_Window *focus = SDL_GetKeyboardFocus(); if (!focus || focus->text_input_active) { @@ -740,12 +743,12 @@ static void WIN_HandleRawKeyboardInput(Uint64 timestamp, SDL_VideoData *data, HA SDL_SendKeyboardKey(timestamp, keyboardID, rawcode, code, down); } -void WIN_PollRawInput(SDL_VideoDevice *_this) +void WIN_PollRawInput(SDL_VideoDevice *_this, Uint64 poll_start) { SDL_VideoData *data = _this->internal; UINT size, i, count, total = 0; RAWINPUT *input; - Uint64 now; + Uint64 poll_finish; if (data->rawinput_offset == 0) { BOOL isWow64; @@ -762,6 +765,7 @@ void WIN_PollRawInput(SDL_VideoDevice *_this) for (;;) { size = data->rawinput_size - (UINT)((BYTE *)input - data->rawinput); count = GetRawInputBuffer(input, &size, sizeof(RAWINPUTHEADER)); + poll_finish = SDL_GetTicksNS(); if (count == 0 || count == (UINT)-1) { if (!data->rawinput || (count == (UINT)-1 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)) { const UINT RAWINPUT_BUFFER_SIZE_INCREMENT = 96; // 2 64-bit raw mouse packets @@ -785,37 +789,28 @@ void WIN_PollRawInput(SDL_VideoDevice *_this) } } - now = SDL_GetTicksNS(); if (total > 0) { - Uint64 mouse_timestamp, mouse_increment; - Uint64 delta = (now - data->last_rawinput_poll); - UINT total_mouse = 0; + Uint64 delta = poll_finish - poll_start; + UINT mouse_total = 0; for (i = 0, input = (RAWINPUT *)data->rawinput; i < total; ++i, input = NEXTRAWINPUTBLOCK(input)) { if (input->header.dwType == RIM_TYPEMOUSE) { - ++total_mouse; + mouse_total += 1; } } - if (total_mouse > 1 && delta <= SDL_MS_TO_NS(100)) { - // We'll spread these events over the time since the last poll - mouse_timestamp = data->last_rawinput_poll; - mouse_increment = delta / total_mouse; - } else { - // Do we want to track the update rate per device? - mouse_timestamp = now; - mouse_increment = 0; - } + int mouse_index = 0; for (i = 0, input = (RAWINPUT *)data->rawinput; i < total; ++i, input = NEXTRAWINPUTBLOCK(input)) { if (input->header.dwType == RIM_TYPEMOUSE) { + mouse_index += 1; // increment first so that it starts at one RAWMOUSE *rawmouse = (RAWMOUSE *)((BYTE *)input + data->rawinput_offset); - mouse_timestamp += mouse_increment; - WIN_HandleRawMouseInput(mouse_timestamp, data, input->header.hDevice, rawmouse); + Uint64 time = poll_finish - (delta * (mouse_total - mouse_index)) / mouse_total; + WIN_HandleRawMouseInput(time, data, input->header.hDevice, rawmouse); } else if (input->header.dwType == RIM_TYPEKEYBOARD) { RAWKEYBOARD *rawkeyboard = (RAWKEYBOARD *)((BYTE *)input + data->rawinput_offset); - WIN_HandleRawKeyboardInput(now, data, input->header.hDevice, rawkeyboard); + WIN_HandleRawKeyboardInput(poll_finish, data, input->header.hDevice, rawkeyboard); } } } - data->last_rawinput_poll = now; + data->last_rawinput_poll = poll_finish; } #endif // !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) @@ -1086,6 +1081,10 @@ LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara case WM_NCACTIVATE: { // Don't immediately clip the cursor in case we're clicking minimize/maximize buttons + // This is the only place that this flag is set. This causes all subsequent calls to + // WIN_UpdateClipCursor for this window to be no-ops in this frame's message-pumping. + // This flag is unset at the end of message pumping each frame for every window, and + // should never be carried over between frames. data->skip_update_clipcursor = true; /* Update the focus here, since it's possible to get WM_ACTIVATE and WM_SETFOCUS without @@ -1128,7 +1127,18 @@ LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara case WM_MOUSEMOVE: { - /* SDL_Mouse *mouse = SDL_GetMouse(); */ + SDL_Window *window = data->window; + + if (window->flags & SDL_WINDOW_INPUT_FOCUS) { + bool wish_clip_cursor = ( + window->flags & (SDL_WINDOW_MOUSE_RELATIVE_MODE | SDL_WINDOW_MOUSE_GRABBED) || + (window->mouse_rect.w > 0 && window->mouse_rect.h > 0) + ); + if (wish_clip_cursor) { + data->skip_update_clipcursor = false; + WIN_UpdateClipCursor(window); + } + } if (!data->mouse_tracked) { TRACKMOUSEEVENT trackMouseEvent; @@ -1146,7 +1156,7 @@ LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara // Only generate mouse events for real mouse if (GetMouseMessageSource((ULONG)GetMessageExtraInfo()) != SDL_MOUSE_EVENT_SOURCE_TOUCH && lParam != data->last_pointer_update) { - SDL_SendMouseMotion(WIN_GetEventTimestamp(), data->window, SDL_GLOBAL_MOUSE_ID, false, (float)GET_X_LPARAM(lParam), (float)GET_Y_LPARAM(lParam)); + SDL_SendMouseMotion(WIN_GetEventTimestamp(), window, SDL_GLOBAL_MOUSE_ID, false, (float)GET_X_LPARAM(lParam), (float)GET_Y_LPARAM(lParam)); } } } break; @@ -1440,32 +1450,6 @@ LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara break; #endif // WM_GETMINMAXINFO - case WM_WINDOWPOSCHANGING: - { - if (data->expected_resize) { - returnCode = 0; - } - - if (data->floating_rect_pending && - !IsIconic(hwnd) && - !IsZoomed(hwnd) && - (data->window->flags & (SDL_WINDOW_MAXIMIZED | SDL_WINDOW_MINIMIZED)) && - !(data->window->flags & SDL_WINDOW_FULLSCREEN)) { - // If a new floating size is pending, apply it if moving from a fixed-size to floating state. - WINDOWPOS *windowpos = (WINDOWPOS*)lParam; - int fx, fy, fw, fh; - - WIN_AdjustWindowRect(data->window, &fx, &fy, &fw, &fh, SDL_WINDOWRECT_FLOATING); - windowpos->x = fx; - windowpos->y = fy; - windowpos->cx = fw; - windowpos->cy = fh; - windowpos->flags &= ~(SWP_NOSIZE | SWP_NOMOVE); - - data->floating_rect_pending = false; - } - } break; - case WM_WINDOWPOSCHANGED: { SDL_Window *win; @@ -1557,12 +1541,7 @@ LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara case WM_TIMER: { if (wParam == (UINT_PTR)SDL_IterateMainCallbacks) { - if (SDL_HasMainCallbacks()) { - SDL_IterateMainCallbacks(false); - } else { - // Send an expose event so the application can redraw - SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_EXPOSED, 0, 0); - } + SDL_OnWindowLiveResizeUpdate(data->window); return 0; } } break; @@ -1608,11 +1587,11 @@ LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara switch (edge) { case WMSZ_LEFT: case WMSZ_RIGHT: - h = (int)(w / data->window->max_aspect); + h = (int)SDL_roundf(w / data->window->max_aspect); break; default: // resizing via corners or top or bottom - w = (int)(h*data->window->max_aspect); + w = (int)SDL_roundf(h * data->window->max_aspect); break; } } else { @@ -1891,9 +1870,21 @@ LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara SDL_WindowFlags window_flags = SDL_GetWindowFlags(data->window); if (wParam == TRUE && (window_flags & SDL_WINDOW_BORDERLESS) && !(window_flags & SDL_WINDOW_FULLSCREEN)) { // When borderless, need to tell windows that the size of the non-client area is 0 - if (!(window_flags & SDL_WINDOW_RESIZABLE)) { + NCCALCSIZE_PARAMS *params = (NCCALCSIZE_PARAMS *)lParam; + WINDOWPLACEMENT placement; + if (GetWindowPlacement(hwnd, &placement) && placement.showCmd == SW_MAXIMIZE) { + // Maximized borderless windows should use the full monitor size + HMONITOR hMonitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONULL); + if (hMonitor) { + MONITORINFO info; + SDL_zero(info); + info.cbSize = sizeof(info); + if (GetMonitorInfo(hMonitor, &info)) { + params->rgrc[0] = info.rcMonitor; + } + } + } else if (!(window_flags & SDL_WINDOW_RESIZABLE)) { int w, h; - NCCALCSIZE_PARAMS *params = (NCCALCSIZE_PARAMS *)lParam; w = data->window->floating.w; h = data->window->floating.h; params->rgrc[0].right = params->rgrc[0].left + w; @@ -2102,28 +2093,6 @@ LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara } #if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) -static void WIN_UpdateClipCursorForWindows(void) -{ - SDL_VideoDevice *_this = SDL_GetVideoDevice(); - SDL_Window *window; - Uint64 now = SDL_GetTicks(); - const int CLIPCURSOR_UPDATE_INTERVAL_MS = SDL_GetMouse()->relative_mode_clip_interval; - - if (_this) { - for (window = _this->windows; window; window = window->next) { - SDL_WindowData *data = window->internal; - if (data) { - if (data->skip_update_clipcursor) { - data->skip_update_clipcursor = false; - WIN_UpdateClipCursor(window); - } else if (CLIPCURSOR_UPDATE_INTERVAL_MS > 0 && now >= (data->last_updated_clipcursor + CLIPCURSOR_UPDATE_INTERVAL_MS)) { - WIN_UpdateClipCursor(window); - } - } - } - } -} - static void WIN_UpdateMouseCapture(void) { SDL_Window *focusWindow = SDL_GetKeyboardFocus(); @@ -2318,7 +2287,17 @@ void WIN_PumpEvents(SDL_VideoDevice *_this) } // Update the clipping rect in case someone else has stolen it - WIN_UpdateClipCursorForWindows(); + if (_this) { + SDL_Window *window = _this->windows; + while (window) { + SDL_WindowData *data = window->internal; + if (data && data->skip_update_clipcursor) { + data->skip_update_clipcursor = false; + WIN_UpdateClipCursor(window); + } + window = window->next; + } + } // Update mouse capture WIN_UpdateMouseCapture(); diff --git a/libs/SDL3/src/video/windows/SDL_windowsevents.h b/libs/SDL3/src/video/windows/SDL_windowsevents.h index a48b8aaa..8d4e762a 100644 --- a/libs/SDL3/src/video/windows/SDL_windowsevents.h +++ b/libs/SDL3/src/video/windows/SDL_windowsevents.h @@ -30,7 +30,7 @@ extern HINSTANCE SDL_Instance; extern LRESULT CALLBACK WIN_KeyboardHookProc(int nCode, WPARAM wParam, LPARAM lParam); extern LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); -extern void WIN_PollRawInput(SDL_VideoDevice *_this); +extern void WIN_PollRawInput(SDL_VideoDevice *_this, Uint64 poll_start); extern void WIN_CheckKeyboardAndMouseHotplug(SDL_VideoDevice *_this, bool initial_check); extern void WIN_PumpEvents(SDL_VideoDevice *_this); extern void WIN_SendWakeupEvent(SDL_VideoDevice *_this, SDL_Window *window); diff --git a/libs/SDL3/src/video/windows/SDL_windowsmouse.c b/libs/SDL3/src/video/windows/SDL_windowsmouse.c index a6b80729..d53a1f20 100644 --- a/libs/SDL3/src/video/windows/SDL_windowsmouse.c +++ b/libs/SDL3/src/video/windows/SDL_windowsmouse.c @@ -29,6 +29,7 @@ #include "../SDL_video_c.h" #include "../../events/SDL_mouse_c.h" #include "../../joystick/usb_ids.h" +#include "../../core/windows/SDL_windows.h" // for checking windows version typedef struct CachedCursor @@ -47,9 +48,22 @@ struct SDL_CursorData HCURSOR cursor; }; +typedef struct +{ + Uint64 xs[5]; + Uint64 ys[5]; + Sint64 residual[2]; + Uint32 dpiscale; + Uint32 dpidenom; + int last_node; + bool enhanced; + bool dpiaware; +} WIN_MouseData; + DWORD SDL_last_warp_time = 0; HCURSOR SDL_cursor = NULL; static SDL_Cursor *SDL_blank_cursor = NULL; +static WIN_MouseData WIN_system_scale_data; static SDL_Cursor *WIN_CreateCursorAndData(HCURSOR hcursor) { @@ -70,11 +84,6 @@ static SDL_Cursor *WIN_CreateCursorAndData(HCURSOR hcursor) return cursor; } -static SDL_Cursor *WIN_CreateDefaultCursor(void) -{ - return WIN_CreateCursorAndData(LoadCursor(NULL, IDC_ARROW)); -} - static bool IsMonochromeSurface(SDL_Surface *surface) { int x, y; @@ -328,6 +337,12 @@ static SDL_Cursor *WIN_CreateSystemCursor(SDL_SystemCursor id) return WIN_CreateCursorAndData(LoadCursor(NULL, name)); } +static SDL_Cursor *WIN_CreateDefaultCursor(void) +{ + SDL_SystemCursor id = SDL_GetDefaultSystemCursor(); + return WIN_CreateSystemCursor(id); +} + static void WIN_FreeCursor(SDL_Cursor *cursor) { SDL_CursorData *data = cursor->internal; @@ -521,6 +536,95 @@ static SDL_MouseButtonFlags WIN_GetGlobalMouseState(float *x, float *y) return result; } +static void WIN_ApplySystemScale(void *internal, Uint64 timestamp, SDL_Window *window, SDL_MouseID mouseID, float *x, float *y) +{ + if (!internal) { + return; + } + WIN_MouseData *data = (WIN_MouseData *)internal; + + SDL_VideoDisplay *display = window ? SDL_GetVideoDisplayForWindow(window) : SDL_GetVideoDisplay(SDL_GetPrimaryDisplay()); + + Sint64 ix = (Sint64)*x * 65536; + Sint64 iy = (Sint64)*y * 65536; + Uint32 dpi = display ? (Uint32)(display->content_scale * USER_DEFAULT_SCREEN_DPI) : USER_DEFAULT_SCREEN_DPI; + + if (!data->enhanced) { // early return if flat scale + dpi = data->dpiscale * (data->dpiaware ? dpi : USER_DEFAULT_SCREEN_DPI); + ix *= dpi; + iy *= dpi; + ix /= USER_DEFAULT_SCREEN_DPI; + iy /= USER_DEFAULT_SCREEN_DPI; + ix /= 32; + iy /= 32; + // data->residual[0] += ix; + // data->residual[1] += iy; + // ix = 65536 * (data->residual[0] / 65536); + // iy = 65536 * (data->residual[1] / 65536); + // data->residual[0] -= ix; + // data->residual[1] -= iy; + *x = (float)ix / 65536.0f; + *y = (float)iy / 65536.0f; + return; + } + + Uint64 *xs = data->xs; + Uint64 *ys = data->ys; + Uint64 absx = SDL_abs(ix); + Uint64 absy = SDL_abs(iy); + Uint64 speed = SDL_min(absx, absy) + (SDL_max(absx, absy) << 1); // super cursed approximation used by Windows + if (speed == 0) { + return; + } + + int i, j, k; + for (i = 1; i < 5; i++) { + j = i; + if (speed < xs[j]) { + break; + } + } + i -= 1; + j -= 1; + k = data->last_node; + data->last_node = j; + + Uint32 denom = data->dpidenom; + Sint64 scale = 0; + Sint64 xdiff = xs[j+1] - xs[j]; + Sint64 ydiff = ys[j+1] - ys[j]; + if (xdiff != 0) { + Sint64 slope = ydiff / xdiff; + Sint64 inter = slope * xs[i] - ys[i]; + scale += slope - inter / speed; + } + + if (j > k) { + denom <<= 1; + xdiff = xs[k+1] - xs[k]; + ydiff = ys[k+1] - ys[k]; + if (xdiff != 0) { + Sint64 slope = ydiff / xdiff; + Sint64 inter = slope * xs[k] - ys[k]; + scale += slope - inter / speed; + } + } + + scale *= dpi; + ix *= scale; + iy *= scale; + ix /= denom; + iy /= denom; + // data->residual[0] += ix; + // data->residual[1] += iy; + // ix = 65536 * (data->residual[0] / 65536); + // iy = 65536 * (data->residual[1] / 65536); + // data->residual[0] -= ix; + // data->residual[1] -= iy; + *x = (float)ix / 65536.0f; + *y = (float)iy / 65536.0f; +} + void WIN_InitMouse(SDL_VideoDevice *_this) { SDL_Mouse *mouse = SDL_GetMouse(); @@ -534,6 +638,8 @@ void WIN_InitMouse(SDL_VideoDevice *_this) mouse->SetRelativeMouseMode = WIN_SetRelativeMouseMode; mouse->CaptureMouse = WIN_CaptureMouse; mouse->GetGlobalMouseState = WIN_GetGlobalMouseState; + mouse->ApplySystemScale = WIN_ApplySystemScale; + mouse->system_scale_data = &WIN_system_scale_data; SDL_SetDefaultCursor(WIN_CreateDefaultCursor()); @@ -550,101 +656,68 @@ void WIN_QuitMouse(SDL_VideoDevice *_this) } } -/* For a great description of how the enhanced mouse curve works, see: - * https://superuser.com/questions/278362/windows-mouse-acceleration-curve-smoothmousexcurve-and-smoothmouseycurve - * http://www.esreality.com/?a=post&id=1846538/ - */ -static bool LoadFiveFixedPointFloats(const BYTE *bytes, float *values) +static void ReadMouseCurve(int v, Uint64 xs[5], Uint64 ys[5]) { - int i; - - for (i = 0; i < 5; ++i) { - float fraction = (float)((Uint16)bytes[1] << 8 | bytes[0]) / 65535.0f; - float value = (float)(((Uint16)bytes[3] << 8) | bytes[2]) + fraction; - *values++ = value; - bytes += 8; - } - return true; -} - -static void WIN_SetEnhancedMouseScale(int mouse_speed) -{ - float scale = (float)mouse_speed / 10.0f; - HKEY hKey; - DWORD dwType = REG_BINARY; - BYTE value[40]; - DWORD length = sizeof(value); - int i; - float xpoints[5]; - float ypoints[5]; - float scale_points[10]; - const int dpi = USER_DEFAULT_SCREEN_DPI; // FIXME, how do we handle different monitors with different DPI? - const float display_factor = 3.5f * (150.0f / dpi); - - if (RegOpenKeyExW(HKEY_CURRENT_USER, L"Control Panel\\Mouse", 0, KEY_READ, &hKey) == ERROR_SUCCESS) { - if (RegQueryValueExW(hKey, L"SmoothMouseXCurve", 0, &dwType, value, &length) == ERROR_SUCCESS && - LoadFiveFixedPointFloats(value, xpoints) && - RegQueryValueExW(hKey, L"SmoothMouseYCurve", 0, &dwType, value, &length) == ERROR_SUCCESS && - LoadFiveFixedPointFloats(value, ypoints)) { - for (i = 0; i < 5; ++i) { - float gain; - if (xpoints[i] > 0.0f) { - gain = (ypoints[i] / xpoints[i]) * scale; - } else { - gain = 0.0f; - } - scale_points[i * 2] = xpoints[i]; - scale_points[i * 2 + 1] = gain / display_factor; - // SDL_Log("Point %d = %f,%f\n", i, scale_points[i * 2], scale_points[i * 2 + 1]); - } - SDL_SetMouseSystemScale(SDL_arraysize(scale_points), scale_points); - } - RegCloseKey(hKey); - } -} - -static void WIN_SetLinearMouseScale(int mouse_speed) -{ - static float mouse_speed_scale[] = { - 0.0f, - 1 / 32.0f, - 1 / 16.0f, - 1 / 8.0f, - 2 / 8.0f, - 3 / 8.0f, - 4 / 8.0f, - 5 / 8.0f, - 6 / 8.0f, - 7 / 8.0f, - 1.0f, - 1.25f, - 1.5f, - 1.75f, - 2.0f, - 2.25f, - 2.5f, - 2.75f, - 3.0f, - 3.25f, - 3.5f + bool win8 = WIN_IsWindows8OrGreater(); + DWORD xbuff[10] = { + 0x00000000, 0, + 0x00006e15, 0, + 0x00014000, 0, + 0x0003dc29, 0, + 0x00280000, 0 }; - - if (mouse_speed > 0 && mouse_speed < SDL_arraysize(mouse_speed_scale)) { - SDL_SetMouseSystemScale(1, &mouse_speed_scale[mouse_speed]); + DWORD ybuff[10] = { + 0x00000000, 0, + win8 ? 0x000111fd : 0x00015eb8, 0, + win8 ? 0x00042400 : 0x00054ccd, 0, + win8 ? 0x0012fc00 : 0x00184ccd, 0, + win8 ? 0x01bbc000 : 0x02380000, 0 + }; + DWORD xsize = sizeof(xbuff); + DWORD ysize = sizeof(ybuff); + HKEY open_handle; + if (RegOpenKeyExW(HKEY_CURRENT_USER, L"Control Panel\\Mouse", 0, KEY_READ, &open_handle) == ERROR_SUCCESS) { + RegQueryValueExW(open_handle, L"SmoothMouseXCurve", NULL, NULL, (BYTE*)xbuff, &xsize); + RegQueryValueExW(open_handle, L"SmoothMouseYCurve", NULL, NULL, (BYTE*)ybuff, &ysize); + RegCloseKey(open_handle); + } + xs[0] = 0; // first node must always be origin + ys[0] = 0; // first node must always be origin + int i; + for (i = 1; i < 5; i++) { + xs[i] = (7 * (Uint64)xbuff[i*2]); + ys[i] = (v * (Uint64)ybuff[i*2]) << 17; } } void WIN_UpdateMouseSystemScale(void) { - int mouse_speed; - int params[3] = { 0, 0, 0 }; + SDL_Mouse *mouse = SDL_GetMouse(); - if (SystemParametersInfo(SPI_GETMOUSESPEED, 0, &mouse_speed, 0) && - SystemParametersInfo(SPI_GETMOUSE, 0, params, 0)) { + if (mouse->ApplySystemScale == WIN_ApplySystemScale) { + mouse->system_scale_data = &WIN_system_scale_data; + } + + // always reinitialize to valid defaults, whether fetch was successful or not. + WIN_MouseData *data = &WIN_system_scale_data; + data->residual[0] = 0; + data->residual[1] = 0; + data->dpiscale = 32; + data->dpidenom = (10 * (WIN_IsWindows8OrGreater() ? 120 : 150)) << 16; + data->dpiaware = WIN_IsPerMonitorV2DPIAware(SDL_GetVideoDevice()); + data->enhanced = false; + + int v = 10; + if (SystemParametersInfo(SPI_GETMOUSESPEED, 0, &v, 0)) { + v = SDL_max(1, SDL_min(v, 20)); + data->dpiscale = SDL_max(SDL_max(v, (v - 2) << 2), (v - 6) << 3); + } + + int params[3]; + if (SystemParametersInfo(SPI_GETMOUSE, 0, ¶ms, 0)) { + data->enhanced = params[2] ? true : false; if (params[2]) { - WIN_SetEnhancedMouseScale(mouse_speed); - } else { - WIN_SetLinearMouseScale(mouse_speed); + ReadMouseCurve(v, data->xs, data->ys); } } } diff --git a/libs/SDL3/src/video/windows/SDL_windowsrawinput.c b/libs/SDL3/src/video/windows/SDL_windowsrawinput.c index 9a003af3..5a0b13a0 100644 --- a/libs/SDL3/src/video/windows/SDL_windowsrawinput.c +++ b/libs/SDL3/src/video/windows/SDL_windowsrawinput.c @@ -29,6 +29,7 @@ #include "SDL_windowsevents.h" #include "../../joystick/usb_ids.h" +#include "../../events/SDL_events_c.h" #define ENABLE_RAW_MOUSE_INPUT 0x01 #define ENABLE_RAW_KEYBOARD_INPUT 0x02 @@ -93,14 +94,21 @@ static DWORD WINAPI WIN_RawInputThread(LPVOID param) SetEvent(data->ready_event); while (!data->done) { - if (MsgWaitForMultipleObjects(1, &data->done_event, FALSE, INFINITE, QS_RAWINPUT) != (WAIT_OBJECT_0 + 1)) { + Uint64 idle_begin = SDL_GetTicksNS(); + DWORD result = MsgWaitForMultipleObjects(1, &data->done_event, FALSE, INFINITE, QS_RAWINPUT); + Uint64 idle_end = SDL_GetTicksNS(); + if (result != (WAIT_OBJECT_0 + 1)) { break; } // Clear the queue status so MsgWaitForMultipleObjects() will wait again (void)GetQueueStatus(QS_RAWINPUT); - WIN_PollRawInput(_this); + Uint64 idle_time = idle_end - idle_begin; + Uint64 usb_8khz_interval = SDL_US_TO_NS(125); + Uint64 poll_start = idle_time < usb_8khz_interval ? _this->internal->last_rawinput_poll : idle_end; + + WIN_PollRawInput(_this, poll_start); } devices[0].dwFlags |= RIDEV_REMOVE; diff --git a/libs/SDL3/src/video/windows/SDL_windowsrawinput.h b/libs/SDL3/src/video/windows/SDL_windowsrawinput.h index a3f56c09..df03ccff 100644 --- a/libs/SDL3/src/video/windows/SDL_windowsrawinput.h +++ b/libs/SDL3/src/video/windows/SDL_windowsrawinput.h @@ -25,5 +25,6 @@ extern bool WIN_SetRawMouseEnabled(SDL_VideoDevice *_this, bool enabled); extern bool WIN_SetRawKeyboardEnabled(SDL_VideoDevice *_this, bool enabled); +extern bool WIN_RefreshRawInputEnabled(SDL_VideoDevice *_this); #endif // SDL_windowsrawinput_h_ diff --git a/libs/SDL3/src/video/windows/SDL_windowswindow.c b/libs/SDL3/src/video/windows/SDL_windowswindow.c index 6dbb001a..bceccc7a 100644 --- a/libs/SDL3/src/video/windows/SDL_windowswindow.c +++ b/libs/SDL3/src/video/windows/SDL_windowswindow.c @@ -43,9 +43,42 @@ typedef HRESULT (WINAPI *DwmSetWindowAttribute_t)(HWND hwnd, DWORD dwAttribute, typedef HRESULT (WINAPI *DwmGetWindowAttribute_t)(HWND hwnd, DWORD dwAttribute, PVOID pvAttribute, DWORD cbAttribute); // Dark mode support -#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE -#define DWMWA_USE_IMMERSIVE_DARK_MODE 20 -#endif +typedef enum { + UXTHEME_APPMODE_DEFAULT, + UXTHEME_APPMODE_ALLOW_DARK, + UXTHEME_APPMODE_FORCE_DARK, + UXTHEME_APPMODE_FORCE_LIGHT, + UXTHEME_APPMODE_MAX +} UxthemePreferredAppMode; + +typedef enum { + WCA_UNDEFINED = 0, + WCA_USEDARKMODECOLORS = 26, + WCA_LAST = 27 +} WINDOWCOMPOSITIONATTRIB; + +typedef struct { + WINDOWCOMPOSITIONATTRIB Attrib; + PVOID pvData; + SIZE_T cbData; +} WINDOWCOMPOSITIONATTRIBDATA; + +typedef struct { + ULONG dwOSVersionInfoSize; + ULONG dwMajorVersion; + ULONG dwMinorVersion; + ULONG dwBuildNumber; + ULONG dwPlatformId; + WCHAR szCSDVersion[128]; +} NT_OSVERSIONINFOW; + +typedef bool (WINAPI *ShouldAppsUseDarkMode_t)(void); +typedef void (WINAPI *AllowDarkModeForWindow_t)(HWND, bool); +typedef void (WINAPI *AllowDarkModeForApp_t)(bool); +typedef void (WINAPI *RefreshImmersiveColorPolicyState_t)(void); +typedef UxthemePreferredAppMode (WINAPI *SetPreferredAppMode_t)(UxthemePreferredAppMode); +typedef BOOL (WINAPI *SetWindowCompositionAttribute_t)(HWND, const WINDOWCOMPOSITIONATTRIBDATA *); +typedef void (NTAPI *RtlGetVersion_t)(NT_OSVERSIONINFOW *); // Corner rounding support (Win 11+) #ifndef DWMWA_WINDOW_CORNER_PREFERENCE @@ -204,6 +237,11 @@ static bool WIN_AdjustWindowRectWithStyle(SDL_Window *window, DWORD style, DWORD *width = window->floating.w; *height = window->floating.h; break; + case SDL_WINDOWRECT_PENDING: + SDL_RelativeToGlobalForWindow(window, window->pending.x, window->pending.y, x, y); + *width = window->pending.w; + *height = window->pending.h; + break; default: // Should never be here SDL_assert_release(false); @@ -348,12 +386,6 @@ bool WIN_SetWindowPositionInternal(SDL_Window *window, UINT flags, SDL_WindowRec return result; } -static void SDLCALL WIN_MouseRelativeModeCenterChanged(void *userdata, const char *name, const char *oldValue, const char *hint) -{ - SDL_WindowData *data = (SDL_WindowData *)userdata; - data->mouse_relative_mode_center = SDL_GetStringBoolean(hint, true); -} - static SDL_WindowEraseBackgroundMode GetEraseBackgroundModeHint(void) { const char *hint = SDL_GetHint(SDL_HINT_WINDOWS_ERASE_BACKGROUND_MODE); @@ -405,6 +437,16 @@ static bool SetupWindowData(SDL_VideoDevice *_this, SDL_Window *window, HWND hwn data->dwma_border_color = DWMWA_COLOR_DEFAULT; data->hint_erase_background_mode = GetEraseBackgroundModeHint(); + + // WIN_WarpCursor() jitters by +1, and remote desktop warp wobble is +/- 1 +#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) + LONG remote_desktop_adjustment = GetSystemMetrics(SM_REMOTESESSION) ? 2 : 0; + data->cursor_ctrlock_rect.left = 0 - remote_desktop_adjustment; + data->cursor_ctrlock_rect.top = 0; + data->cursor_ctrlock_rect.right = 1 + remote_desktop_adjustment; + data->cursor_ctrlock_rect.bottom = 1; +#endif + if (SDL_GetHintBoolean("SDL_WINDOW_RETAIN_CONTENT", false)) { data->copybits_flag = 0; } else { @@ -415,8 +457,6 @@ static bool SetupWindowData(SDL_VideoDevice *_this, SDL_Window *window, HWND hwn SDL_Log("SetupWindowData: initialized data->scaling_dpi to %d", data->scaling_dpi); #endif - SDL_AddHintCallback(SDL_HINT_MOUSE_RELATIVE_MODE_CENTER, WIN_MouseRelativeModeCenterChanged, data); - #if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) // Associate the data with the window if (!SetProp(hwnd, TEXT("SDL_WindowData"), data)) { @@ -460,7 +500,7 @@ static bool SetupWindowData(SDL_VideoDevice *_this, SDL_Window *window, HWND hwn } if (style & WS_THICKFRAME) { window->flags |= SDL_WINDOW_RESIZABLE; - } else { + } else if (!(style & WS_POPUP)) { window->flags &= ~SDL_WINDOW_RESIZABLE; } #ifdef WS_MAXIMIZE @@ -588,7 +628,6 @@ static void CleanupWindowData(SDL_VideoDevice *_this, SDL_Window *window) SDL_WindowData *data = window->internal; if (data) { - SDL_RemoveHintCallback(SDL_HINT_MOUSE_RELATIVE_MODE_CENTER, WIN_MouseRelativeModeCenterChanged, data); #if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) if (data->drop_target) { @@ -901,10 +940,8 @@ bool WIN_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window *window) if (!(window->flags & (SDL_WINDOW_MAXIMIZED | SDL_WINDOW_MINIMIZED))) { WIN_ConstrainPopup(window); return WIN_SetWindowPositionInternal(window, - window->internal->copybits_flag | SWP_NOZORDER | SWP_NOOWNERZORDER | - SWP_NOACTIVATE, SDL_WINDOWRECT_FLOATING); - } else { - window->internal->floating_rect_pending = true; + window->internal->copybits_flag | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOSIZE | + SWP_NOACTIVATE, SDL_WINDOWRECT_PENDING); } } else { return SDL_UpdateFullscreenMode(window, SDL_FULLSCREEN_OP_ENTER, true); @@ -916,9 +953,10 @@ bool WIN_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window *window) void WIN_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window) { if (!(window->flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_MAXIMIZED))) { - WIN_SetWindowPositionInternal(window, window->internal->copybits_flag | SWP_NOMOVE | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE, SDL_WINDOWRECT_FLOATING); + WIN_SetWindowPositionInternal(window, window->internal->copybits_flag | SWP_NOMOVE | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE, SDL_WINDOWRECT_PENDING); } else { - window->internal->floating_rect_pending = true; + // Can't resize the window + window->last_size_pending = false; } } @@ -1551,107 +1589,109 @@ static BOOL GetClientScreenRect(HWND hwnd, RECT *rect) ClientToScreen(hwnd, (LPPOINT)rect + 1); // POINT( right , bottom ) } +void WIN_UnclipCursorForWindow(SDL_Window *window) { + SDL_WindowData *data = window->internal; + RECT rect; + if (GetClipCursor(&rect) && SDL_memcmp(&rect, &data->cursor_clipped_rect, sizeof(rect)) == 0) { + ClipCursor(NULL); + SDL_zero(data->cursor_clipped_rect); + } +} + void WIN_UpdateClipCursor(SDL_Window *window) { - SDL_VideoDevice *videodevice = SDL_GetVideoDevice(); SDL_WindowData *data = window->internal; - SDL_Mouse *mouse = SDL_GetMouse(); - RECT rect, clipped_rect; - - if (data->in_title_click || data->focus_click_pending) { - return; - } - if (data->skip_update_clipcursor) { - return; - } - if (!GetClipCursor(&clipped_rect)) { + if (data->in_title_click || data->focus_click_pending || data->skip_update_clipcursor) { return; } - if ((mouse->relative_mode || (window->flags & SDL_WINDOW_MOUSE_GRABBED) || - (window->mouse_rect.w > 0 && window->mouse_rect.h > 0)) && - (window->flags & SDL_WINDOW_INPUT_FOCUS)) { - if (mouse->relative_mode && !mouse->relative_mode_warp && data->mouse_relative_mode_center) { - if (GetClientScreenRect(data->hwnd, &rect)) { - // WIN_WarpCursor() jitters by +1, and remote desktop warp wobble is +/- 1 - LONG remote_desktop_adjustment = GetSystemMetrics(SM_REMOTESESSION) ? 2 : 0; - LONG cx, cy; + SDL_Rect mouse_rect = window->mouse_rect; + bool win_mouse_rect = (mouse_rect.w > 0 && mouse_rect.h > 0); + bool win_have_focus = (window->flags & SDL_WINDOW_INPUT_FOCUS); + bool win_is_grabbed = (window->flags & SDL_WINDOW_MOUSE_GRABBED); + bool win_in_relmode = (window->flags & SDL_WINDOW_MOUSE_RELATIVE_MODE); + bool cursor_confine = win_in_relmode || win_is_grabbed || win_mouse_rect; - cx = (rect.left + rect.right) / 2; - cy = (rect.top + rect.bottom) / 2; - - // Make an absurdly small clip rect - rect.left = cx - remote_desktop_adjustment; - rect.right = cx + 1 + remote_desktop_adjustment; - rect.top = cy; - rect.bottom = cy + 1; - - if (SDL_memcmp(&rect, &clipped_rect, sizeof(rect)) != 0) { - if (ClipCursor(&rect)) { - data->cursor_clipped_rect = rect; - } - } - } - } else { - if (GetClientScreenRect(data->hwnd, &rect)) { - if (window->mouse_rect.w > 0 && window->mouse_rect.h > 0) { - SDL_Rect mouse_rect_win_client; - RECT mouse_rect, intersection; - - // mouse_rect_win_client is the mouse rect in Windows client space - mouse_rect_win_client = window->mouse_rect; - - // mouse_rect is the rect in Windows screen space - mouse_rect.left = rect.left + mouse_rect_win_client.x; - mouse_rect.top = rect.top + mouse_rect_win_client.y; - mouse_rect.right = mouse_rect.left + mouse_rect_win_client.w; - mouse_rect.bottom = mouse_rect.top + mouse_rect_win_client.h; - if (IntersectRect(&intersection, &rect, &mouse_rect)) { - SDL_memcpy(&rect, &intersection, sizeof(rect)); - } else if (window->flags & SDL_WINDOW_MOUSE_GRABBED) { - // Mouse rect was invalid, just do the normal grab - } else { - SDL_zero(rect); - } - } - if (SDL_memcmp(&rect, &clipped_rect, sizeof(rect)) != 0) { - if (!WIN_IsRectEmpty(&rect)) { - if (ClipCursor(&rect)) { - data->cursor_clipped_rect = rect; - } - } else { - ClipCursor(NULL); - SDL_zero(data->cursor_clipped_rect); - } - } - } + // This is verbatim translation of the old logic, + // but I don't quite get what it's trying to do. + // A clean-room implementation according to MSDN + // documentation of GetClipCursor is provided in + // a commented-out block below. + if (!win_have_focus || !cursor_confine) { + SDL_VideoDevice *videodevice = SDL_GetVideoDevice(); + RECT current; + if (!GetClipCursor(¤t)) { + return; } - } else { - bool unclip_cursor = false; - - // If the cursor is clipped to the screen, clear the clip state - if (!videodevice || - (clipped_rect.left == videodevice->desktop_bounds.x && - clipped_rect.top == videodevice->desktop_bounds.y)) { - unclip_cursor = true; - } else { + if (videodevice && ( + current.left != videodevice->desktop_bounds.x || + current.top != videodevice->desktop_bounds.y + )) { POINT first, second; - - first.x = clipped_rect.left; - first.y = clipped_rect.top; - second.x = clipped_rect.right - 1; - second.y = clipped_rect.bottom - 1; - if (PtInRect(&data->cursor_clipped_rect, first) && - PtInRect(&data->cursor_clipped_rect, second)) { - unclip_cursor = true; + first.x = current.left; + first.y = current.top; + second.x = current.right - 1; + second.y = current.bottom - 1; + if (!PtInRect(&data->cursor_clipped_rect, first) || + !PtInRect(&data->cursor_clipped_rect, second)) { + return; } } - if (unclip_cursor) { - ClipCursor(NULL); - SDL_zero(data->cursor_clipped_rect); + ClipCursor(NULL); + SDL_zero(data->cursor_clipped_rect); + return; + } + + // if (!win_have_focus || !cursor_confine) { + // RECT current; + // SDL_VideoDevice *videodevice = SDL_GetVideoDevice(); + // if (GetClipCursor(¤t) && (!videodevice || + // current.left != videodevice->desktop_bounds.x || + // current.top != videodevice->desktop_bounds.y || + // current.right != videodevice->desktop_bounds.x + videodevice->desktop_bounds.w || + // current.bottom != videodevice->desktop_bounds.y + videodevice->desktop_bounds.h )) { + // ClipCursor(NULL); + // SDL_zero(data->cursor_clipped_rect); + // } + // return; + // } + + SDL_Mouse *mouse = SDL_GetMouse(); + bool lock_to_ctr = (mouse->relative_mode && mouse->relative_mode_center); + + RECT client; + if (!GetClientScreenRect(data->hwnd, &client)) { + return; + } + + RECT target = client; + if (lock_to_ctr) { + LONG cx = (client.left + client.right ) / 2; + LONG cy = (client.top + client.bottom) / 2; + target = data->cursor_ctrlock_rect; + target.left += cx; + target.right += cx; + target.top += cy; + target.bottom += cy; + } else if (win_mouse_rect) { + RECT custom, overlap; + custom.left = client.left + mouse_rect.x; + custom.top = client.top + mouse_rect.y; + custom.right = client.left + mouse_rect.x + mouse_rect.w; + custom.bottom = client.top + mouse_rect.y + mouse_rect.h; + if (IntersectRect(&overlap, &client, &custom)) { + target = overlap; + } else if (!win_is_grabbed) { + WIN_UnclipCursorForWindow(window); + return; } } - data->last_updated_clipcursor = SDL_GetTicks(); + + if (GetClipCursor(&client) && + 0 != SDL_memcmp(&target, &client, sizeof(client)) && + ClipCursor(&target)) { + data->cursor_clipped_rect = target; // ClipCursor may fail if rect beyond screen + } } bool WIN_SetWindowHitTest(SDL_Window *window, bool enabled) @@ -2226,16 +2266,70 @@ bool WIN_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, bool foc void WIN_UpdateDarkModeForHWND(HWND hwnd) { - SDL_SharedObject *handle = SDL_LoadObject("dwmapi.dll"); - if (handle) { - DwmSetWindowAttribute_t DwmSetWindowAttributeFunc = (DwmSetWindowAttribute_t)SDL_LoadFunction(handle, "DwmSetWindowAttribute"); - if (DwmSetWindowAttributeFunc) { - // FIXME: Do we need to traverse children? - BOOL value = (SDL_GetSystemTheme() == SDL_SYSTEM_THEME_DARK) ? TRUE : FALSE; - DwmSetWindowAttributeFunc(hwnd, DWMWA_USE_IMMERSIVE_DARK_MODE, &value, sizeof(value)); - } - SDL_UnloadObject(handle); +#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) + SDL_SharedObject *ntdll = SDL_LoadObject("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"); + NT_OSVERSIONINFOW os_info; + os_info.dwOSVersionInfoSize = sizeof(NT_OSVERSIONINFOW); + os_info.dwBuildNumber = 0; + if (RtlGetVersionFunc) { + RtlGetVersionFunc(&os_info); + } + SDL_UnloadObject(ntdll); + os_info.dwBuildNumber &= ~0xF0000000; + if (os_info.dwBuildNumber < 17763) { + // Too old to support dark mode + return; + } + SDL_SharedObject *uxtheme = SDL_LoadObject("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)); + if (os_info.dwBuildNumber < 18362) { + AllowDarkModeForApp_t AllowDarkModeForAppFunc = (AllowDarkModeForApp_t)SDL_LoadFunction(uxtheme, MAKEINTRESOURCEA(135)); + if (AllowDarkModeForAppFunc) { + AllowDarkModeForAppFunc(true); + } + } else { + SetPreferredAppMode_t SetPreferredAppModeFunc = (SetPreferredAppMode_t)SDL_LoadFunction(uxtheme, MAKEINTRESOURCEA(135)); + if (SetPreferredAppModeFunc) { + SetPreferredAppModeFunc(UXTHEME_APPMODE_ALLOW_DARK); + } + } + if (RefreshImmersiveColorPolicyStateFunc) { + RefreshImmersiveColorPolicyStateFunc(); + } + if (AllowDarkModeForWindowFunc) { + AllowDarkModeForWindowFunc(hwnd, true); + } + BOOL value; + // Check dark mode using ShouldAppsUseDarkMode, but use SDL_GetSystemTheme as a fallback + if (ShouldAppsUseDarkModeFunc) { + value = ShouldAppsUseDarkModeFunc() ? TRUE : FALSE; + } else { + value = (SDL_GetSystemTheme() == SDL_SYSTEM_THEME_DARK) ? TRUE : FALSE; + } + SDL_UnloadObject(uxtheme); + if (os_info.dwBuildNumber < 18362) { + SetProp(hwnd, TEXT("UseImmersiveDarkModeColors"), SDL_reinterpret_cast(HANDLE, SDL_static_cast(INT_PTR, value))); + } else { + HMODULE user32 = GetModuleHandle(TEXT("user32.dll")); + if (user32) { + SetWindowCompositionAttribute_t SetWindowCompositionAttributeFunc = (SetWindowCompositionAttribute_t)GetProcAddress(user32, "SetWindowCompositionAttribute"); + if (SetWindowCompositionAttributeFunc) { + WINDOWCOMPOSITIONATTRIBDATA data = { WCA_USEDARKMODECOLORS, &value, sizeof(value) }; + SetWindowCompositionAttributeFunc(hwnd, &data); + } + } + } +#endif } bool WIN_SetWindowParent(SDL_VideoDevice *_this, SDL_Window *window, SDL_Window *parent) diff --git a/libs/SDL3/src/video/windows/SDL_windowswindow.h b/libs/SDL3/src/video/windows/SDL_windowswindow.h index 9a448a79..ddc3ddd6 100644 --- a/libs/SDL3/src/video/windows/SDL_windowswindow.h +++ b/libs/SDL3/src/video/windows/SDL_windowswindow.h @@ -38,7 +38,8 @@ typedef enum SDL_WindowRect { SDL_WINDOWRECT_CURRENT, SDL_WINDOWRECT_WINDOWED, - SDL_WINDOWRECT_FLOATING + SDL_WINDOWRECT_FLOATING, + SDL_WINDOWRECT_PENDING } SDL_WindowRect; typedef enum SDL_WindowEraseBackgroundMode @@ -76,14 +77,12 @@ struct SDL_WindowData bool expected_resize; bool in_border_change; bool in_title_click; - bool floating_rect_pending; Uint8 focus_click_pending; bool skip_update_clipcursor; - Uint64 last_updated_clipcursor; - bool mouse_relative_mode_center; bool windowed_mode_was_maximized; bool in_window_deactivation; - RECT cursor_clipped_rect; + RECT cursor_clipped_rect; // last successfully committed clipping rect for this window + RECT cursor_ctrlock_rect; // this is Windows-specific, but probably does not need to be per-window UINT windowed_mode_corner_rounding; COLORREF dwma_border_color; bool mouse_tracked; @@ -128,6 +127,7 @@ extern bool WIN_SetWindowKeyboardGrab(SDL_VideoDevice *_this, SDL_Window *window extern void WIN_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window); extern void WIN_OnWindowEnter(SDL_VideoDevice *_this, SDL_Window *window); extern void WIN_UpdateClipCursor(SDL_Window *window); +extern void WIN_UnclipCursorForWindow(SDL_Window *window); extern bool WIN_SetWindowHitTest(SDL_Window *window, bool enabled); extern void WIN_AcceptDragAndDrop(SDL_Window *window, bool accept); extern bool WIN_FlashWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_FlashOperation operation); diff --git a/libs/SDL3/src/video/x11/SDL_x11dyn.h b/libs/SDL3/src/video/x11/SDL_x11dyn.h index 8c9757be..9897b4f7 100644 --- a/libs/SDL3/src/video/x11/SDL_x11dyn.h +++ b/libs/SDL3/src/video/x11/SDL_x11dyn.h @@ -59,6 +59,9 @@ #ifdef SDL_VIDEO_DRIVER_X11_XFIXES #include #endif +#ifdef SDL_VIDEO_DRIVER_X11_XSYNC +#include +#endif #ifdef SDL_VIDEO_DRIVER_X11_XRANDR #include #endif diff --git a/libs/SDL3/src/video/x11/SDL_x11events.c b/libs/SDL3/src/video/x11/SDL_x11events.c index 562a335c..41a05f04 100644 --- a/libs/SDL3/src/video/x11/SDL_x11events.c +++ b/libs/SDL3/src/video/x11/SDL_x11events.c @@ -35,6 +35,7 @@ #include "SDL_x11xfixes.h" #include "SDL_x11settings.h" #include "../SDL_clipboard_c.h" +#include "SDL_x11xsync.h" #include "../../core/unix/SDL_poll.h" #include "../../events/SDL_events_c.h" #include "../../events/SDL_mouse_c.h" @@ -650,7 +651,9 @@ static void X11_HandleClipboardEvent(SDL_VideoDevice *_this, const XEvent *xeven int allocationsize = (length + 1) * sizeof(char*); for (j = 0, patom = (Atom*)data; j < length; j++, patom++) { - allocationsize += SDL_strlen( X11_XGetAtomName(display, *patom) ) + 1; + char *atomStr = X11_XGetAtomName(display, *patom); + allocationsize += SDL_strlen(atomStr) + 1; + X11_XFree(atomStr); } char **new_mime_types = SDL_AllocateTemporaryMemory(allocationsize); @@ -658,13 +661,19 @@ static void X11_HandleClipboardEvent(SDL_VideoDevice *_this, const XEvent *xeven char *strPtr = (char *)(new_mime_types + length + 1); for (j = 0, patom = (Atom*)data; j < length; j++, patom++) { + char *atomStr = X11_XGetAtomName(display, *patom); new_mime_types[j] = strPtr; - strPtr = stpcpy(strPtr, X11_XGetAtomName(display, *patom)) + 1; + strPtr = stpcpy(strPtr, atomStr) + 1; + X11_XFree(atomStr); } new_mime_types[length] = NULL; SDL_SendClipboardUpdate(false, new_mime_types, length); } + + if (data) { + X11_XFree(data); + } } videodata->selection_waiting = false; @@ -786,6 +795,12 @@ SDL_WindowData *X11_FindWindow(SDL_VideoDevice *_this, Window window) return NULL; } +Uint64 X11_GetEventTimestamp(unsigned long time) +{ + // FIXME: Get the event time in the SDL tick time base + return SDL_GetTicksNS(); +} + void X11_HandleKeyEvent(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_KeyboardID keyboardID, XEvent *xevent) { SDL_VideoData *videodata = (SDL_VideoData *)_this->internal; @@ -796,6 +811,7 @@ void X11_HandleKeyEvent(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_ char text[64]; Status status = 0; bool handled_by_ime = false; + Uint64 timestamp = X11_GetEventTimestamp(xevent->xkey.time); #ifdef DEBUG_XEVENTS SDL_Log("window 0x%lx %s (X11 keycode = 0x%X)\n", xevent->xany.window, (xevent->type == KeyPress ? "KeyPress" : "KeyRelease"), xevent->xkey.keycode); @@ -837,9 +853,9 @@ void X11_HandleKeyEvent(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_ videodata->filter_time = xevent->xkey.time; if (orig_event_type == KeyPress) { - SDL_SendKeyboardKey(0, keyboardID, orig_keycode, scancode, true); + SDL_SendKeyboardKey(timestamp, keyboardID, orig_keycode, scancode, true); } else { - SDL_SendKeyboardKey(0, keyboardID, orig_keycode, scancode, false); + SDL_SendKeyboardKey(timestamp, keyboardID, orig_keycode, scancode, false); } #endif return; @@ -865,7 +881,7 @@ void X11_HandleKeyEvent(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_ if (xevent->type == KeyPress) { // Don't send the key if it looks like a duplicate of a filtered key sent by an IME if (xevent->xkey.keycode != videodata->filter_code || xevent->xkey.time != videodata->filter_time) { - SDL_SendKeyboardKey(0, keyboardID, keycode, videodata->key_layout[keycode], true); + SDL_SendKeyboardKey(timestamp, keyboardID, keycode, videodata->key_layout[keycode], true); } if (*text) { text[text_length] = '\0'; @@ -876,7 +892,7 @@ void X11_HandleKeyEvent(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_ // We're about to get a repeated key down, ignore the key up return; } - SDL_SendKeyboardKey(0, keyboardID, keycode, videodata->key_layout[keycode], false); + SDL_SendKeyboardKey(timestamp, keyboardID, keycode, videodata->key_layout[keycode], false); } } @@ -885,35 +901,38 @@ void X11_HandleKeyEvent(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_ } } -void X11_HandleButtonPress(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_MouseID mouseID, int button, const float x, const float y, const unsigned long time) +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 = (SDL_VideoData *)_this->internal; Display *display = videodata->display; int xticks = 0, yticks = 0; + Uint64 timestamp = X11_GetEventTimestamp(time); + #ifdef DEBUG_XEVENTS SDL_Log("window 0x%lx: ButtonPress (X11 button = %d)\n", windowdata->xwindow, button); #endif SDL_Mouse *mouse = SDL_GetMouse(); - if ((!mouse->relative_mode || mouse->relative_mode_warp) && (x != mouse->x || y != mouse->y)) { + if (!mouse->relative_mode && (x != mouse->x || y != mouse->y)) { X11_ProcessHitTest(_this, windowdata, x, y, false); - SDL_SendMouseMotion(0, window, mouseID, false, x, y); + SDL_SendMouseMotion(timestamp, window, mouseID, false, x, y); } if (X11_IsWheelEvent(display, button, &xticks, &yticks)) { - SDL_SendMouseWheel(0, window, mouseID, (float)-xticks, (float)yticks, SDL_MOUSEWHEEL_NORMAL); + SDL_SendMouseWheel(timestamp, window, mouseID, (float)-xticks, (float)yticks, SDL_MOUSEWHEEL_NORMAL); } else { bool ignore_click = false; + if (button > 7) { + /* X button values 4-7 are used for scrolling, so X1 is 8, X2 is 9, ... + => subtract (8-SDL_BUTTON_X1) to get value SDL expects */ + button -= (8 - SDL_BUTTON_X1); + } if (button == Button1) { if (X11_TriggerHitTestAction(_this, windowdata, x, y)) { SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_HIT_TEST, 0, 0); return; // don't pass this event on to app. } - } else if (button > 7) { - /* X button values 4-7 are used for scrolling, so X1 is 8, X2 is 9, ... - => subtract (8-SDL_BUTTON_X1) to get value SDL expects */ - button -= (8 - SDL_BUTTON_X1); } if (windowdata->last_focus_event_time) { const int X11_FOCUS_CLICK_TIMEOUT = 10; @@ -923,19 +942,21 @@ void X11_HandleButtonPress(SDL_VideoDevice *_this, SDL_WindowData *windowdata, S windowdata->last_focus_event_time = 0; } if (!ignore_click) { - SDL_SendMouseButton(0, window, mouseID, button, true); + SDL_SendMouseButton(timestamp, window, mouseID, button, true); } } X11_UpdateUserTime(windowdata, time); } -void X11_HandleButtonRelease(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_MouseID mouseID, int button) +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 = (SDL_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); + #ifdef DEBUG_XEVENTS SDL_Log("window 0x%lx: ButtonRelease (X11 button = %d)\n", windowdata->xwindow, button); #endif @@ -944,7 +965,7 @@ void X11_HandleButtonRelease(SDL_VideoDevice *_this, SDL_WindowData *windowdata, // see explanation at case ButtonPress button -= (8 - SDL_BUTTON_X1); } - SDL_SendMouseButton(0, window, mouseID, button, false); + SDL_SendMouseButton(timestamp, window, mouseID, button, false); } } @@ -1368,6 +1389,11 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent) } } } + +#ifdef SDL_VIDEO_DRIVER_X11_XSYNC + X11_HandleConfigure(data->window, &xevent->xconfigure); +#endif /* SDL_VIDEO_DRIVER_X11_XSYNC */ + if (xevent->xconfigure.width != data->last_xconfigure.width || xevent->xconfigure.height != data->last_xconfigure.height) { if (!data->disable_size_position_events) { @@ -1493,6 +1519,17 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent) #endif SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_CLOSE_REQUESTED, 0, 0); break; + } else if ((xevent->xclient.message_type == videodata->atoms.WM_PROTOCOLS) && + (xevent->xclient.format == 32) && + (xevent->xclient.data.l[0] == videodata->atoms._NET_WM_SYNC_REQUEST)) { + +#ifdef DEBUG_XEVENTS + printf("window %p: _NET_WM_SYNC_REQUEST\n", data); +#endif +#ifdef SDL_VIDEO_DRIVER_X11_XSYNC + X11_HandleSyncRequest(data->window, &xevent->xclient); +#endif /* SDL_VIDEO_DRIVER_X11_XSYNC */ + break; } } break; @@ -1531,7 +1568,7 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent) } SDL_Mouse *mouse = SDL_GetMouse(); - if (!mouse->relative_mode || mouse->relative_mode_warp) { + if (!mouse->relative_mode) { #ifdef DEBUG_MOTION SDL_Log("window 0x%lx: X11 motion: %d,%d\n", xevent->xany.window, xevent->xmotion.x, xevent->xmotion.y); #endif @@ -1559,7 +1596,7 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent) break; } - X11_HandleButtonRelease(_this, data, SDL_GLOBAL_MOUSE_ID, xevent->xbutton.button); + X11_HandleButtonRelease(_this, data, SDL_GLOBAL_MOUSE_ID, xevent->xbutton.button, xevent->xbutton.time); } break; case PropertyNotify: @@ -1747,16 +1784,23 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent) } if (!(flags & (SDL_WINDOW_MAXIMIZED | SDL_WINDOW_MINIMIZED))) { data->pending_operation &= ~X11_PENDING_OP_RESTORE; - if (SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_RESTORED, 0, 0)) { - // Restore the last known floating state if leaving maximized mode - if (!(flags & SDL_WINDOW_FULLSCREEN)) { - data->pending_operation |= X11_PENDING_OP_MOVE | X11_PENDING_OP_RESIZE; - data->expected.x = data->window->floating.x - data->border_left; - data->expected.y = data->window->floating.y - data->border_top; - data->expected.w = data->window->floating.w; - data->expected.h = data->window->floating.h; - X11_XMoveWindow(display, data->xwindow, data->window->floating.x - data->border_left, data->window->floating.y - data->border_top); - X11_XResizeWindow(display, data->xwindow, data->window->floating.w, data->window->floating.h); + SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_RESTORED, 0, 0); + + // Apply any pending state if restored. + if (!(flags & SDL_WINDOW_FULLSCREEN)) { + if (data->pending_position) { + data->pending_position = false; + data->pending_operation |= X11_PENDING_OP_MOVE; + data->expected.x = data->window->pending.x - data->border_left; + data->expected.y = data->window->pending.y - data->border_top; + X11_XMoveWindow(display, data->xwindow, data->window->pending.x - data->border_left, data->window->pending.y - data->border_top); + } + if (data->pending_size) { + data->pending_size = false; + data->pending_operation |= X11_PENDING_OP_RESIZE; + data->expected.w = data->window->pending.w; + data->expected.h = data->window->pending.h; + X11_XResizeWindow(display, data->xwindow, data->window->pending.w, data->window->pending.h); } } } @@ -1787,18 +1831,28 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent) X11_GetBorderValues(data); if (data->border_top != 0 || data->border_left != 0 || data->border_right != 0 || data->border_bottom != 0) { // Adjust if the window size/position changed to accommodate the borders. - if (data->window->flags & SDL_WINDOW_MAXIMIZED) { - data->pending_operation |= X11_PENDING_OP_RESIZE; + data->pending_operation |= X11_PENDING_OP_MOVE | X11_PENDING_OP_RESIZE; + + if (data->pending_position) { + data->pending_position = false; + data->expected.x = data->window->pending.x - data->border_left; + data->expected.y = data->window->pending.y - data->border_top; + + } else { + data->expected.x = data->window->windowed.x - data->border_left; + data->expected.y = data->window->windowed.y - data->border_top; + } + + if (data->pending_size) { + data->pending_size = false; + data->expected.w = data->window->pending.w; + data->expected.h = data->window->pending.h; + } else { data->expected.w = data->window->windowed.w; data->expected.h = data->window->windowed.h; - X11_XResizeWindow(display, data->xwindow, data->window->windowed.w, data->window->windowed.h); - } else { - data->pending_operation |= X11_PENDING_OP_RESIZE | X11_PENDING_OP_MOVE; - data->expected.w = data->window->floating.w; - data->expected.h = data->window->floating.h; - X11_XMoveWindow(display, data->xwindow, data->window->floating.x - data->border_left, data->window->floating.y - data->border_top); - X11_XResizeWindow(display, data->xwindow, data->window->floating.w, data->window->floating.h); } + X11_XMoveWindow(display, data->xwindow, data->expected.x, data->expected.y - data->border_top); + X11_XResizeWindow(display, data->xwindow, data->expected.w, data->expected.h); } } if (!(data->window->flags & SDL_WINDOW_FULLSCREEN) && data->toggle_borders) { diff --git a/libs/SDL3/src/video/x11/SDL_x11events.h b/libs/SDL3/src/video/x11/SDL_x11events.h index af3fe83f..4e00f04e 100644 --- a/libs/SDL3/src/video/x11/SDL_x11events.h +++ b/libs/SDL3/src/video/x11/SDL_x11events.h @@ -29,9 +29,10 @@ extern void X11_SendWakeupEvent(SDL_VideoDevice *_this, SDL_Window *window); extern bool X11_SuspendScreenSaver(SDL_VideoDevice *_this); extern void X11_ReconcileKeyboardState(SDL_VideoDevice *_this); extern void X11_GetBorderValues(SDL_WindowData *data); +extern Uint64 X11_GetEventTimestamp(unsigned long time); extern void X11_HandleKeyEvent(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_KeyboardID keyboardID, XEvent *xevent); -extern void X11_HandleButtonPress(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_MouseID mouseID, int button, const float x, const float y, const unsigned long time); -extern void X11_HandleButtonRelease(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_MouseID mouseID, int button); +extern void X11_HandleButtonPress(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_MouseID mouseID, int button, float x, float y, unsigned long time); +extern void X11_HandleButtonRelease(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_MouseID mouseID, int button, unsigned long time); 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); diff --git a/libs/SDL3/src/video/x11/SDL_x11framebuffer.c b/libs/SDL3/src/video/x11/SDL_x11framebuffer.c index 948694af..a586ba4a 100644 --- a/libs/SDL3/src/video/x11/SDL_x11framebuffer.c +++ b/libs/SDL3/src/video/x11/SDL_x11framebuffer.c @@ -24,6 +24,7 @@ #include "SDL_x11video.h" #include "SDL_x11framebuffer.h" +#include "SDL_x11xsync.h" #ifndef NO_SHARED_MEMORY @@ -216,6 +217,10 @@ bool X11_UpdateWindowFramebuffer(SDL_VideoDevice *_this, SDL_Window *window, con } } +#ifdef SDL_VIDEO_DRIVER_X11_XSYNC + X11_HandlePresent(data->window); +#endif /* SDL_VIDEO_DRIVER_X11_XSYNC */ + X11_XSync(display, False); return true; diff --git a/libs/SDL3/src/video/x11/SDL_x11messagebox.c b/libs/SDL3/src/video/x11/SDL_x11messagebox.c index f2a5e59c..2ecaa346 100644 --- a/libs/SDL3/src/video/x11/SDL_x11messagebox.c +++ b/libs/SDL3/src/video/x11/SDL_x11messagebox.c @@ -459,10 +459,11 @@ static bool X11_MessageBoxCreateWindow(SDL_MessageBoxDataX11 *data) (unsigned char *)&_NET_WM_WINDOW_TYPE_DIALOG, 1); // Allow the window to be deleted by the window manager - data->wm_protocols = X11_XInternAtom(display, "WM_PROTOCOLS", False); data->wm_delete_message = X11_XInternAtom(display, "WM_DELETE_WINDOW", False); X11_XSetWMProtocols(display, data->window, &data->wm_delete_message, 1); + data->wm_protocols = X11_XInternAtom(display, "WM_PROTOCOLS", False); + if (windowdata) { XWindowAttributes attrib; Window dummy; diff --git a/libs/SDL3/src/video/x11/SDL_x11mouse.c b/libs/SDL3/src/video/x11/SDL_x11mouse.c index 8a366ad5..5015d957 100644 --- a/libs/SDL3/src/video/x11/SDL_x11mouse.c +++ b/libs/SDL3/src/video/x11/SDL_x11mouse.c @@ -89,12 +89,6 @@ static SDL_Cursor *X11_CreateCursorAndData(Cursor x11_cursor) return cursor; } -static SDL_Cursor *X11_CreateDefaultCursor(void) -{ - // None is used to indicate the default cursor - return X11_CreateCursorAndData(None); -} - #ifdef SDL_VIDEO_DRIVER_X11_XCURSOR static Cursor X11_CreateXCursorCursor(SDL_Surface *surface, int hot_x, int hot_y) { @@ -279,6 +273,12 @@ static SDL_Cursor *X11_CreateSystemCursor(SDL_SystemCursor id) return cursor; } +static SDL_Cursor *X11_CreateDefaultCursor(void) +{ + SDL_SystemCursor id = SDL_GetDefaultSystemCursor(); + return X11_CreateSystemCursor(id); +} + static void X11_FreeCursor(SDL_Cursor *cursor) { Cursor x11_cursor = cursor->internal->cursor; diff --git a/libs/SDL3/src/video/x11/SDL_x11opengl.c b/libs/SDL3/src/video/x11/SDL_x11opengl.c index 888c4892..e64d2f34 100644 --- a/libs/SDL3/src/video/x11/SDL_x11opengl.c +++ b/libs/SDL3/src/video/x11/SDL_x11opengl.c @@ -24,6 +24,7 @@ #ifdef SDL_VIDEO_DRIVER_X11 #include "SDL_x11video.h" +#include "SDL_x11xsync.h" // GLX implementation of SDL OpenGL support @@ -1089,6 +1090,11 @@ bool X11_GL_SwapWindow(SDL_VideoDevice *_this, SDL_Window *window) Display *display = data->videodata->display; _this->gl_data->glXSwapBuffers(display, data->xwindow); + +#ifdef SDL_VIDEO_DRIVER_X11_XSYNC + X11_HandlePresent(data->window); +#endif /* SDL_VIDEO_DRIVER_X11_XSYNC */ + return true; } diff --git a/libs/SDL3/src/video/x11/SDL_x11opengles.c b/libs/SDL3/src/video/x11/SDL_x11opengles.c index caa2d0c4..56afa615 100644 --- a/libs/SDL3/src/video/x11/SDL_x11opengles.c +++ b/libs/SDL3/src/video/x11/SDL_x11opengles.c @@ -25,6 +25,7 @@ #include "SDL_x11video.h" #include "SDL_x11opengles.h" #include "SDL_x11opengl.h" +#include "SDL_x11xsync.h" // EGL implementation of SDL OpenGL support @@ -134,7 +135,18 @@ SDL_EGLSurface X11_GLES_GetEGLSurface(SDL_VideoDevice *_this, SDL_Window *window return data->egl_surface; } -SDL_EGL_SwapWindow_impl(X11) +bool X11_GLES_SwapWindow(SDL_VideoDevice *_this, SDL_Window *window) +{ + const bool ret = SDL_EGL_SwapBuffers(_this, window->internal->egl_surface); \ + +#ifdef SDL_VIDEO_DRIVER_X11_XSYNC + X11_HandlePresent(window); +#endif /* SDL_VIDEO_DRIVER_X11_XSYNC */ + + return ret; +} + SDL_EGL_MakeCurrent_impl(X11) #endif // SDL_VIDEO_DRIVER_X11 && SDL_VIDEO_OPENGL_EGL + diff --git a/libs/SDL3/src/video/x11/SDL_x11sym.h b/libs/SDL3/src/video/x11/SDL_x11sym.h index b763d7df..a84773db 100644 --- a/libs/SDL3/src/video/x11/SDL_x11sym.h +++ b/libs/SDL3/src/video/x11/SDL_x11sym.h @@ -173,6 +173,15 @@ SDL_X11_SYM(Status, XFixesQueryVersion,(Display* a, int* b, int* c), (a,b,c), re SDL_X11_SYM(Status, XFixesSelectSelectionInput, (Display* a, Window b, Atom c, unsigned long d), (a,b,c,d), return) #endif +#ifdef SDL_VIDEO_DRIVER_X11_XSYNC +SDL_X11_MODULE(XSYNC) +SDL_X11_SYM(Status, XSyncQueryExtension, (Display* a, int* b, int* c), (a, b, c), return) +SDL_X11_SYM(Status, XSyncInitialize, (Display* a, int* b, int* c), (a, b, c), return) +SDL_X11_SYM(XSyncCounter, XSyncCreateCounter, (Display* a, XSyncValue b), (a, b), return) +SDL_X11_SYM(Status, XSyncDestroyCounter, (Display* a, XSyncCounter b), (a, b), return) +SDL_X11_SYM(Status, XSyncSetCounter, (Display* a, XSyncCounter b, XSyncValue c), (a, b, c), return) +#endif + #ifdef SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS SDL_X11_SYM(Bool,XGetEventData,(Display* a,XGenericEventCookie* b),(a,b),return) SDL_X11_SYM(void,XFreeEventData,(Display* a,XGenericEventCookie* b),(a,b),) diff --git a/libs/SDL3/src/video/x11/SDL_x11video.c b/libs/SDL3/src/video/x11/SDL_x11video.c index 5e9774ee..275bd4db 100644 --- a/libs/SDL3/src/video/x11/SDL_x11video.c +++ b/libs/SDL3/src/video/x11/SDL_x11video.c @@ -38,6 +38,7 @@ #include "SDL_x11xinput2.h" #include "SDL_x11messagebox.h" #include "SDL_x11shape.h" +#include "SDL_x11xsync.h" #ifdef SDL_VIDEO_OPENGL_EGL #include "SDL_x11opengles.h" @@ -377,6 +378,8 @@ static bool X11_VideoInit(SDL_VideoDevice *_this) GET_ATOM(_NET_WM_ICON_NAME); GET_ATOM(_NET_WM_ICON); GET_ATOM(_NET_WM_PING); + GET_ATOM(_NET_WM_SYNC_REQUEST); + GET_ATOM(_NET_WM_SYNC_REQUEST_COUNTER); GET_ATOM(_NET_WM_WINDOW_OPACITY); GET_ATOM(_NET_WM_USER_TIME); GET_ATOM(_NET_ACTIVE_WINDOW); @@ -420,6 +423,10 @@ static bool X11_VideoInit(SDL_VideoDevice *_this) X11_InitXsettings(_this); +#ifdef SDL_VIDEO_DRIVER_X11_XSYNC + X11_InitXsync(_this); +#endif /* SDL_VIDEO_DRIVER_X11_XSYNC */ + #ifndef X_HAVE_UTF8_STRING #warning X server does not support UTF8_STRING, a feature introduced in 2000! This is likely to become a hard error in a future libSDL3. #endif diff --git a/libs/SDL3/src/video/x11/SDL_x11video.h b/libs/SDL3/src/video/x11/SDL_x11video.h index 09dfe854..f801981e 100644 --- a/libs/SDL3/src/video/x11/SDL_x11video.h +++ b/libs/SDL3/src/video/x11/SDL_x11video.h @@ -89,6 +89,8 @@ struct SDL_VideoData Atom _NET_WM_ICON_NAME; Atom _NET_WM_ICON; Atom _NET_WM_PING; + Atom _NET_WM_SYNC_REQUEST; + Atom _NET_WM_SYNC_REQUEST_COUNTER; Atom _NET_WM_WINDOW_OPACITY; Atom _NET_WM_USER_TIME; Atom _NET_ACTIVE_WINDOW; @@ -135,6 +137,7 @@ struct SDL_VideoData Uint32 global_mouse_buttons; SDL_XInput2DeviceInfo *mouse_device_info; + int xinput_master_pointer_device; bool xinput_hierarchy_changed; int xrandr_event_base; diff --git a/libs/SDL3/src/video/x11/SDL_x11window.c b/libs/SDL3/src/video/x11/SDL_x11window.c index 5802a2ea..6d64bfcd 100644 --- a/libs/SDL3/src/video/x11/SDL_x11window.c +++ b/libs/SDL3/src/video/x11/SDL_x11window.c @@ -38,6 +38,8 @@ #include "SDL_x11opengles.h" #endif +#include "SDL_x11xsync.h" + #define _NET_WM_STATE_REMOVE 0l #define _NET_WM_STATE_ADD 1l @@ -124,6 +126,18 @@ static bool X11_IsActionAllowed(SDL_Window *window, Atom action) } #endif // 0 +static void X11_FlushPendingEvents(SDL_VideoDevice *_this, SDL_Window *window) +{ + // Serialize and restore the pending flags, as they may be overwritten while flushing. + const bool last_position_pending = window->last_position_pending; + const bool last_size_pending = window->last_size_pending; + + X11_SyncWindow(_this, window); + + window->last_position_pending = last_position_pending; + window->last_size_pending = last_size_pending; +} + void X11_SetNetWMState(SDL_VideoDevice *_this, Window xwindow, SDL_WindowFlags flags) { SDL_VideoData *videodata = _this->internal; @@ -182,15 +196,15 @@ void X11_SetNetWMState(SDL_VideoDevice *_this, Window xwindow, SDL_WindowFlags f } } -static void X11_ConstrainPopup(SDL_Window *window) +static void X11_ConstrainPopup(SDL_Window *window, bool use_current_position) { // Clamp popup windows to the output borders if (SDL_WINDOW_IS_POPUP(window)) { SDL_Window *w; SDL_DisplayID displayID; SDL_Rect rect; - int abs_x = window->floating.x; - int abs_y = window->floating.y; + int abs_x = use_current_position ? window->floating.x : window->pending.x; + int abs_y = use_current_position ? window->floating.y : window->pending.y; int offset_x = 0, offset_y = 0; // Calculate the total offset from the parents @@ -509,6 +523,7 @@ bool X11_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_Properties } const bool force_override_redirect = SDL_GetHintBoolean(SDL_HINT_X11_FORCE_OVERRIDE_REDIRECT, false); + const bool use_resize_sync = (window->flags & SDL_WINDOW_VULKAN); /* doesn't work well with Vulkan */ SDL_WindowData *windowdata; Display *display = data->display; int screen = displaydata->screen; @@ -670,7 +685,7 @@ bool X11_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_Properties } if (SDL_WINDOW_IS_POPUP(window)) { - X11_ConstrainPopup(window); + X11_ConstrainPopup(window, true); } SDL_RelativeToGlobalForWindow(window, window->floating.x, window->floating.y, @@ -770,7 +785,7 @@ bool X11_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_Properties } { - Atom protocols[3]; + Atom protocols[4]; int proto_count = 0; protocols[proto_count++] = data->atoms.WM_DELETE_WINDOW; // Allow window to be deleted by the WM @@ -781,6 +796,12 @@ bool X11_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_Properties protocols[proto_count++] = data->atoms._NET_WM_PING; // Respond so WM knows we're alive } +#ifdef SDL_VIDEO_DRIVER_X11_XSYNC + if (use_resize_sync) { + protocols[proto_count++] = data->atoms._NET_WM_SYNC_REQUEST; /* Respond after completing resize */ + } +#endif /* SDL_VIDEO_DRIVER_X11_XSYNC */ + SDL_assert(proto_count <= sizeof(protocols) / sizeof(protocols[0])); X11_XSetWMProtocols(display, w, protocols, proto_count); @@ -801,6 +822,12 @@ bool X11_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_Properties windowdata->fullscreen_borders_forced_on = !!(window->pending_flags & SDL_WINDOW_FULLSCREEN) && !!(window->flags & SDL_WINDOW_BORDERLESS); +#ifdef SDL_VIDEO_DRIVER_X11_XSYNC + if (use_resize_sync) { + X11_InitResizeSync(window); + } +#endif /* SDL_VIDEO_DRIVER_X11_XSYNC */ + #if defined(SDL_VIDEO_OPENGL_ES) || defined(SDL_VIDEO_OPENGL_ES2) || defined(SDL_VIDEO_OPENGL_EGL) if ((window->flags & SDL_WINDOW_OPENGL) && ((_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) || @@ -1029,8 +1056,8 @@ void X11_UpdateWindowPosition(SDL_Window *window, bool use_current_position) { SDL_WindowData *data = window->internal; Display *display = data->videodata->display; - const int rel_x = use_current_position ? window->x : window->floating.x; - const int rel_y = use_current_position ? window->y : window->floating.y; + const int rel_x = use_current_position ? window->x : window->pending.x; + const int rel_y = use_current_position ? window->y : window->pending.y; SDL_RelativeToGlobalForWindow(window, rel_x - data->border_left, rel_y - data->border_top, @@ -1045,17 +1072,20 @@ bool X11_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window *window) { // Sync any pending fullscreen or maximize events. if (window->internal->pending_operation & (X11_PENDING_OP_FULLSCREEN | X11_PENDING_OP_MAXIMIZE)) { - X11_SyncWindow(_this, window); + X11_FlushPendingEvents(_this, window); } - // Position will be set when window is de-maximized + // Set the position as pending if the window is maximized with a restore pending. if (window->flags & SDL_WINDOW_MAXIMIZED) { + if (window->internal->pending_operation & X11_PENDING_OP_RESTORE) { + window->internal->pending_position = true; + } return true; } if (!(window->flags & SDL_WINDOW_FULLSCREEN)) { if (SDL_WINDOW_IS_POPUP(window)) { - X11_ConstrainPopup(window); + X11_ConstrainPopup(window, false); } X11_UpdateWindowPosition(window, false); } else { @@ -1088,10 +1118,10 @@ static void X11_SetWMNormalHints(SDL_VideoDevice *_this, SDL_Window *window, XSi hide/show, because there are supposedly subtle problems with doing so and transitioning from windowed to fullscreen in Unity. */ - X11_XResizeWindow(display, data->xwindow, window->floating.w, window->floating.h); + X11_XResizeWindow(display, data->xwindow, window->pending.w, window->pending.h); SDL_RelativeToGlobalForWindow(window, - window->floating.x - data->border_left, - window->floating.y - data->border_top, + window->pending.x - data->border_left, + window->pending.y - data->border_top, &dest_x, &dest_y); X11_XMoveWindow(display, data->xwindow, dest_x, dest_y); X11_XRaiseWindow(display, data->xwindow); @@ -1172,15 +1202,22 @@ void X11_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window) SDL_WindowData *data = window->internal; Display *display = data->videodata->display; - /* Wait for pending maximize operations to complete, or the window can end up in a weird, - * partially-maximized state. + /* Wait for pending maximize and fullscreen operations to complete, as these windows + * don't get size changes. */ if (data->pending_operation & (X11_PENDING_OP_MAXIMIZE | X11_PENDING_OP_FULLSCREEN)) { - X11_SyncWindow(_this, window); + X11_FlushPendingEvents(_this, window); } - // Don't try to resize a maximized or fullscreen window, it will be done on restore. + // Set the size as pending if the window is being restored. if (window->flags & (SDL_WINDOW_MAXIMIZED | SDL_WINDOW_FULLSCREEN)) { + // New size will be set when the window is restored. + if (data->pending_operation & X11_PENDING_OP_RESTORE) { + data->pending_size = true; + } else { + // Can't resize the window. + window->last_size_pending = false; + } return; } @@ -1194,8 +1231,8 @@ void X11_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window) X11_XGetWMNormalHints(display, data->xwindow, sizehints, &userhints); - sizehints->min_width = sizehints->max_width = window->floating.w; - sizehints->min_height = sizehints->max_height = window->floating.h; + sizehints->min_width = sizehints->max_width = window->pending.w; + sizehints->min_height = sizehints->max_height = window->pending.h; sizehints->flags |= PMinSize | PMaxSize; X11_SetWMNormalHints(_this, window, sizehints); @@ -1203,8 +1240,8 @@ void X11_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window) X11_XFree(sizehints); } } else { - data->expected.w = window->floating.w; - data->expected.h = window->floating.h; + data->expected.w = window->pending.w; + data->expected.h = window->pending.h; data->pending_operation |= X11_PENDING_OP_RESIZE; X11_XResizeWindow(display, data->xwindow, data->expected.w, data->expected.h); } @@ -1552,7 +1589,7 @@ static bool X11_SetWindowMaximized(SDL_VideoDevice *_this, SDL_Window *window, b Atom _NET_WM_STATE_MAXIMIZED_VERT = data->videodata->atoms._NET_WM_STATE_MAXIMIZED_VERT; Atom _NET_WM_STATE_MAXIMIZED_HORZ = data->videodata->atoms._NET_WM_STATE_MAXIMIZED_HORZ; - if (!maximized && window->flags & SDL_WINDOW_FULLSCREEN) { + if (window->flags & SDL_WINDOW_FULLSCREEN) { /* Fullscreen windows are maximized on some window managers, and this is functional behavior, so don't remove that state now, we'll take care of it when we leave fullscreen mode. @@ -1608,7 +1645,13 @@ void X11_MaximizeWindow(SDL_VideoDevice *_this, SDL_Window *window) SDL_SyncWindow(window); } - if (!(window->flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_MINIMIZED))) { + if (window->flags & SDL_WINDOW_FULLSCREEN) { + // If fullscreen, just toggle the restored state. + window->internal->window_was_maximized = true; + return; + } + + if (!(window->flags & SDL_WINDOW_MINIMIZED)) { window->internal->pending_operation |= X11_PENDING_OP_MAXIMIZE; X11_SetWindowMaximized(_this, window, true); } @@ -1620,8 +1663,14 @@ void X11_MinimizeWindow(SDL_VideoDevice *_this, SDL_Window *window) SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(window); Display *display = data->videodata->display; + if (data->pending_operation & SDL_WINDOW_FULLSCREEN) { + SDL_SyncWindow(window); + } + data->pending_operation |= X11_PENDING_OP_MINIMIZE; - data->window_was_maximized = !!(window->flags & SDL_WINDOW_MAXIMIZED); + if (!(window->flags & SDL_WINDOW_FULLSCREEN)) { + data->window_was_maximized = !!(window->flags & SDL_WINDOW_MAXIMIZED); + } X11_XIconifyWindow(display, data->xwindow, displaydata->screen); X11_XFlush(display); } @@ -1632,14 +1681,19 @@ void X11_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window) SDL_SyncWindow(window); } + if ((window->flags & SDL_WINDOW_FULLSCREEN) && !(window->flags & SDL_WINDOW_MINIMIZED)) { + // If fullscreen and not minimized, just toggle the restored state. + window->internal->window_was_maximized = false; + return; + } + if (window->flags & (SDL_WINDOW_MINIMIZED | SDL_WINDOW_MAXIMIZED) || (window->internal->pending_operation & X11_PENDING_OP_MINIMIZE)) { window->internal->pending_operation |= X11_PENDING_OP_RESTORE; } // If the window was minimized while maximized, restore as maximized. - const bool maximize = !!(window->flags & SDL_WINDOW_MINIMIZED) && window->internal->window_was_maximized; - window->internal->window_was_maximized = false; + const bool maximize = !!(window->flags & SDL_WINDOW_MINIMIZED) && window->internal->window_was_maximized; X11_SetWindowMaximized(_this, window, maximize); X11_ShowWindow(_this, window); X11_SetWindowActive(_this, window); @@ -1976,6 +2030,11 @@ void X11_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window) X11_XDestroyIC(data->ic); } #endif + +#ifdef SDL_VIDEO_DRIVER_X11_XSYNC + X11_TermResizeSync(window); +#endif /* SDL_VIDEO_DRIVER_X11_XSYNC */ + if (!(window->flags & SDL_WINDOW_EXTERNAL)) { X11_XDestroyWindow(display, data->xwindow); X11_XFlush(display); diff --git a/libs/SDL3/src/video/x11/SDL_x11window.h b/libs/SDL3/src/video/x11/SDL_x11window.h index d867255b..8a9e8b29 100644 --- a/libs/SDL3/src/video/x11/SDL_x11window.h +++ b/libs/SDL3/src/video/x11/SDL_x11window.h @@ -83,6 +83,12 @@ struct SDL_WindowData PointerBarrier barrier[4]; SDL_Rect barrier_rect; #endif // SDL_VIDEO_DRIVER_X11_XFIXES +#ifdef SDL_VIDEO_DRIVER_X11_XSYNC + XSyncCounter resize_counter; + XSyncValue resize_id; + bool resize_in_progress; +#endif /* SDL_VIDEO_DRIVER_X11_XSYNC */ + SDL_Rect expected; SDL_DisplayMode requested_fullscreen_mode; @@ -97,6 +103,8 @@ struct SDL_WindowData X11_PENDING_OP_RESIZE = 0x20 } pending_operation; + bool pending_size; + bool pending_position; bool window_was_maximized; bool disable_size_position_events; bool previous_borders_nonzero; diff --git a/libs/SDL3/src/video/x11/SDL_x11xinput2.c b/libs/SDL3/src/video/x11/SDL_x11xinput2.c index 1bdead85..214a5c01 100644 --- a/libs/SDL3/src/video/x11/SDL_x11xinput2.c +++ b/libs/SDL3/src/video/x11/SDL_x11xinput2.c @@ -319,21 +319,13 @@ void X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie) double coords[2]; double processed_coords[2]; int i; + Uint64 timestamp = X11_GetEventTimestamp(rawev->time); videodata->global_mouse_changed = true; if (is_pen) { break; // Pens check for XI_Motion instead } - if (!mouse->relative_mode || mouse->relative_mode_warp) { - break; - } - - // Relative mouse motion is delivered to the window with keyboard focus - if (!SDL_GetKeyboardFocus()) { - break; - } - devinfo = xinput2_get_device_info(videodata, rawev->deviceid); if (!devinfo) { break; // oh well. @@ -350,7 +342,11 @@ void X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie) } } - SDL_SendMouseMotion(0, mouse->focus, (SDL_MouseID)rawev->sourceid, true, (float)processed_coords[0], (float)processed_coords[1]); + // Relative mouse motion is delivered to the window with keyboard focus + if (mouse->relative_mode && SDL_GetKeyboardFocus()) { + SDL_SendMouseMotion(timestamp, mouse->focus, (SDL_MouseID)rawev->sourceid, true, (float)processed_coords[0], (float)processed_coords[1]); + } + devinfo->prev_coords[0] = coords[0]; devinfo->prev_coords[1] = coords[1]; } break; @@ -430,7 +426,7 @@ void X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie) X11_HandleButtonPress(_this, windowdata, (SDL_MouseID)xev->sourceid, button, (float)xev->event_x, (float)xev->event_y, xev->time); } else { - X11_HandleButtonRelease(_this, windowdata, (SDL_MouseID)xev->sourceid, button); + X11_HandleButtonRelease(_this, windowdata, (SDL_MouseID)xev->sourceid, button, xev->time); } } } break; @@ -448,13 +444,13 @@ void X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie) videodata->global_mouse_changed = true; - if (xev->deviceid != xev->sourceid) { - // Discard events from "Master" devices to avoid duplicates. - break; - } - X11_PenHandle *pen = X11_FindPenByDeviceID(xev->deviceid); if (pen) { + if (xev->deviceid != xev->sourceid) { + // Discard events from "Master" devices to avoid duplicates. + break; + } + SDL_Window *window = xinput2_get_sdlwindow(videodata, xev->event); SDL_SendPenMotion(0, pen->pen, window, (float) xev->event_x, (float) xev->event_y); @@ -466,13 +462,14 @@ void X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie) SDL_SendPenAxis(0, pen->pen, window, (SDL_PenAxis) i, axes[i]); } } - } else if (!pointer_emulated) { + } else if (!pointer_emulated && xev->deviceid == videodata->xinput_master_pointer_device) { + // Use the master device for non-relative motion, as the slave devices can seemingly lag behind. SDL_Mouse *mouse = SDL_GetMouse(); - if (!mouse->relative_mode || mouse->relative_mode_warp) { + if (!mouse->relative_mode) { SDL_Window *window = xinput2_get_sdlwindow(videodata, xev->event); if (window) { X11_ProcessHitTest(_this, window->internal, (float)xev->event_x, (float)xev->event_y, false); - SDL_SendMouseMotion(0, window, (SDL_MouseID)xev->sourceid, false, (float)xev->event_x, (float)xev->event_y); + SDL_SendMouseMotion(0, window, SDL_GLOBAL_MOUSE_ID, false, (float)xev->event_x, (float)xev->event_y); } } } @@ -753,6 +750,8 @@ void X11_Xinput2UpdateDevices(SDL_VideoDevice *_this, bool initial_check) } break; case XIMasterPointer: + data->xinput_master_pointer_device = dev->deviceid; + SDL_FALLTHROUGH; case XISlavePointer: { SDL_MouseID mouseID = (SDL_MouseID)dev->deviceid; diff --git a/libs/SDL3/src/video/x11/SDL_x11xsync.c b/libs/SDL3/src/video/x11/SDL_x11xsync.c new file mode 100644 index 00000000..ada1ce39 --- /dev/null +++ b/libs/SDL3/src/video/x11/SDL_x11xsync.c @@ -0,0 +1,148 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2023 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#include "SDL_internal.h" + +#if defined(SDL_VIDEO_DRIVER_X11) && defined(SDL_VIDEO_DRIVER_X11_XSYNC) + +#include "SDL_x11video.h" +#include "SDL_x11xsync.h" + +static int xsync_initialized = 0; + +static int query_xsync_version(Display *display, int major, int minor) +{ + /* We don't care if this fails, so long as it sets major/minor on it's way out the door. */ + X11_XSyncInitialize(display, &major, &minor); + return (major * 1000) + minor; +} + +static bool xsync_version_atleast(const int version, const int wantmajor, const int wantminor) +{ + return version >= ((wantmajor * 1000) + wantminor); +} + +void X11_InitXsync(SDL_VideoDevice *_this) +{ + SDL_VideoData *data = (SDL_VideoData *) _this->internal; + + int version = 0; + int event, error; + int sync_opcode; + + if (!SDL_X11_HAVE_XSYNC || + !X11_XQueryExtension(data->display, "SYNC", &sync_opcode, &event, &error)) { + return; + } + + /* We need at least 5.0 for barriers. */ + version = query_xsync_version(data->display, 5, 0); + if (!xsync_version_atleast(version, 3, 0)) { + return; /* X server does not support the version we want at all. */ + } + + xsync_initialized = 1; +} + +int X11_XsyncIsInitialized(void) +{ + return xsync_initialized; +} + +int X11_InitResizeSync(SDL_Window *window) +{ + SDL_assert(window != NULL); + SDL_WindowData *data = (SDL_WindowData *) window->internal; + Display *display = data->videodata->display; + Atom counter_prop = data->videodata->atoms._NET_WM_SYNC_REQUEST_COUNTER; + XSyncCounter counter; + CARD32 counter_id; + + if (!X11_XsyncIsInitialized()){ + return SDL_Unsupported(); + } + + counter = X11_XSyncCreateCounter(display, (XSyncValue){0, 0}); + data->resize_counter = counter; + data->resize_id.lo = 0; + data->resize_id.hi = 0; + data->resize_in_progress = false; + + if (counter == None){ + return SDL_Unsupported(); + } + + counter_id = counter; + X11_XChangeProperty(display, data->xwindow, counter_prop, XA_CARDINAL, 32, + PropModeReplace, (unsigned char *)&counter_id, 1); + + return 0; +} + +void X11_TermResizeSync(SDL_Window *window) +{ + SDL_WindowData *data = (SDL_WindowData *) window->internal; + Display *display = data->videodata->display; + Atom counter_prop = data->videodata->atoms._NET_WM_SYNC_REQUEST_COUNTER; + XSyncCounter counter = data->resize_counter; + + X11_XDeleteProperty(display, data->xwindow, counter_prop); + if (counter != None) { + X11_XSyncDestroyCounter(display, counter); + } +} + +void X11_HandleSyncRequest(SDL_Window *window, XClientMessageEvent *event) +{ + SDL_WindowData *data = (SDL_WindowData *) window->internal; + + data->resize_id.lo = event->data.l[2]; + data->resize_id.hi = event->data.l[3]; + data->resize_in_progress = false; +} + +void X11_HandleConfigure(SDL_Window *window, XConfigureEvent *event) +{ + SDL_WindowData *data = (SDL_WindowData *) window->internal; + + if (data->resize_id.lo || data->resize_id.hi) { + data->resize_in_progress = true; + } +} + +void X11_HandlePresent(SDL_Window *window) +{ + SDL_WindowData *data = (SDL_WindowData *) window->internal; + Display *display = data->videodata->display; + XSyncCounter counter = data->resize_counter; + + if ((counter == None) || (!data->resize_in_progress)) { + return; + } + + X11_XSyncSetCounter(display, counter, data->resize_id); + + data->resize_id.lo = 0; + data->resize_id.hi = 0; + data->resize_in_progress = false; +} + +#endif /* SDL_VIDEO_DRIVER_X11 && SDL_VIDEO_DRIVER_X11_XSYNC */ diff --git a/libs/SDL3/src/joystick/steam/SDL_steamcontroller.c b/libs/SDL3/src/video/x11/SDL_x11xsync.h similarity index 60% rename from libs/SDL3/src/joystick/steam/SDL_steamcontroller.c rename to libs/SDL3/src/video/x11/SDL_x11xsync.h index d2bd3ecc..610e892e 100644 --- a/libs/SDL3/src/joystick/steam/SDL_steamcontroller.c +++ b/libs/SDL3/src/video/x11/SDL_x11xsync.h @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2024 Sam Lantinga + Copyright (C) 1997-2023 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -18,32 +18,22 @@ misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ + #include "SDL_internal.h" -#include "../SDL_sysjoystick.h" -#include "../SDL_joystick_c.h" -#include "SDL_steamcontroller.h" +#ifndef SDL_x11xsync_h_ +#define SDL_x11xsync_h_ -void SDL_InitSteamControllers(SteamControllerConnectedCallback_t connectedCallback, - SteamControllerDisconnectedCallback_t disconnectedCallback) -{ -} +#ifdef SDL_VIDEO_DRIVER_X11_XSYNC -void SDL_GetSteamControllerInputs(int *nbuttons, int *naxes, int *nhats) -{ - *nbuttons = 0; - *naxes = 0; - *nhats = 0; -} +extern void X11_InitXsync(SDL_VideoDevice *_this); +extern int X11_XsyncIsInitialized(void); +int X11_InitResizeSync(SDL_Window *window); +void X11_TermResizeSync(SDL_Window *window); +void X11_HandleSyncRequest(SDL_Window *window, XClientMessageEvent *event); +void X11_HandleConfigure(SDL_Window *window, XConfigureEvent *event); +void X11_HandlePresent(SDL_Window *window); -void SDL_UpdateSteamControllers(void) -{ -} +#endif /* SDL_VIDEO_DRIVER_X11_XSYNC */ -void SDL_UpdateSteamController(SDL_Joystick *joystick) -{ -} - -void SDL_QuitSteamControllers(void) -{ -} +#endif /* SDL_x11xsync_h_ */ diff --git a/libs/SDL3/src/video/yuv2rgb/yuv_rgb_lsx.c b/libs/SDL3/src/video/yuv2rgb/yuv_rgb_lsx.c index 97e46d62..250ff375 100644 --- a/libs/SDL3/src/video/yuv2rgb/yuv_rgb_lsx.c +++ b/libs/SDL3/src/video/yuv2rgb/yuv_rgb_lsx.c @@ -2,7 +2,7 @@ // Distributed under BSD 3-Clause License #include "SDL_internal.h" -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV #include "yuv_rgb_lsx.h" #include "yuv_rgb_internal.h" diff --git a/libs/SDL3/src/video/yuv2rgb/yuv_rgb_sse.c b/libs/SDL3/src/video/yuv2rgb/yuv_rgb_sse.c index b32dbf97..37fe7e44 100644 --- a/libs/SDL3/src/video/yuv2rgb/yuv_rgb_sse.c +++ b/libs/SDL3/src/video/yuv2rgb/yuv_rgb_sse.c @@ -2,7 +2,7 @@ // Distributed under BSD 3-Clause License #include "SDL_internal.h" -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV #include "yuv_rgb_internal.h" #ifdef SDL_SSE2_INTRINSICS diff --git a/libs/SDL3/src/video/yuv2rgb/yuv_rgb_std.c b/libs/SDL3/src/video/yuv2rgb/yuv_rgb_std.c index 2641fdd1..0fa900df 100644 --- a/libs/SDL3/src/video/yuv2rgb/yuv_rgb_std.c +++ b/libs/SDL3/src/video/yuv2rgb/yuv_rgb_std.c @@ -2,7 +2,7 @@ // Distributed under BSD 3-Clause License #include "SDL_internal.h" -#if SDL_HAVE_YUV +#ifdef SDL_HAVE_YUV #include "yuv_rgb_internal.h" diff --git a/libs/SDL3/test/CMakeLists.txt b/libs/SDL3/test/CMakeLists.txt index 5381ddf8..1a799482 100644 --- a/libs/SDL3/test/CMakeLists.txt +++ b/libs/SDL3/test/CMakeLists.txt @@ -331,6 +331,7 @@ files2headers(gamepad_image_headers files2headers(icon_bmp_header icon.bmp) files2headers(glass_bmp_header glass.bmp) +add_sdl_test_executable(testasyncio MAIN_CALLBACKS NEEDS_RESOURCES TESTUTILS SOURCES testasyncio.c) add_sdl_test_executable(testaudio MAIN_CALLBACKS NEEDS_RESOURCES TESTUTILS SOURCES testaudio.c) add_sdl_test_executable(testcolorspace SOURCES testcolorspace.c) add_sdl_test_executable(testfile NONINTERACTIVE SOURCES testfile.c) @@ -406,6 +407,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(testprocess diff --git a/libs/SDL3/test/LICENSE.txt b/libs/SDL3/test/LICENSE.txt index 0cad4d3f..472c6ad1 100644 --- a/libs/SDL3/test/LICENSE.txt +++ b/libs/SDL3/test/LICENSE.txt @@ -34,6 +34,14 @@ Thanks to Will for permitting us to distribute this sample with SDL! +This directory contains sword.wav: + + sword04.wav by Erdie + Original: https://freesound.org/s/27858/ + License: https://creativecommons.org/licenses/by/4.0/ + + + The .bmp files used by testaudio.c were made by AlDraw: https://linktr.ee/AlexDraw diff --git a/libs/SDL3/test/sdl-test_round.bmp b/libs/SDL3/test/sdl-test_round.bmp new file mode 100644 index 00000000..edb60497 Binary files /dev/null and b/libs/SDL3/test/sdl-test_round.bmp differ diff --git a/libs/SDL3/test/sword.wav b/libs/SDL3/test/sword.wav new file mode 100644 index 00000000..193cf543 Binary files /dev/null and b/libs/SDL3/test/sword.wav differ diff --git a/libs/SDL3/test/testasyncio.c b/libs/SDL3/test/testasyncio.c new file mode 100644 index 00000000..b330b4a9 --- /dev/null +++ b/libs/SDL3/test/testasyncio.c @@ -0,0 +1,191 @@ +/* + Copyright (C) 1997-2024 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely. +*/ + +#define SDL_MAIN_USE_CALLBACKS 1 +#include +#include +#include + +static SDL_Renderer *renderer = NULL; +static SDL_Texture *texture = NULL; +static SDL_AsyncIOQueue *queue = NULL; +static SDLTest_CommonState *state = NULL; + +SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[]) +{ + const char *base = NULL; + SDL_AsyncIO *asyncio = NULL; + char **bmps = NULL; + int bmpcount = 0; + int i; + + SDL_srand(0); + + /* Initialize test framework */ + state = SDLTest_CommonCreateState(argv, SDL_INIT_VIDEO); + if (!state) { + return SDL_APP_FAILURE; + } + + /* Enable standard application logging */ + SDL_SetLogPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO); + + /* Parse commandline */ + for (i = 1; i < argc;) { + int consumed = SDLTest_CommonArg(state, i); + if (consumed <= 0) { + static const char *options[] = { + NULL, + }; + SDLTest_CommonLogUsage(state, argv[0], options); + SDL_Quit(); + SDLTest_CommonDestroyState(state); + return 1; + } + i += consumed; + } + + state->num_windows = 1; + + /* Load the SDL library */ + if (!SDLTest_CommonInit(state)) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s", SDL_GetError()); + return SDL_APP_FAILURE; + } + + SDL_SetLogPriorities(SDL_LOG_PRIORITY_VERBOSE); + + renderer = state->renderers[0]; + if (!renderer) { + /* SDL_Log("Couldn't create renderer: %s", SDL_GetError()); */ + return SDL_APP_FAILURE; + } + + texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STATIC, 512, 512); + if (!texture) { + SDL_Log("Couldn't create texture: %s", SDL_GetError()); + return SDL_APP_FAILURE; + } else { + static const Uint32 blank[512 * 512]; + const SDL_Rect rect = { 0, 0, 512, 512 }; + SDL_UpdateTexture(texture, &rect, blank, 512 * sizeof (Uint32)); + } + + queue = SDL_CreateAsyncIOQueue(); + if (!queue) { + SDL_Log("Couldn't create async i/o queue: %s", SDL_GetError()); + return SDL_APP_FAILURE; + } + + base = SDL_GetBasePath(); + bmps = SDL_GlobDirectory(base, "*.bmp", SDL_GLOB_CASEINSENSITIVE, &bmpcount); + if (!bmps || (bmpcount == 0)) { + SDL_Log("No BMP files found."); + return SDL_APP_FAILURE; + } + + for (i = 0; i < bmpcount; i++) { + char *path = NULL; + if (SDL_asprintf(&path, "%s%s", base, bmps[i]) < 0) { + SDL_free(path); + } else { + SDL_Log("Loading %s...", path); + SDL_LoadFileAsync(path, queue, path); + } + } + + SDL_free(bmps); + + SDL_Log("Opening asyncio.tmp..."); + asyncio = SDL_AsyncIOFromFile("asyncio.tmp", "w"); + if (!asyncio) { + SDL_Log("Failed!"); + return SDL_APP_FAILURE; + } + SDL_WriteAsyncIO(asyncio, "hello", 0, 5, queue, "asyncio.tmp (write)"); + SDL_CloseAsyncIO(asyncio, true, queue, "asyncio.tmp (flush/close)"); + + return SDL_APP_CONTINUE; +} + +SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) +{ + switch (event->type) { + case SDL_EVENT_QUIT: + return SDL_APP_SUCCESS; + + default: + break; + } + + return SDLTest_CommonEventMainCallbacks(state, event); +} + +static void async_io_task_complete(const SDL_AsyncIOOutcome *outcome) +{ + const char *fname = (const char *) outcome->userdata; + const char *resultstr = "[unknown result]"; + + switch (outcome->result) { + #define RESCASE(x) case x: resultstr = #x; break + RESCASE(SDL_ASYNCIO_COMPLETE); + RESCASE(SDL_ASYNCIO_FAILURE); + RESCASE(SDL_ASYNCIO_CANCELLED); + #undef RESCASE + } + + SDL_Log("File '%s' async results: %s", fname, resultstr); + + if (SDL_strncmp(fname, "asyncio.tmp", 11) == 0) { + return; + } + + if (outcome->result == SDL_ASYNCIO_COMPLETE) { + SDL_Surface *surface = SDL_LoadBMP_IO(SDL_IOFromConstMem(outcome->buffer, (size_t) outcome->bytes_transferred), true); + if (surface) { + SDL_Surface *converted = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_RGBA8888); + SDL_DestroySurface(surface); + if (converted) { + const SDL_Rect rect = { 50 + SDL_rand(512 - 100), 50 + SDL_rand(512 - 100), converted->w, converted->h }; + SDL_UpdateTexture(texture, &rect, converted->pixels, converted->pitch); + SDL_DestroySurface(converted); + } + } + } + + SDL_free(outcome->userdata); + SDL_free(outcome->buffer); +} + +SDL_AppResult SDL_AppIterate(void *appstate) +{ + SDL_AsyncIOOutcome outcome; + if (SDL_GetAsyncIOResult(queue, &outcome)) { + async_io_task_complete(&outcome); + } + + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); + SDL_RenderClear(renderer); + SDL_RenderTexture(renderer, texture, NULL, NULL); + SDL_RenderPresent(renderer); + + return SDL_APP_CONTINUE; +} + +void SDL_AppQuit(void *appstate, SDL_AppResult result) +{ + SDL_DestroyAsyncIOQueue(queue); + SDL_DestroyTexture(texture); + SDL_RemovePath("asyncio.tmp"); + SDLTest_CommonQuit(state); +} + diff --git a/libs/SDL3/test/testautomation_events.c b/libs/SDL3/test/testautomation_events.c index 4eb1f23f..aca6fafb 100644 --- a/libs/SDL3/test/testautomation_events.c +++ b/libs/SDL3/test/testautomation_events.c @@ -203,6 +203,62 @@ static int SDLCALL events_addDelEventWatchWithUserdata(void *arg) return TEST_COMPLETED; } +/** + * Runs callbacks on the main thread. + * + * \sa SDL_IsMainThread + * \sa SDL_RunOnMainThread + * + */ + +static void SDLCALL IncrementCounter(void *userdata) +{ + int *value = (int *)userdata; + *value = *value + 1; +} + +#ifndef SDL_PLATFORM_EMSCRIPTEN /* Emscripten doesn't have threads */ +static int SDLCALL IncrementCounterThread(void *userdata) +{ + SDL_assert(!SDL_IsMainThread()); + SDL_RunOnMainThread(IncrementCounter, userdata, false); + SDL_RunOnMainThread(IncrementCounter, userdata, true); + return 0; +} +#endif /* !SDL_PLATFORM_EMSCRIPTEN */ + +static int SDLCALL events_mainThreadCallbacks(void *arg) +{ + int counter = 0; + + /* Make sure we're on the main thread */ + SDLTest_AssertCheck(SDL_IsMainThread(), "Verify we're on the main thread"); + + SDL_RunOnMainThread(IncrementCounter, &counter, true); + SDLTest_AssertCheck(counter == 1, "Incremented counter on main thread, expected 1, got %d", counter); + +#ifndef SDL_PLATFORM_EMSCRIPTEN /* Emscripten doesn't have threads */ + { + SDL_Thread *thread; + + thread = SDL_CreateThread(IncrementCounterThread, NULL, &counter); + SDLTest_AssertCheck(thread != NULL, "Create counter thread"); + + /* Wait for both increment calls to be queued up */ + SDL_Delay(100); + + /* Run the main callbacks */ + while (counter < 3) { + SDL_PumpEvents(); + } + SDL_WaitThread(thread, NULL); + SDLTest_AssertCheck(counter == 3, "Incremented counter on main thread, expected 3, got %d", counter); + } +#endif /* !SDL_PLATFORM_EMSCRIPTEN */ + + return TEST_COMPLETED; +} + /* ================= Test References ================== */ /* Events test cases */ @@ -218,11 +274,16 @@ static const SDLTest_TestCaseReference eventsTest_addDelEventWatchWithUserdata = events_addDelEventWatchWithUserdata, "events_addDelEventWatchWithUserdata", "Adds and deletes an event watch function with userdata", TEST_ENABLED }; +static const SDLTest_TestCaseReference eventsTest_mainThreadCallbacks = { + events_mainThreadCallbacks, "events_mainThreadCallbacks", "Run callbacks on the main thread", TEST_ENABLED +}; + /* Sequence of Events test cases */ static const SDLTest_TestCaseReference *eventsTests[] = { &eventsTest_pushPumpAndPollUserevent, &eventsTest_addDelEventWatch, &eventsTest_addDelEventWatchWithUserdata, + &eventsTest_mainThreadCallbacks, NULL }; diff --git a/libs/SDL3/test/testautomation_hints.c b/libs/SDL3/test/testautomation_hints.c index e5b682fd..e2c158b4 100644 --- a/libs/SDL3/test/testautomation_hints.c +++ b/libs/SDL3/test/testautomation_hints.c @@ -11,7 +11,6 @@ static const char *HintsEnum[] = { SDL_HINT_GAMECONTROLLERCONFIG, SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, SDL_HINT_MAC_CTRL_CLICK_EMULATE_RIGHT_CLICK, - SDL_HINT_MOUSE_RELATIVE_MODE_WARP, SDL_HINT_ORIENTATIONS, SDL_HINT_RENDER_DIRECT3D_THREADSAFE, SDL_HINT_RENDER_VSYNC, @@ -28,7 +27,6 @@ static const char *HintsVerbose[] = { "SDL_GAMECONTROLLERCONFIG", "SDL_JOYSTICK_ALLOW_BACKGROUND_EVENTS", "SDL_MAC_CTRL_CLICK_EMULATE_RIGHT_CLICK", - "SDL_MOUSE_RELATIVE_MODE_WARP", "SDL_ORIENTATIONS", "SDL_RENDER_DIRECT3D_THREADSAFE", "SDL_RENDER_VSYNC", diff --git a/libs/SDL3/test/testautomation_keyboard.c b/libs/SDL3/test/testautomation_keyboard.c index 62f2aefd..bf73423e 100644 --- a/libs/SDL3/test/testautomation_keyboard.c +++ b/libs/SDL3/test/testautomation_keyboard.c @@ -124,9 +124,9 @@ static int SDLCALL keyboard_getKeyFromScancode(void *arg) SDL_Keycode result; /* Case where input is valid */ - result = SDL_GetKeyFromScancode(SDL_SCANCODE_A, SDL_KMOD_NONE, false); + result = SDL_GetKeyFromScancode(SDL_SCANCODE_SPACE, SDL_KMOD_NONE, false); SDLTest_AssertPass("Call to SDL_GetKeyFromScancode(valid)"); - SDLTest_AssertCheck(result == SDLK_A, "Verify result from call, expected: %d, got: %" SDL_PRIu32, SDLK_A, result); + SDLTest_AssertCheck(result == SDLK_SPACE, "Verify result from call, expected: %d, got: %" SDL_PRIu32, SDLK_SPACE, result); /* Case where input is zero */ result = SDL_GetKeyFromScancode(SDL_SCANCODE_UNKNOWN, SDL_KMOD_NONE, false); diff --git a/libs/SDL3/test/testautomation_pixels.c b/libs/SDL3/test/testautomation_pixels.c index 8769bd93..cbe8161c 100644 --- a/libs/SDL3/test/testautomation_pixels.c +++ b/libs/SDL3/test/testautomation_pixels.c @@ -101,6 +101,27 @@ static const char *g_AllFormatsVerbose[] = { "SDL_PIXELFORMAT_NV21" }; +static const SDL_PixelFormat g_AllLargeFormats[] = { + SDL_PIXELFORMAT_RGB48, + SDL_PIXELFORMAT_BGR48, + SDL_PIXELFORMAT_RGBA64, + SDL_PIXELFORMAT_ARGB64, + SDL_PIXELFORMAT_BGRA64, + SDL_PIXELFORMAT_ABGR64, + SDL_PIXELFORMAT_RGB48_FLOAT, + SDL_PIXELFORMAT_BGR48_FLOAT, + SDL_PIXELFORMAT_RGBA64_FLOAT, + SDL_PIXELFORMAT_ARGB64_FLOAT, + SDL_PIXELFORMAT_BGRA64_FLOAT, + SDL_PIXELFORMAT_ABGR64_FLOAT, + SDL_PIXELFORMAT_RGB96_FLOAT, + SDL_PIXELFORMAT_BGR96_FLOAT, + SDL_PIXELFORMAT_RGBA128_FLOAT, + SDL_PIXELFORMAT_ARGB128_FLOAT, + SDL_PIXELFORMAT_BGRA128_FLOAT, + SDL_PIXELFORMAT_ABGR128_FLOAT +}; + /* Definition of some invalid formats for negative tests */ static Uint32 g_invalidPixelFormats[] = { SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_ABGR, SDL_PACKEDLAYOUT_1010102 + 1, 32, 4), @@ -114,68 +135,510 @@ static const char *g_invalidPixelFormatsVerbose[] = { /* Verify the pixel formats are laid out as expected */ SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_INDEX1LSB_FORMAT, SDL_PIXELFORMAT_INDEX1LSB == SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_INDEX1, SDL_BITMAPORDER_4321, 0, 1, 0)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_INDEX1LSB_FOURCC, !SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_INDEX1LSB)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_INDEX1LSB_INDEXED, SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_INDEX1LSB)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_INDEX1LSB_PACKED, !SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_INDEX1LSB)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_INDEX1LSB_ARRAY, !SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_INDEX1LSB)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_INDEX1LSB_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_INDEX1LSB)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_INDEX1LSB_FLOAT, !SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_INDEX1LSB)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_INDEX1LSB_ALPHA, !SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_INDEX1LSB)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_INDEX1MSB_FORMAT, SDL_PIXELFORMAT_INDEX1MSB == SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_INDEX1, SDL_BITMAPORDER_1234, 0, 1, 0)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_INDEX1MSB_FOURCC, !SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_INDEX1MSB)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_INDEX1MSB_INDEXED, SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_INDEX1MSB)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_INDEX1MSB_PACKED, !SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_INDEX1MSB)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_INDEX1MSB_ARRAY, !SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_INDEX1MSB)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_INDEX1MSB_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_INDEX1MSB)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_INDEX1MSB_FLOAT, !SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_INDEX1MSB)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_INDEX1MSB_ALPHA, !SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_INDEX1MSB)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_INDEX2LSB_FORMAT, SDL_PIXELFORMAT_INDEX2LSB == SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_INDEX2, SDL_BITMAPORDER_4321, 0, 2, 0)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_INDEX2LSB_FOURCC, !SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_INDEX2LSB)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_INDEX2LSB_INDEXED, SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_INDEX2LSB)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_INDEX2LSB_PACKED, !SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_INDEX2LSB)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_INDEX2LSB_ARRAY, !SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_INDEX2LSB)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_INDEX2LSB_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_INDEX2LSB)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_INDEX2LSB_FLOAT, !SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_INDEX2LSB)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_INDEX2LSB_ALPHA, !SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_INDEX2LSB)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_INDEX2MSB_FORMAT, SDL_PIXELFORMAT_INDEX2MSB == SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_INDEX2, SDL_BITMAPORDER_1234, 0, 2, 0)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_INDEX2MSB_FOURCC, !SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_INDEX2MSB)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_INDEX2MSB_INDEXED, SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_INDEX2MSB)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_INDEX2MSB_PACKED, !SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_INDEX2MSB)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_INDEX2MSB_ARRAY, !SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_INDEX2MSB)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_INDEX2MSB_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_INDEX2MSB)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_INDEX2MSB_FLOAT, !SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_INDEX2MSB)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_INDEX2MSB_ALPHA, !SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_INDEX2MSB)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_INDEX4LSB_FORMAT, SDL_PIXELFORMAT_INDEX4LSB == SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_INDEX4, SDL_BITMAPORDER_4321, 0, 4, 0)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_INDEX4LSB_FOURCC, !SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_INDEX4LSB)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_INDEX4LSB_INDEXED, SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_INDEX4LSB)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_INDEX4LSB_PACKED, !SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_INDEX4LSB)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_INDEX4LSB_ARRAY, !SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_INDEX4LSB)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_INDEX4LSB_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_INDEX4LSB)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_INDEX4LSB_FLOAT, !SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_INDEX4LSB)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_INDEX4LSB_ALPHA, !SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_INDEX4LSB)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_INDEX4MSB_FORMAT, SDL_PIXELFORMAT_INDEX4MSB == SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_INDEX4, SDL_BITMAPORDER_1234, 0, 4, 0)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_INDEX4MSB_FOURCC, !SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_INDEX4MSB)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_INDEX4MSB_INDEXED, SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_INDEX4MSB)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_INDEX4MSB_PACKED, !SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_INDEX4MSB)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_INDEX4MSB_ARRAY, !SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_INDEX4MSB)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_INDEX4MSB_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_INDEX4MSB)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_INDEX4MSB_FLOAT, !SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_INDEX4MSB)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_INDEX4MSB_ALPHA, !SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_INDEX4MSB)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_INDEX8_FORMAT, SDL_PIXELFORMAT_INDEX8 == SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_INDEX8, 0, 0, 8, 1)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_INDEX8_FOURCC, !SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_INDEX8)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_INDEX8_INDEXED, SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_INDEX8)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_INDEX8_PACKED, !SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_INDEX8)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_INDEX8_ARRAY, !SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_INDEX8)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_INDEX8_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_INDEX8)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_INDEX8_FLOAT, !SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_INDEX8)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_INDEX8_ALPHA, !SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_INDEX8)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGB332_FORMAT, SDL_PIXELFORMAT_RGB332 == SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED8, SDL_PACKEDORDER_XRGB, SDL_PACKEDLAYOUT_332, 8, 1)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGB332_FOURCC, !SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_RGB332)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGB332_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_RGB332)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGB332_PACKED, SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_RGB332)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGB332_ARRAY, !SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_RGB332)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGB332_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_RGB332)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGB332_FLOAT, !SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_RGB332)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGB332_ALPHA, !SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_RGB332)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XRGB4444_FORMAT, SDL_PIXELFORMAT_XRGB4444 == SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_XRGB, SDL_PACKEDLAYOUT_4444, 12, 2)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XRGB4444_FOURCC, !SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_XRGB4444)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XRGB4444_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_XRGB4444)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XRGB4444_PACKED, SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_XRGB4444)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XRGB4444_ARRAY, !SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_XRGB4444)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XRGB4444_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_XRGB4444)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XRGB4444_FLOAT, !SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_XRGB4444)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XRGB4444_ALPHA, !SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_XRGB4444)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XBGR4444_FORMAT, SDL_PIXELFORMAT_XBGR4444 == SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_XBGR, SDL_PACKEDLAYOUT_4444, 12, 2)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XBGR4444_FOURCC, !SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_XBGR4444)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XBGR4444_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_XBGR4444)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XBGR4444_PACKED, SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_XBGR4444)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XBGR4444_ARRAY, !SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_XBGR4444)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XBGR4444_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_XBGR4444)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XBGR4444_FLOAT, !SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_XBGR4444)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XBGR4444_ALPHA, !SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_XBGR4444)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XRGB1555_FORMAT, SDL_PIXELFORMAT_XRGB1555 == SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_XRGB, SDL_PACKEDLAYOUT_1555, 15, 2)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XRGB1555_FOURCC, !SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_XRGB1555)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XRGB1555_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_XRGB1555)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XRGB1555_PACKED, SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_XRGB1555)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XRGB1555_ARRAY, !SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_XRGB1555)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XRGB1555_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_XRGB1555)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XRGB1555_FLOAT, !SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_XRGB1555)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XRGB1555_ALPHA, !SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_XRGB1555)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XBGR1555_FORMAT, SDL_PIXELFORMAT_XBGR1555 == SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_XBGR, SDL_PACKEDLAYOUT_1555, 15, 2)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XBGR1555_FOURCC, !SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_XBGR1555)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XBGR1555_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_XBGR1555)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XBGR1555_PACKED, SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_XBGR1555)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XBGR1555_ARRAY, !SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_XBGR1555)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XBGR1555_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_XBGR1555)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XBGR1555_FLOAT, !SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_XBGR1555)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XBGR1555_ALPHA, !SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_XBGR1555)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ARGB4444_FORMAT, SDL_PIXELFORMAT_ARGB4444 == SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_ARGB, SDL_PACKEDLAYOUT_4444, 16, 2)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ARGB4444_FOURCC, !SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_ARGB4444)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ARGB4444_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_ARGB4444)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ARGB4444_PACKED, SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_ARGB4444)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ARGB4444_ARRAY, !SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_ARGB4444)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ARGB4444_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_ARGB4444)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ARGB4444_FLOAT, !SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_ARGB4444)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ARGB4444_ALPHA, SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_ARGB4444)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGBA4444_FORMAT, SDL_PIXELFORMAT_RGBA4444 == SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_RGBA, SDL_PACKEDLAYOUT_4444, 16, 2)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGBA4444_FOURCC, !SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_RGBA4444)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGBA4444_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_RGBA4444)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGBA4444_PACKED, SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_RGBA4444)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGBA4444_ARRAY, !SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_RGBA4444)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGBA4444_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_RGBA4444)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGBA4444_FLOAT, !SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_RGBA4444)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGBA4444_ALPHA, SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_RGBA4444)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ABGR4444_FORMAT, SDL_PIXELFORMAT_ABGR4444 == SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_ABGR, SDL_PACKEDLAYOUT_4444, 16, 2)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ABGR4444_FOURCC, !SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_ABGR4444)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ABGR4444_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_ABGR4444)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ABGR4444_PACKED, SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_ABGR4444)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ABGR4444_ARRAY, !SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_ABGR4444)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ABGR4444_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_ABGR4444)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ABGR4444_FLOAT, !SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_ABGR4444)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ABGR4444_ALPHA, SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_ABGR4444)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGRA4444_FORMAT, SDL_PIXELFORMAT_BGRA4444 == SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_BGRA, SDL_PACKEDLAYOUT_4444, 16, 2)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGRA4444_FOURCC, !SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_BGRA4444)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGRA4444_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_BGRA4444)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGRA4444_PACKED, SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_BGRA4444)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGRA4444_ARRAY, !SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_BGRA4444)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGRA4444_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_BGRA4444)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGRA4444_FLOAT, !SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_BGRA4444)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGRA4444_ALPHA, SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_BGRA4444)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ARGB1555_FORMAT, SDL_PIXELFORMAT_ARGB1555 == SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_ARGB, SDL_PACKEDLAYOUT_1555, 16, 2)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ARGB1555_FOURCC, !SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_ARGB1555)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ARGB1555_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_ARGB1555)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ARGB1555_PACKED, SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_ARGB1555)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ARGB1555_ARRAY, !SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_ARGB1555)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ARGB1555_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_ARGB1555)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ARGB1555_FLOAT, !SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_ARGB1555)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ARGB1555_ALPHA, SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_ARGB1555)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGBA5551_FORMAT, SDL_PIXELFORMAT_RGBA5551 == SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_RGBA, SDL_PACKEDLAYOUT_5551, 16, 2)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGBA5551_FOURCC, !SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_RGBA5551)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGBA5551_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_RGBA5551)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGBA5551_PACKED, SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_RGBA5551)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGBA5551_ARRAY, !SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_RGBA5551)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGBA5551_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_RGBA5551)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGBA5551_FLOAT, !SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_RGBA5551)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGBA5551_ALPHA, SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_RGBA5551)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ABGR1555_FORMAT, SDL_PIXELFORMAT_ABGR1555 == SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_ABGR, SDL_PACKEDLAYOUT_1555, 16, 2)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ABGR1555_FOURCC, !SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_ABGR1555)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ABGR1555_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_ABGR1555)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ABGR1555_PACKED, SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_ABGR1555)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ABGR1555_ARRAY, !SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_ABGR1555)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ABGR1555_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_ABGR1555)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ABGR1555_FLOAT, !SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_ABGR1555)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ABGR1555_ALPHA, SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_ABGR1555)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGRA5551_FORMAT, SDL_PIXELFORMAT_BGRA5551 == SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_BGRA, SDL_PACKEDLAYOUT_5551, 16, 2)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGRA5551_FOURCC, !SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_BGRA5551)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGRA5551_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_BGRA5551)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGRA5551_PACKED, SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_BGRA5551)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGRA5551_ARRAY, !SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_BGRA5551)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGRA5551_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_BGRA5551)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGRA5551_FLOAT, !SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_BGRA5551)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGRA5551_ALPHA, SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_BGRA5551)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGB565_FORMAT, SDL_PIXELFORMAT_RGB565 == SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_XRGB, SDL_PACKEDLAYOUT_565, 16, 2)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGB565_FOURCC, !SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_RGB565)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGB565_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_RGB565)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGB565_PACKED, SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_RGB565)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGB565_ARRAY, !SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_RGB565)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGB565_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_RGB565)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGB565_FLOAT, !SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_RGB565)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGB565_ALPHA, !SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_RGB565)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGR565_FORMAT, SDL_PIXELFORMAT_BGR565 == SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_XBGR, SDL_PACKEDLAYOUT_565, 16, 2)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGR565_FOURCC, !SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_BGR565)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGR565_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_BGR565)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGR565_PACKED, SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_BGR565)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGR565_ARRAY, !SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_BGR565)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGR565_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_BGR565)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGR565_FLOAT, !SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_BGR565)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGR565_ALPHA, !SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_BGR565)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGB24_FORMAT, SDL_PIXELFORMAT_RGB24 == SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_ARRAYU8, SDL_ARRAYORDER_RGB, 0, 24, 3)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGB24_FOURCC, !SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_RGB24)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGB24_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_RGB24)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGB24_PACKED, !SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_RGB24)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGB24_ARRAY, SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_RGB24)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGB24_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_RGB24)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGB24_FLOAT, !SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_RGB24)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGB24_ALPHA, !SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_RGB24)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGR24_FORMAT, SDL_PIXELFORMAT_BGR24 == SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_ARRAYU8, SDL_ARRAYORDER_BGR, 0, 24, 3)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGR24_FOURCC, !SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_BGR24)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGR24_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_BGR24)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGR24_PACKED, !SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_BGR24)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGR24_ARRAY, SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_BGR24)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGR24_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_BGR24)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGR24_FLOAT, !SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_BGR24)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGR24_ALPHA, !SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_BGR24)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XRGB8888_FORMAT, SDL_PIXELFORMAT_XRGB8888 == SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_XRGB, SDL_PACKEDLAYOUT_8888, 24, 4)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XRGB8888_FOURCC, !SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_XRGB8888)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XRGB8888_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_XRGB8888)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XRGB8888_PACKED, SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_XRGB8888)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XRGB8888_ARRAY, !SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_XRGB8888)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XRGB8888_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_XRGB8888)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XRGB8888_FLOAT, !SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_XRGB8888)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XRGB8888_ALPHA, !SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_XRGB8888)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGBX8888_FORMAT, SDL_PIXELFORMAT_RGBX8888 == SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_RGBX, SDL_PACKEDLAYOUT_8888, 24, 4)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGBX8888_FOURCC, !SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_RGBX8888)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGBX8888_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_RGBX8888)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGBX8888_PACKED, SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_RGBX8888)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGBX8888_ARRAY, !SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_RGBX8888)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGBX8888_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_RGBX8888)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGBX8888_FLOAT, !SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_RGBX8888)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGBX8888_ALPHA, !SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_RGBX8888)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XBGR8888_FORMAT, SDL_PIXELFORMAT_XBGR8888 == SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_XBGR, SDL_PACKEDLAYOUT_8888, 24, 4)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XBGR8888_FOURCC, !SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_XBGR8888)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XBGR8888_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_XBGR8888)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XBGR8888_PACKED, SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_XBGR8888)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XBGR8888_ARRAY, !SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_XBGR8888)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XBGR8888_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_XBGR8888)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XBGR8888_FLOAT, !SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_XBGR8888)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XBGR8888_ALPHA, !SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_XBGR8888)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGRX8888_FORMAT, SDL_PIXELFORMAT_BGRX8888 == SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_BGRX, SDL_PACKEDLAYOUT_8888, 24, 4)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGRX8888_FOURCC, !SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_BGRX8888)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGRX8888_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_BGRX8888)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGRX8888_PACKED, SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_BGRX8888)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGRX8888_ARRAY, !SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_BGRX8888)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGRX8888_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_BGRX8888)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGRX8888_FLOAT, !SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_BGRX8888)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGRX8888_ALPHA, !SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_BGRX8888)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ARGB8888_FORMAT, SDL_PIXELFORMAT_ARGB8888 == SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_ARGB, SDL_PACKEDLAYOUT_8888, 32, 4)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ARGB8888_FOURCC, !SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_ARGB8888)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ARGB8888_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_ARGB8888)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ARGB8888_PACKED, SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_ARGB8888)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ARGB8888_ARRAY, !SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_ARGB8888)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ARGB8888_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_ARGB8888)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ARGB8888_FLOAT, !SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_ARGB8888)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ARGB8888_ALPHA, SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_ARGB8888)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGBA8888_FORMAT, SDL_PIXELFORMAT_RGBA8888 == SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_RGBA, SDL_PACKEDLAYOUT_8888, 32, 4)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGBA8888_FOURCC, !SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_RGBA8888)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGBA8888_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_RGBA8888)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGBA8888_PACKED, SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_RGBA8888)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGBA8888_ARRAY, !SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_RGBA8888)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGBA8888_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_RGBA8888)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGBA8888_FLOAT, !SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_RGBA8888)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGBA8888_ALPHA, SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_RGBA8888)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ABGR8888_FORMAT, SDL_PIXELFORMAT_ABGR8888 == SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_ABGR, SDL_PACKEDLAYOUT_8888, 32, 4)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ABGR8888_FOURCC, !SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_ABGR8888)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ABGR8888_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_ABGR8888)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ABGR8888_PACKED, SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_ABGR8888)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ABGR8888_ARRAY, !SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_ABGR8888)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ABGR8888_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_ABGR8888)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ABGR8888_FLOAT, !SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_ABGR8888)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ABGR8888_ALPHA, SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_ABGR8888)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGRA8888_FORMAT, SDL_PIXELFORMAT_BGRA8888 == SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_BGRA, SDL_PACKEDLAYOUT_8888, 32, 4)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGRA8888_FOURCC, !SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_BGRA8888)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGRA8888_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_BGRA8888)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGRA8888_PACKED, SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_BGRA8888)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGRA8888_ARRAY, !SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_BGRA8888)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGRA8888_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_BGRA8888)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGRA8888_FLOAT, !SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_BGRA8888)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGRA8888_ALPHA, SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_BGRA8888)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XRGB2101010_FORMAT, SDL_PIXELFORMAT_XRGB2101010 == SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_XRGB, SDL_PACKEDLAYOUT_2101010, 32, 4)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XRGB2101010_FOURCC, !SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_XRGB2101010)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XRGB2101010_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_XRGB2101010)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XRGB2101010_PACKED, SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_XRGB2101010)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XRGB2101010_ARRAY, !SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_XRGB2101010)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XRGB2101010_10BIT, SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_XRGB2101010)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XRGB2101010_FLOAT, !SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_XRGB2101010)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XRGB2101010_ALPHA, !SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_XRGB2101010)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XBGR2101010_FORMAT, SDL_PIXELFORMAT_XBGR2101010 == SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_XBGR, SDL_PACKEDLAYOUT_2101010, 32, 4)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XBGR2101010_FOURCC, !SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_XBGR2101010)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XBGR2101010_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_XBGR2101010)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XBGR2101010_PACKED, SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_XBGR2101010)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XBGR2101010_ARRAY, !SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_XBGR2101010)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XBGR2101010_10BIT, SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_XBGR2101010)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XBGR2101010_FLOAT, !SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_XBGR2101010)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_XBGR2101010_ALPHA, !SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_XBGR2101010)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ARGB2101010_FORMAT, SDL_PIXELFORMAT_ARGB2101010 == SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_ARGB, SDL_PACKEDLAYOUT_2101010, 32, 4)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ARGB2101010_FOURCC, !SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_ARGB2101010)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ARGB2101010_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_ARGB2101010)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ARGB2101010_PACKED, SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_ARGB2101010)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ARGB2101010_ARRAY, !SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_ARGB2101010)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ARGB2101010_10BIT, SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_ARGB2101010)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ARGB2101010_FLOAT, !SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_ARGB2101010)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ARGB2101010_ALPHA, SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_ARGB2101010)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ABGR2101010_FORMAT, SDL_PIXELFORMAT_ABGR2101010 == SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_ABGR, SDL_PACKEDLAYOUT_2101010, 32, 4)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ABGR2101010_FOURCC, !SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_ABGR2101010)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ABGR2101010_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_ABGR2101010)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ABGR2101010_PACKED, SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_ABGR2101010)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ABGR2101010_ARRAY, !SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_ABGR2101010)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ABGR2101010_10BIT, SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_ABGR2101010)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ABGR2101010_FLOAT, !SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_ABGR2101010)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ABGR2101010_ALPHA, SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_ABGR2101010)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGB48_FORMAT, SDL_PIXELFORMAT_RGB48 == SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_ARRAYU16, SDL_ARRAYORDER_RGB, 0, 48, 6)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGB48_FOURCC, !SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_RGB48)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGB48_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_RGB48)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGB48_PACKED, !SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_RGB48)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGB48_ARRAY, SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_RGB48)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGB48_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_RGB48)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGB48_FLOAT, !SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_RGB48)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGB48_ALPHA, !SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_RGB48)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGR48_FORMAT, SDL_PIXELFORMAT_BGR48 == SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_ARRAYU16, SDL_ARRAYORDER_BGR, 0, 48, 6)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGR48_FOURCC, !SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_BGR48)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGR48_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_BGR48)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGR48_PACKED, !SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_BGR48)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGR48_ARRAY, SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_BGR48)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGR48_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_BGR48)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGR48_FLOAT, !SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_BGR48)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGR48_ALPHA, !SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_BGR48)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGBA64_FORMAT, SDL_PIXELFORMAT_RGBA64 == SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_ARRAYU16, SDL_ARRAYORDER_RGBA, 0, 64, 8)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGBA64_FOURCC, !SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_RGBA64)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGBA64_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_RGBA64)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGBA64_PACKED, !SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_RGBA64)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGBA64_ARRAY, SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_RGBA64)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGBA64_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_RGBA64)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGBA64_FLOAT, !SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_RGBA64)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGBA64_ALPHA, SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_RGBA64)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ARGB64_FORMAT, SDL_PIXELFORMAT_ARGB64 == SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_ARRAYU16, SDL_ARRAYORDER_ARGB, 0, 64, 8)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ARGB64_FOURCC, !SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_ARGB64)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ARGB64_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_ARGB64)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ARGB64_PACKED, !SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_ARGB64)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ARGB64_ARRAY, SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_ARGB64)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ARGB64_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_ARGB64)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ARGB64_FLOAT, !SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_ARGB64)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ARGB64_ALPHA, SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_ARGB64)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGRA64_FORMAT, SDL_PIXELFORMAT_BGRA64 == SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_ARRAYU16, SDL_ARRAYORDER_BGRA, 0, 64, 8)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGRA64_FOURCC, !SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_BGRA64)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGRA64_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_BGRA64)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGRA64_PACKED, !SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_BGRA64)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGRA64_ARRAY, SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_BGRA64)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGRA64_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_BGRA64)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGRA64_FLOAT, !SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_BGRA64)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGRA64_ALPHA, SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_BGRA64)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ABGR64_FORMAT, SDL_PIXELFORMAT_ABGR64 == SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_ARRAYU16, SDL_ARRAYORDER_ABGR, 0, 64, 8)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ABGR64_FOURCC, !SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_ABGR64)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ABGR64_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_ABGR64)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ABGR64_PACKED, !SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_ABGR64)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ABGR64_ARRAY, SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_ABGR64)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ABGR64_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_ABGR64)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ABGR64_FLOAT, !SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_ABGR64)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ABGR64_ALPHA, SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_ABGR64)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGB48_FLOAT_FORMAT, SDL_PIXELFORMAT_RGB48_FLOAT == SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_ARRAYF16, SDL_ARRAYORDER_RGB, 0, 48, 6)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGB48_FLOAT_FOURCC, !SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_RGB48_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGB48_FLOAT_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_RGB48_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGB48_FLOAT_PACKED, !SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_RGB48_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGB48_FLOAT_ARRAY, SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_RGB48_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGB48_FLOAT_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_RGB48_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGB48_FLOAT_FLOAT, SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_RGB48_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGB48_FLOAT_ALPHA, !SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_RGB48_FLOAT)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGR48_FLOAT_FORMAT, SDL_PIXELFORMAT_BGR48_FLOAT == SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_ARRAYF16, SDL_ARRAYORDER_BGR, 0, 48, 6)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGR48_FLOAT_FOURCC, !SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_BGR48_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGR48_FLOAT_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_BGR48_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGR48_FLOAT_PACKED, !SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_BGR48_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGR48_FLOAT_ARRAY, SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_BGR48_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGR48_FLOAT_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_BGR48_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGR48_FLOAT_FLOAT, SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_BGR48_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGR48_FLOAT_ALPHA, !SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_BGR48_FLOAT)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGBA64_FLOAT_FORMAT, SDL_PIXELFORMAT_RGBA64_FLOAT == SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_ARRAYF16, SDL_ARRAYORDER_RGBA, 0, 64, 8)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGBA64_FLOAT_FOURCC, !SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_RGBA64_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGBA64_FLOAT_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_RGBA64_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGBA64_FLOAT_PACKED, !SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_RGBA64_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGBA64_FLOAT_ARRAY, SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_RGBA64_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGBA64_FLOAT_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_RGBA64_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGBA64_FLOAT_FLOAT, SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_RGBA64_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGBA64_FLOAT_ALPHA, SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_RGBA64_FLOAT)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ARGB64_FLOAT_FORMAT, SDL_PIXELFORMAT_ARGB64_FLOAT == SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_ARRAYF16, SDL_ARRAYORDER_ARGB, 0, 64, 8)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ARGB64_FLOAT_FOURCC, !SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_ARGB64_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ARGB64_FLOAT_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_ARGB64_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ARGB64_FLOAT_PACKED, !SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_ARGB64_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ARGB64_FLOAT_ARRAY, SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_ARGB64_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ARGB64_FLOAT_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_ARGB64_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ARGB64_FLOAT_FLOAT, SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_ARGB64_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ARGB64_FLOAT_ALPHA, SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_ARGB64_FLOAT)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGRA64_FLOAT_FORMAT, SDL_PIXELFORMAT_BGRA64_FLOAT == SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_ARRAYF16, SDL_ARRAYORDER_BGRA, 0, 64, 8)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGRA64_FLOAT_FOURCC, !SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_BGRA64_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGRA64_FLOAT_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_BGRA64_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGRA64_FLOAT_PACKED, !SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_BGRA64_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGRA64_FLOAT_ARRAY, SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_BGRA64_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGRA64_FLOAT_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_BGRA64_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGRA64_FLOAT_FLOAT, SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_BGRA64_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGRA64_FLOAT_ALPHA, SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_BGRA64_FLOAT)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ABGR64_FLOAT_FORMAT, SDL_PIXELFORMAT_ABGR64_FLOAT == SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_ARRAYF16, SDL_ARRAYORDER_ABGR, 0, 64, 8)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ABGR64_FLOAT_FOURCC, !SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_ABGR64_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ABGR64_FLOAT_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_ABGR64_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ABGR64_FLOAT_PACKED, !SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_ABGR64_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ABGR64_FLOAT_ARRAY, SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_ABGR64_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ABGR64_FLOAT_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_ABGR64_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ABGR64_FLOAT_FLOAT, SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_ABGR64_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ABGR64_FLOAT_ALPHA, SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_ABGR64_FLOAT)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGB96_FLOAT_FORMAT, SDL_PIXELFORMAT_RGB96_FLOAT == SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_ARRAYF32, SDL_ARRAYORDER_RGB, 0, 96, 12)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGB96_FLOAT_FOURCC, !SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_RGB96_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGB96_FLOAT_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_RGB96_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGB96_FLOAT_PACKED, !SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_RGB96_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGB96_FLOAT_ARRAY, SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_RGB96_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGB96_FLOAT_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_RGB96_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGB96_FLOAT_FLOAT, SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_RGB96_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGB96_FLOAT_ALPHA, !SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_RGB96_FLOAT)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGR96_FLOAT_FORMAT, SDL_PIXELFORMAT_BGR96_FLOAT == SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_ARRAYF32, SDL_ARRAYORDER_BGR, 0, 96, 12)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGR96_FLOAT_FOURCC, !SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_BGR96_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGR96_FLOAT_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_BGR96_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGR96_FLOAT_PACKED, !SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_BGR96_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGR96_FLOAT_ARRAY, SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_BGR96_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGR96_FLOAT_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_BGR96_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGR96_FLOAT_FLOAT, SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_BGR96_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGR96_FLOAT_ALPHA, !SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_BGR96_FLOAT)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGBA128_FLOAT_FORMAT, SDL_PIXELFORMAT_RGBA128_FLOAT == SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_ARRAYF32, SDL_ARRAYORDER_RGBA, 0, 128, 16)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGBA128_FLOAT_FOURCC, !SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_RGBA128_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGBA128_FLOAT_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_RGBA128_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGBA128_FLOAT_PACKED, !SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_RGBA128_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGBA128_FLOAT_ARRAY, SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_RGBA128_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGBA128_FLOAT_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_RGBA128_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGBA128_FLOAT_FLOAT, SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_RGBA128_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_RGBA128_FLOAT_ALPHA, SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_RGBA128_FLOAT)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ARGB128_FLOAT_FORMAT, SDL_PIXELFORMAT_ARGB128_FLOAT == SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_ARRAYF32, SDL_ARRAYORDER_ARGB, 0, 128, 16)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ARGB128_FLOAT_FOURCC, !SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_ARGB128_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ARGB128_FLOAT_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_ARGB128_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ARGB128_FLOAT_PACKED, !SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_ARGB128_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ARGB128_FLOAT_ARRAY, SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_ARGB128_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ARGB128_FLOAT_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_ARGB128_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ARGB128_FLOAT_FLOAT, SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_ARGB128_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ARGB128_FLOAT_ALPHA, SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_ARGB128_FLOAT)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGRA128_FLOAT_FORMAT, SDL_PIXELFORMAT_BGRA128_FLOAT == SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_ARRAYF32, SDL_ARRAYORDER_BGRA, 0, 128, 16)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGRA128_FLOAT_FOURCC, !SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_BGRA128_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGRA128_FLOAT_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_BGRA128_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGRA128_FLOAT_PACKED, !SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_BGRA128_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGRA128_FLOAT_ARRAY, SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_BGRA128_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGRA128_FLOAT_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_BGRA128_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGRA128_FLOAT_FLOAT, SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_BGRA128_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_BGRA128_FLOAT_ALPHA, SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_BGRA128_FLOAT)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ABGR128_FLOAT_FORMAT, SDL_PIXELFORMAT_ABGR128_FLOAT == SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_ARRAYF32, SDL_ARRAYORDER_ABGR, 0, 128, 16)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ABGR128_FLOAT_FOURCC, !SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_ABGR128_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ABGR128_FLOAT_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_ABGR128_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ABGR128_FLOAT_PACKED, !SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_ABGR128_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ABGR128_FLOAT_ARRAY, SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_ABGR128_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ABGR128_FLOAT_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_ABGR128_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ABGR128_FLOAT_FLOAT, SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_ABGR128_FLOAT)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_ABGR128_FLOAT_ALPHA, SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_ABGR128_FLOAT)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_YV12_FORMAT, SDL_PIXELFORMAT_YV12 == SDL_DEFINE_PIXELFOURCC('Y', 'V', '1', '2')); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_YV12_FOURCC, SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_YV12)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_YV12_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_YV12)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_YV12_PACKED, !SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_YV12)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_YV12_ARRAY, !SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_YV12)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_YV12_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_YV12)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_YV12_FLOAT, !SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_YV12)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_YV12_ALPHA, !SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_YV12)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_IYUV_FORMAT, SDL_PIXELFORMAT_IYUV == SDL_DEFINE_PIXELFOURCC('I', 'Y', 'U', 'V')); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_IYUV_FOURCC, SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_IYUV)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_IYUV_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_IYUV)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_IYUV_PACKED, !SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_IYUV)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_IYUV_ARRAY, !SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_IYUV)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_IYUV_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_IYUV)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_IYUV_FLOAT, !SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_IYUV)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_IYUV_ALPHA, !SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_IYUV)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_YUY2_FORMAT, SDL_PIXELFORMAT_YUY2 == SDL_DEFINE_PIXELFOURCC('Y', 'U', 'Y', '2')); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_YUY2_FOURCC, SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_YUY2)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_YUY2_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_YUY2)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_YUY2_PACKED, !SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_YUY2)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_YUY2_ARRAY, !SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_YUY2)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_YUY2_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_YUY2)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_YUY2_FLOAT, !SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_YUY2)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_YUY2_ALPHA, !SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_YUY2)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_UYVY_FORMAT, SDL_PIXELFORMAT_UYVY == SDL_DEFINE_PIXELFOURCC('U', 'Y', 'V', 'Y')); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_UYVY_FOURCC, SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_UYVY)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_UYVY_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_UYVY)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_UYVY_PACKED, !SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_UYVY)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_UYVY_ARRAY, !SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_UYVY)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_UYVY_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_UYVY)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_UYVY_FLOAT, !SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_UYVY)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_UYVY_ALPHA, !SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_UYVY)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_YVYU_FORMAT, SDL_PIXELFORMAT_YVYU == SDL_DEFINE_PIXELFOURCC('Y', 'V', 'Y', 'U')); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_YVYU_FOURCC, SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_YVYU)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_YVYU_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_YVYU)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_YVYU_PACKED, !SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_YVYU)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_YVYU_ARRAY, !SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_YVYU)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_YVYU_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_YVYU)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_YVYU_FLOAT, !SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_YVYU)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_YVYU_ALPHA, !SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_YVYU)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_NV12_FORMAT, SDL_PIXELFORMAT_NV12 == SDL_DEFINE_PIXELFOURCC('N', 'V', '1', '2')); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_NV12_FOURCC, SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_NV12)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_NV12_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_NV12)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_NV12_PACKED, !SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_NV12)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_NV12_ARRAY, !SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_NV12)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_NV12_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_NV12)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_NV12_FLOAT, !SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_NV12)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_NV12_ALPHA, !SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_NV12)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_NV21_FORMAT, SDL_PIXELFORMAT_NV21 == SDL_DEFINE_PIXELFOURCC('N', 'V', '2', '1')); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_NV21_FOURCC, SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_NV21)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_NV21_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_NV21)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_NV21_PACKED, !SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_NV21)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_NV21_ARRAY, !SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_NV21)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_NV21_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_NV21)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_NV21_FLOAT, !SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_NV21)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_NV21_ALPHA, !SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_NV21)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_P010_FORMAT, SDL_PIXELFORMAT_P010 == SDL_DEFINE_PIXELFOURCC('P', '0', '1', '0')); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_P010_FOURCC, SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_P010)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_P010_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_P010)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_P010_PACKED, !SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_P010)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_P010_ARRAY, !SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_P010)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_P010_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_P010)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_P010_FLOAT, !SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_P010)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_P010_ALPHA, !SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_P010)); SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_EXTERNAL_OES_FORMAT, SDL_PIXELFORMAT_EXTERNAL_OES == SDL_DEFINE_PIXELFOURCC('O', 'E', 'S', ' ')); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_EXTERNAL_OES_FOURCC, SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_EXTERNAL_OES)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_EXTERNAL_OES_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_EXTERNAL_OES)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_EXTERNAL_OES_PACKED, !SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_EXTERNAL_OES)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_EXTERNAL_OES_ARRAY, !SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_EXTERNAL_OES)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_EXTERNAL_OES_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_EXTERNAL_OES)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_EXTERNAL_OES_FLOAT, !SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_EXTERNAL_OES)); +SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_EXTERNAL_OES_ALPHA, !SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_EXTERNAL_OES)); + /* Verify the colorspaces are laid out as expected */ SDL_COMPILE_TIME_ASSERT(SDL_COLORSPACE_SRGB_FORMAT, SDL_COLORSPACE_SRGB == @@ -258,6 +721,73 @@ SDL_COMPILE_TIME_ASSERT(SDL_COLORSPACE_BT2020_FULL_FORMAT, SDL_COLORSPACE_BT2020 SDL_CHROMA_LOCATION_LEFT)); /* Test case functions */ +/** + * Call to SDL_GetPixelFormatName + * + * \sa SDL_GetPixelFormatName + */ +static int SDLCALL pixels_getPixelFormatName(void *arg) +{ + const char *unknownFormat = "SDL_PIXELFORMAT_UNKNOWN"; + const char *error; + int i; + SDL_PixelFormat format; + const char *result; + + /* Blank/undefined format */ + format = SDL_PIXELFORMAT_UNKNOWN; + SDLTest_Log("RGB Format: %s (%d)", unknownFormat, format); + + /* Get name of format */ + result = SDL_GetPixelFormatName(format); + SDLTest_AssertPass("Call to SDL_GetPixelFormatName()"); + SDLTest_AssertCheck(result != NULL, "Verify result is not NULL"); + if (result != NULL) { + SDLTest_AssertCheck(result[0] != '\0', "Verify result is non-empty"); + SDLTest_AssertCheck(SDL_strcmp(result, unknownFormat) == 0, + "Verify result text; expected: %s, got %s", unknownFormat, result); + } + + /* RGB formats */ + for (i = 0; i < g_numAllFormats; i++) { + format = g_AllFormats[i]; + SDLTest_Log("RGB Format: %s (%d)", g_AllFormatsVerbose[i], format); + + /* Get name of format */ + result = SDL_GetPixelFormatName(format); + SDLTest_AssertPass("Call to SDL_GetPixelFormatName()"); + SDLTest_AssertCheck(result != NULL, "Verify result is not NULL"); + if (result != NULL) { + SDLTest_AssertCheck(result[0] != '\0', "Verify result is non-empty"); + SDLTest_AssertCheck(SDL_strcmp(result, g_AllFormatsVerbose[i]) == 0, + "Verify result text; expected: %s, got %s", g_AllFormatsVerbose[i], result); + } + } + + /* Negative cases */ + + /* Invalid Formats */ + SDL_ClearError(); + SDLTest_AssertPass("Call to SDL_ClearError()"); + for (i = 0; i < g_numInvalidPixelFormats; i++) { + format = g_invalidPixelFormats[i]; + result = SDL_GetPixelFormatName(format); + SDLTest_AssertPass("Call to SDL_GetPixelFormatName(%d)", format); + SDLTest_AssertCheck(result != NULL, "Verify result is not NULL"); + if (result != NULL) { + SDLTest_AssertCheck(result[0] != '\0', + "Verify result is non-empty; got: %s", result); + SDLTest_AssertCheck(SDL_strcmp(result, g_invalidPixelFormatsVerbose[i]) == 0, + "Validate name is UNKNOWN, expected: '%s', got: '%s'", g_invalidPixelFormatsVerbose[i], result); + } + error = SDL_GetError(); + SDLTest_AssertPass("Call to SDL_GetError()"); + SDLTest_AssertCheck(error == NULL || error[0] == '\0', "Validate that error message is empty"); + } + + return TEST_COMPLETED; +} + /** * Call to SDL_GetPixelFormatDetails */ @@ -344,73 +874,6 @@ static int SDLCALL pixels_getPixelFormatDetails(void *arg) return TEST_COMPLETED; } -/** - * Call to SDL_GetPixelFormatName - * - * \sa SDL_GetPixelFormatName - */ -static int SDLCALL pixels_getPixelFormatName(void *arg) -{ - const char *unknownFormat = "SDL_PIXELFORMAT_UNKNOWN"; - const char *error; - int i; - SDL_PixelFormat format; - const char *result; - - /* Blank/undefined format */ - format = SDL_PIXELFORMAT_UNKNOWN; - SDLTest_Log("RGB Format: %s (%d)", unknownFormat, format); - - /* Get name of format */ - result = SDL_GetPixelFormatName(format); - SDLTest_AssertPass("Call to SDL_GetPixelFormatName()"); - SDLTest_AssertCheck(result != NULL, "Verify result is not NULL"); - if (result != NULL) { - SDLTest_AssertCheck(result[0] != '\0', "Verify result is non-empty"); - SDLTest_AssertCheck(SDL_strcmp(result, unknownFormat) == 0, - "Verify result text; expected: %s, got %s", unknownFormat, result); - } - - /* RGB formats */ - for (i = 0; i < g_numAllFormats; i++) { - format = g_AllFormats[i]; - SDLTest_Log("RGB Format: %s (%d)", g_AllFormatsVerbose[i], format); - - /* Get name of format */ - result = SDL_GetPixelFormatName(format); - SDLTest_AssertPass("Call to SDL_GetPixelFormatName()"); - SDLTest_AssertCheck(result != NULL, "Verify result is not NULL"); - if (result != NULL) { - SDLTest_AssertCheck(result[0] != '\0', "Verify result is non-empty"); - SDLTest_AssertCheck(SDL_strcmp(result, g_AllFormatsVerbose[i]) == 0, - "Verify result text; expected: %s, got %s", g_AllFormatsVerbose[i], result); - } - } - - /* Negative cases */ - - /* Invalid Formats */ - SDL_ClearError(); - SDLTest_AssertPass("Call to SDL_ClearError()"); - for (i = 0; i < g_numInvalidPixelFormats; i++) { - format = g_invalidPixelFormats[i]; - result = SDL_GetPixelFormatName(format); - SDLTest_AssertPass("Call to SDL_GetPixelFormatName(%d)", format); - SDLTest_AssertCheck(result != NULL, "Verify result is not NULL"); - if (result != NULL) { - SDLTest_AssertCheck(result[0] != '\0', - "Verify result is non-empty; got: %s", result); - SDLTest_AssertCheck(SDL_strcmp(result, g_invalidPixelFormatsVerbose[i]) == 0, - "Validate name is UNKNOWN, expected: '%s', got: '%s'", g_invalidPixelFormatsVerbose[i], result); - } - error = SDL_GetError(); - SDLTest_AssertPass("Call to SDL_GetError()"); - SDLTest_AssertCheck(error == NULL || error[0] == '\0', "Validate that error message is empty"); - } - - return TEST_COMPLETED; -} - /** * Call to SDL_CreatePalette and SDL_DestroyPalette * @@ -496,24 +959,150 @@ static int SDLCALL pixels_allocFreePalette(void *arg) return TEST_COMPLETED; } +/** + * Call to SDL_SaveBMP and SDL_LoadBMP + * + * \sa SDL_SaveBMP + * \sa SDL_LoadBMP + */ +static int SDLCALL pixels_saveLoadBMP(void *arg) +{ + int i; + SDL_Surface *surface; + bool result; + + for (i = 0; i < g_numAllFormats; i++) { + SDL_PixelFormat format = g_AllFormats[i]; + + if (SDL_ISPIXELFORMAT_FOURCC(format)) { + continue; + } + if (SDL_ISPIXELFORMAT_INDEXED(format)) { + /* We'll test BMP with palette another time */ + continue; + } + + SDLTest_Log("Pixel Format: %s (%d)", g_AllFormatsVerbose[i], format); + + /* Create surface */ + surface = SDL_CreateSurface(1, 1, format); + SDLTest_AssertCheck(surface != NULL, "Verify surface is not NULL"); + + /* Fill with white */ + result = SDL_ClearSurface(surface, 1.0f, 1.0f, 1.0f, 1.0f); + SDLTest_AssertCheck(result, "Verify SDL_ClearSurface() succeeded"); + + /* Save as BMP */ + result = SDL_SaveBMP(surface, "test.bmp"); + SDLTest_AssertCheck(result, "Verify SDL_SaveBMP() succeeded"); + SDL_DestroySurface(surface); + + /* Load BMP */ + surface = SDL_LoadBMP("test.bmp"); + SDLTest_AssertCheck(surface != NULL, "Verify SDL_LoadBMP() succeeded"); + + /* Remove BMP */ + result = SDL_RemovePath("test.bmp"); + SDLTest_AssertCheck(result, "Verify SDL_RemovePath() succeeded"); + + /* Verify the BMP contents */ + if (surface) { + Uint8 *pixels = (Uint8 *)surface->pixels; + if (SDL_ISPIXELFORMAT_ALPHA(format)) { + SDLTest_AssertCheck(surface->format == SDL_PIXELFORMAT_ARGB8888, "Verify BMP surface format, expected %s, got %s", SDL_GetPixelFormatName(SDL_PIXELFORMAT_ARGB8888), SDL_GetPixelFormatName(surface->format)); + SDLTest_AssertCheck(pixels[0] == 255 && + pixels[1] == 255 && + pixels[2] == 255 && + pixels[3] == 255, + "Verify BMP colors, expected 255/255/255/255 got %d/%d/%d/%d", pixels[0], pixels[1], pixels[2], pixels[3]); + } else { + SDLTest_AssertCheck(surface->format == SDL_PIXELFORMAT_BGR24, "Verify BMP surface format, expected %s, got %s", SDL_GetPixelFormatName(SDL_PIXELFORMAT_BGR24), SDL_GetPixelFormatName(surface->format)); + SDLTest_AssertCheck(pixels[0] == 255 && + pixels[1] == 255 && + pixels[2] == 255, + "Verify BMP colors, expected 255/255/255 got %d/%d/%d", pixels[0], pixels[1], pixels[2]); + } + SDL_DestroySurface(surface); + } + } + + /* Test large formats */ + for (i = 0; i < SDL_arraysize(g_AllLargeFormats); i++) { + SDL_PixelFormat format = g_AllLargeFormats[i]; + + SDLTest_Log("Pixel Format: %s (%d)", SDL_GetPixelFormatName(format), format); + + /* Create surface */ + surface = SDL_CreateSurface(1, 1, format); + SDLTest_AssertCheck(surface != NULL, "Verify surface is not NULL"); + + /* Fill with white */ + result = SDL_ClearSurface(surface, 1.0f, 1.0f, 1.0f, 1.0f); + SDLTest_AssertCheck(result, "Verify SDL_ClearSurface() succeeded"); + + /* Save as BMP */ + result = SDL_SaveBMP(surface, "test.bmp"); + SDLTest_AssertCheck(result, "Verify SDL_SaveBMP() succeeded"); + SDL_DestroySurface(surface); + + /* Load BMP */ + surface = SDL_LoadBMP("test.bmp"); + SDLTest_AssertCheck(surface != NULL, "Verify SDL_LoadBMP() succeeded"); + + /* Remove BMP */ + result = SDL_RemovePath("test.bmp"); + SDLTest_AssertCheck(result, "Verify SDL_RemovePath() succeeded"); + + /* Verify the BMP contents */ + if (surface) { + Uint8 *pixels = (Uint8 *)surface->pixels; + if (SDL_ISPIXELFORMAT_ALPHA(format)) { + SDLTest_AssertCheck(surface->format == SDL_PIXELFORMAT_ARGB8888, "Verify BMP surface format, expected %s, got %s", SDL_GetPixelFormatName(SDL_PIXELFORMAT_ARGB8888), SDL_GetPixelFormatName(surface->format)); + SDLTest_AssertCheck(pixels[0] == 255 && + pixels[1] == 255 && + pixels[2] == 255 && + pixels[3] == 255, + "Verify BMP colors, expected 255/255/255/255 got %d/%d/%d/%d", pixels[0], pixels[1], pixels[2], pixels[3]); + } else { + SDLTest_AssertCheck(surface->format == SDL_PIXELFORMAT_BGR24, "Verify BMP surface format, expected %s, got %s", SDL_GetPixelFormatName(SDL_PIXELFORMAT_BGR24), SDL_GetPixelFormatName(surface->format)); + SDLTest_AssertCheck(pixels[0] == 255 && + pixels[1] == 255 && + pixels[2] == 255, + "Verify BMP colors, expected 255/255/255 got %d/%d/%d", pixels[0], pixels[1], pixels[2]); + } + SDL_DestroySurface(surface); + } + } + + return TEST_COMPLETED; +} + /* ================= Test References ================== */ /* Pixels test cases */ -static const SDLTest_TestCaseReference pixelsTest1 = { - pixels_getPixelFormatDetails, "pixels_allocFreeFormat", "Call to SDL_GetPixelFormatDetails", TEST_ENABLED +static const SDLTest_TestCaseReference pixelsTestGetPixelFormatName = { + pixels_getPixelFormatName, "pixels_getPixelFormatName", "Call to SDL_GetPixelFormatName", TEST_ENABLED }; -static const SDLTest_TestCaseReference pixelsTest2 = { +static const SDLTest_TestCaseReference pixelsTestGetPixelFormatDetails = { + pixels_getPixelFormatDetails, "pixels_getPixelFormatDetails", "Call to SDL_GetPixelFormatDetails", TEST_ENABLED +}; + +static const SDLTest_TestCaseReference pixelsTestAllocFreePalette = { pixels_allocFreePalette, "pixels_allocFreePalette", "Call to SDL_CreatePalette and SDL_DestroyPalette", TEST_ENABLED }; -static const SDLTest_TestCaseReference pixelsTest3 = { - pixels_getPixelFormatName, "pixels_getPixelFormatName", "Call to SDL_GetPixelFormatName", TEST_ENABLED +static const SDLTest_TestCaseReference pixelsTestSaveLoadBMP = { + pixels_saveLoadBMP, "pixels_saveLoadBMP", "Call to SDL_SaveBMP and SDL_LoadBMP", TEST_ENABLED }; /* Sequence of Pixels test cases */ static const SDLTest_TestCaseReference *pixelsTests[] = { - &pixelsTest1, &pixelsTest2, &pixelsTest3, NULL + &pixelsTestGetPixelFormatName, + &pixelsTestGetPixelFormatDetails, + &pixelsTestAllocFreePalette, + &pixelsTestSaveLoadBMP, + NULL }; /* Pixels test suite (global) */ diff --git a/libs/SDL3/test/testautomation_surface.c b/libs/SDL3/test/testautomation_surface.c index 7ba11caa..c1d641ee 100644 --- a/libs/SDL3/test/testautomation_surface.c +++ b/libs/SDL3/test/testautomation_surface.c @@ -1479,6 +1479,64 @@ static int SDLCALL surface_testPremultiplyAlpha(void *arg) } +static int SDLCALL surface_testScale(void *arg) +{ + SDL_PixelFormat formats[] = { + SDL_PIXELFORMAT_ARGB8888, SDL_PIXELFORMAT_RGBA8888, + SDL_PIXELFORMAT_ARGB2101010, SDL_PIXELFORMAT_ABGR2101010, + SDL_PIXELFORMAT_ARGB64, SDL_PIXELFORMAT_RGBA64, + SDL_PIXELFORMAT_ARGB128_FLOAT, SDL_PIXELFORMAT_RGBA128_FLOAT, + }; + SDL_ScaleMode modes[] = { + SDL_SCALEMODE_NEAREST, SDL_SCALEMODE_LINEAR + }; + SDL_Surface *surface, *result; + SDL_PixelFormat format; + SDL_ScaleMode mode; + const float MAXIMUM_ERROR = 0.0001f; + float srcR = 10 / 255.0f, srcG = 128 / 255.0f, srcB = 240 / 255.0f, srcA = 170 / 255.0f; + float actualR, actualG, actualB, actualA; + float deltaR, deltaG, deltaB, deltaA; + int i, j, ret; + + for (i = 0; i < SDL_arraysize(formats); ++i) { + for (j = 0; j < SDL_arraysize(modes); ++j) { + format = formats[i]; + mode = modes[j]; + + surface = SDL_CreateSurface(1, 1, format); + SDLTest_AssertCheck(surface != NULL, "SDL_CreateSurface()"); + ret = SDL_SetSurfaceColorspace(surface, SDL_COLORSPACE_SRGB); + SDLTest_AssertCheck(ret == true, "SDL_SetSurfaceColorspace()"); + ret = SDL_ClearSurface(surface, srcR, srcG, srcB, srcA); + SDLTest_AssertCheck(ret == true, "SDL_ClearSurface()"); + result = SDL_ScaleSurface(surface, 2, 2, mode); + SDLTest_AssertCheck(ret == true, "SDL_PremultiplySurfaceAlpha()"); + ret = SDL_ReadSurfacePixelFloat(result, 1, 1, &actualR, &actualG, &actualB, &actualA); + SDLTest_AssertCheck(ret == true, "SDL_ReadSurfacePixelFloat()"); + deltaR = SDL_fabsf(actualR - srcR); + deltaG = SDL_fabsf(actualG - srcG); + deltaB = SDL_fabsf(actualB - srcB); + deltaA = SDL_fabsf(actualA - srcA); + SDLTest_AssertCheck( + deltaR <= MAXIMUM_ERROR && + deltaG <= MAXIMUM_ERROR && + deltaB <= MAXIMUM_ERROR && + deltaA <= MAXIMUM_ERROR, + "Checking %s %s scaling results, expected %.4f,%.4f,%.4f,%.4f got %.4f,%.4f,%.4f,%.4f", + SDL_GetPixelFormatName(format), + mode == SDL_SCALEMODE_NEAREST ? "nearest" : "linear", + srcR, srcG, srcB, srcA, actualR, actualG, actualB, actualA); + + SDL_DestroySurface(surface); + SDL_DestroySurface(result); + } + } + + return TEST_COMPLETED; +} + + /* ================= Test References ================== */ /* Surface test cases */ @@ -1574,6 +1632,10 @@ static const SDLTest_TestCaseReference surfaceTestPremultiplyAlpha = { surface_testPremultiplyAlpha, "surface_testPremultiplyAlpha", "Test alpha premultiply operations.", TEST_ENABLED }; +static const SDLTest_TestCaseReference surfaceTestScale = { + surface_testScale, "surface_testScale", "Test scaling operations.", TEST_ENABLED +}; + /* Sequence of Surface test cases */ static const SDLTest_TestCaseReference *surfaceTests[] = { &surfaceTestSaveLoadBitmap, @@ -1599,6 +1661,7 @@ static const SDLTest_TestCaseReference *surfaceTests[] = { &surfaceTestPalettization, &surfaceTestClearSurface, &surfaceTestPremultiplyAlpha, + &surfaceTestScale, NULL }; diff --git a/libs/SDL3/test/testautomation_video.c b/libs/SDL3/test/testautomation_video.c index e9b2433f..5547f965 100644 --- a/libs/SDL3/test/testautomation_video.c +++ b/libs/SDL3/test/testautomation_video.c @@ -1843,7 +1843,7 @@ static int SDLCALL video_setWindowCenteredOnDisplay(void *arg) SDLTest_AssertCheck(currentW == w, "Validate width (current: %d, expected: %d)", currentW, w); SDLTest_AssertCheck(currentH == h, "Validate height (current: %d, expected: %d)", currentH, h); } - if (video_driver_is_wayland) { + if (video_driver_is_emscripten || video_driver_is_wayland) { SDLTest_Log("Skipping window position validation: %s driver does not support window positioning", video_driver); } else { SDLTest_AssertCheck(currentX == expectedX, "Validate x (current: %d, expected: %d)", currentX, expectedX); @@ -1879,7 +1879,7 @@ static int SDLCALL video_setWindowCenteredOnDisplay(void *arg) SDLTest_AssertCheck(currentW == w, "Validate width (current: %d, expected: %d)", currentW, w); SDLTest_AssertCheck(currentH == h, "Validate height (current: %d, expected: %d)", currentH, h); } - if (video_driver_is_wayland) { + if (video_driver_is_emscripten || video_driver_is_wayland) { SDLTest_Log("Skipping window position validation: %s driver does not support window positioning", video_driver); } else { SDLTest_AssertCheck(currentX == expectedX, "Validate x (current: %d, expected: %d)", currentX, expectedX); @@ -2133,7 +2133,54 @@ static int SDLCALL video_getSetWindowState(void *arg) SDLTest_AssertCheck(windowedW == currentW, "Verify returned width; expected: %d, got: %d", windowedW, currentW); SDLTest_AssertCheck(windowedH == currentH, "Verify returned height; expected: %d, got: %d", windowedH, currentH); - /* Maximize, change size, and restore */ + /* Maximize, restore, and change size */ + result = SDL_MaximizeWindow(window); + SDLTest_AssertPass("SDL_MaximizeWindow()"); + SDLTest_AssertCheck(result == true, "Verify return value; expected: true, got: %d", result); + + result = SDL_RestoreWindow(window); + SDLTest_AssertPass("SDL_RestoreWindow()"); + SDLTest_AssertCheck(result == true, "Verify return value; expected: true, got: %d", result); + + desiredW = windowedW + 10; + desiredH = windowedH + 10; + result = SDL_SetWindowSize(window, desiredW, desiredH); + SDLTest_AssertPass("SDL_SetWindowSize()"); + SDLTest_AssertCheck(result == true, "Verify return value; expected: true, got: %d", result); + + if (!skipPos) { + desiredX = windowedX + 10; + desiredY = windowedY + 10; + result = SDL_SetWindowPosition(window, desiredX, desiredY); + SDLTest_AssertPass("SDL_SetWindowPosition()"); + SDLTest_AssertCheck(result == true, "Verify return value; expected: true, got: %d", result); + } + + result = SDL_SyncWindow(window); + SDLTest_AssertPass("SDL_SyncWindow()"); + SDLTest_AssertCheck(result == true, "Verify return value; expected: true, got: %d", result); + + flags = SDL_GetWindowFlags(window); + SDLTest_AssertPass("SDL_GetWindowFlags()"); + SDLTest_AssertCheck(!(flags & SDL_WINDOW_MAXIMIZED), "Verify that the `SDL_WINDOW_MAXIMIZED` flag is cleared: %s", !(flags & SDL_WINDOW_MAXIMIZED) ? "true" : "false"); + + if (!skipPos) { + currentX = desiredX + 1; + currentY = desiredY + 1; + SDL_GetWindowPosition(window, ¤tX, ¤tY); + SDLTest_AssertPass("Call to SDL_GetWindowPosition()"); + SDLTest_AssertCheck(desiredX == currentX, "Verify returned X coordinate; expected: %d, got: %d", desiredX, currentX); + SDLTest_AssertCheck(desiredY == currentY, "Verify returned Y coordinate; expected: %d, got: %d", desiredY, currentY); + } + + currentW = desiredW + 1; + currentH = desiredH + 1; + SDL_GetWindowSize(window, ¤tW, ¤tH); + SDLTest_AssertPass("Call to SDL_GetWindowSize()"); + SDLTest_AssertCheck(desiredW == currentW, "Verify returned width; expected: %d, got: %d", desiredW, currentW); + SDLTest_AssertCheck(desiredH == currentH, "Verify returned height; expected: %d, got: %d", desiredH, currentH); + + /* Maximize, change size/position (should be ignored), and restore. */ result = SDL_MaximizeWindow(window); SDLTest_AssertPass("SDL_MaximizeWindow()"); SDLTest_AssertCheck(result == true, "Verify return value; expected: true, got: %d", result); @@ -2165,20 +2212,20 @@ static int SDLCALL video_getSetWindowState(void *arg) SDLTest_AssertCheck(!(flags & SDL_WINDOW_MAXIMIZED), "Verify that the `SDL_WINDOW_MAXIMIZED` flag is cleared: %s", !(flags & SDL_WINDOW_MAXIMIZED) ? "true" : "false"); if (!skipPos) { - currentX = desiredX + 1; - currentY = desiredY + 1; - SDL_GetWindowPosition(window, ¤tX, ¤tY); + int previousX = desiredX + 1; + int previousY = desiredY + 1; + SDL_GetWindowPosition(window, &previousX, &previousY); SDLTest_AssertPass("Call to SDL_GetWindowPosition()"); - SDLTest_AssertCheck(desiredX == currentX, "Verify returned X coordinate; expected: %d, got: %d", desiredX, currentX); - SDLTest_AssertCheck(desiredY == currentY, "Verify returned Y coordinate; expected: %d, got: %d", desiredY, currentY); + SDLTest_AssertCheck(desiredX == currentX, "Verify returned X coordinate; expected: %d, got: %d", previousX, currentX); + SDLTest_AssertCheck(desiredY == currentY, "Verify returned Y coordinate; expected: %d, got: %d", previousY, currentY); } - currentW = desiredW + 1; - currentH = desiredH + 1; - SDL_GetWindowSize(window, ¤tW, ¤tH); + int previousW = desiredW + 1; + int previousH = desiredH + 1; + SDL_GetWindowSize(window, &previousW, &previousH); SDLTest_AssertPass("Call to SDL_GetWindowSize()"); - SDLTest_AssertCheck(desiredW == currentW, "Verify returned width; expected: %d, got: %d", desiredW, currentW); - SDLTest_AssertCheck(desiredH == currentH, "Verify returned height; expected: %d, got: %d", desiredH, currentH); + SDLTest_AssertCheck(desiredW == currentW, "Verify returned width; expected: %d, got: %d", previousW, currentW); + SDLTest_AssertCheck(desiredH == currentH, "Verify returned height; expected: %d, got: %d", previousH, currentH); /* Change size and position, maximize and restore */ desiredW = windowedW - 5; diff --git a/libs/SDL3/test/testcontroller.c b/libs/SDL3/test/testcontroller.c index dc032ff1..6b587415 100644 --- a/libs/SDL3/test/testcontroller.c +++ b/libs/SDL3/test/testcontroller.c @@ -1027,7 +1027,10 @@ static void HandleGamepadAdded(SDL_JoystickID id, bool verbose) SDL_PropertiesID props = SDL_GetGamepadProperties(gamepad); const char *name = SDL_GetGamepadName(gamepad); const char *path = SDL_GetGamepadPath(gamepad); - SDL_Log("Opened gamepad %s%s%s\n", name, path ? ", " : "", path ? path : ""); + SDL_GUID guid = SDL_GetGamepadGUIDForID(id); + char guid_string[33]; + SDL_GUIDToString(guid, guid_string, sizeof(guid_string)); + SDL_Log("Opened gamepad %s, guid %s%s%s\n", name, guid_string, path ? ", " : "", path ? path : ""); firmware_version = SDL_GetGamepadFirmwareVersion(gamepad); if (firmware_version) { @@ -1074,6 +1077,7 @@ static void HandleGamepadAdded(SDL_JoystickID id, bool verbose) } HandleGamepadRemapped(id); + SetController(id); } static void HandleGamepadRemoved(SDL_JoystickID id) diff --git a/libs/SDL3/test/testfilesystem.c b/libs/SDL3/test/testfilesystem.c index 3a068a0f..76087998 100644 --- a/libs/SDL3/test/testfilesystem.c +++ b/libs/SDL3/test/testfilesystem.c @@ -62,6 +62,7 @@ int main(int argc, char *argv[]) { SDLTest_CommonState *state; char *pref_path; + char *curdir; const char *base_path; /* Initialize test framework */ @@ -106,6 +107,15 @@ int main(int argc, char *argv[]) } SDL_free(pref_path); + curdir = SDL_GetCurrentDirectory(); + if (!curdir) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't find current directory: %s\n", + SDL_GetError()); + } else { + SDL_Log("current directory: '%s'\n", curdir); + } + SDL_free(curdir); + if (base_path) { char **globlist; SDL_IOStream *stream; diff --git a/libs/SDL3/test/testgl.c b/libs/SDL3/test/testgl.c index cfa82d95..44e1ca42 100644 --- a/libs/SDL3/test/testgl.c +++ b/libs/SDL3/test/testgl.c @@ -33,7 +33,7 @@ static SDL_GLContext context; static GL_Context ctx; static bool suspend_when_occluded; -static int LoadContext(GL_Context *data) +static bool LoadContext(GL_Context *data) { #ifdef SDL_VIDEO_DRIVER_UIKIT #define __SDL_NOGETPROCADDR__ @@ -55,7 +55,7 @@ static int LoadContext(GL_Context *data) #include "../src/render/opengl/SDL_glfuncs.h" #undef SDL_PROC - return 0; + return true; } /* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */ @@ -284,7 +284,7 @@ int main(int argc, char *argv[]) } /* Important: call this *after* creating the context */ - if (LoadContext(&ctx) < 0) { + if (!LoadContext(&ctx)) { SDL_Log("Could not load GL functions\n"); quit(2); return 0; diff --git a/libs/SDL3/test/testgles2.c b/libs/SDL3/test/testgles2.c index 1903938f..9c7ecf6a 100644 --- a/libs/SDL3/test/testgles2.c +++ b/libs/SDL3/test/testgles2.c @@ -70,7 +70,7 @@ static bool suspend_when_occluded; static GLES2_Context ctx; static shader_data *datas; -static int LoadContext(GLES2_Context *data) +static bool LoadContext(GLES2_Context *data) { #ifdef SDL_VIDEO_DRIVER_UIKIT #define __SDL_NOGETPROCADDR__ @@ -92,7 +92,7 @@ static int LoadContext(GLES2_Context *data) #include "../src/render/opengles2/SDL_gles2funcs.h" #undef SDL_PROC - return 0; + return true; } /* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */ @@ -764,7 +764,7 @@ int main(int argc, char *argv[]) } /* Important: call this *after* creating the context */ - if (LoadContext(&ctx) < 0) { + if (!LoadContext(&ctx)) { SDL_Log("Could not load GLES2 functions\n"); quit(2); return 0; diff --git a/libs/SDL3/test/testgles2_sdf.c b/libs/SDL3/test/testgles2_sdf.c index 09e2b6c4..d406d041 100644 --- a/libs/SDL3/test/testgles2_sdf.c +++ b/libs/SDL3/test/testgles2_sdf.c @@ -61,7 +61,7 @@ static SDL_GLContext *context = NULL; static int depth = 16; static GLES2_Context ctx; -static int LoadContext(GLES2_Context *data) +static bool LoadContext(GLES2_Context *data) { #ifdef SDL_VIDEO_DRIVER_UIKIT #define __SDL_NOGETPROCADDR__ @@ -83,7 +83,7 @@ static int LoadContext(GLES2_Context *data) #include "../src/render/opengles2/SDL_gles2funcs.h" #undef SDL_PROC - return 0; + return true; } /* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */ @@ -523,7 +523,7 @@ int main(int argc, char *argv[]) } /* Important: call this *after* creating the context */ - if (LoadContext(&ctx) < 0) { + if (!LoadContext(&ctx)) { SDL_Log("Could not load GLES2 functions\n"); quit(2); return 0; diff --git a/libs/SDL3/test/testgpu/build-shaders.sh b/libs/SDL3/test/testgpu/build-shaders.sh index ac62d272..d76f9d64 100644 --- a/libs/SDL3/test/testgpu/build-shaders.sh +++ b/libs/SDL3/test/testgpu/build-shaders.sh @@ -82,18 +82,9 @@ if [[ "$OSTYPE" == "darwin"* ]]; then elif [[ "$OSTYPE" == "cygwin"* ]] || [[ "$OSTYPE" == "msys"* ]]; then - # FXC - fxc cube.hlsl /E VSMain /T vs_5_0 /Fh cube.vert.h - fxc cube.hlsl /E PSMain /T ps_5_0 /Fh cube.frag.h - - cat cube.vert.h | perl -w -p -e 's/BYTE/unsigned char/;s/g_VSMain/D3D11_CubeVert/;' > cube.vert.temp.h - cat cube.frag.h | perl -w -p -e 's/BYTE/unsigned char/;s/g_PSMain/D3D11_CubeFrag/;' > cube.frag.temp.h - cat cube.vert.temp.h cube.frag.temp.h > testgpu_dxbc.h - rm -f cube.vert.h cube.frag.h cube.vert.temp.h cube.frag.temp.h - # DXC - dxc cube.hlsl /E VSMain /T vs_6_0 /Fh cube.vert.h /D D3D12=1 - dxc cube.hlsl /E PSMain /T ps_6_0 /Fh cube.frag.h /D D3D12=1 + dxc cube.hlsl /E VSMain /T vs_6_0 /Fh cube.vert.h + dxc cube.hlsl /E PSMain /T ps_6_0 /Fh cube.frag.h cat cube.vert.h | perl -w -p -e 's/BYTE/unsigned char/;s/g_VSMain/D3D12_CubeVert/;' > cube.vert.temp.h cat cube.frag.h | perl -w -p -e 's/BYTE/unsigned char/;s/g_PSMain/D3D12_CubeFrag/;' > cube.frag.temp.h diff --git a/libs/SDL3/test/testgpu/cube.hlsl b/libs/SDL3/test/testgpu/cube.hlsl index 219065b1..6d214dcb 100644 --- a/libs/SDL3/test/testgpu/cube.hlsl +++ b/libs/SDL3/test/testgpu/cube.hlsl @@ -1,8 +1,4 @@ -#if D3D12 #define REG(reg, space) register(reg, space) -#else -#define REG(reg, space) register(reg) -#endif cbuffer UBO : REG(b0, space1) { @@ -32,4 +28,4 @@ VSOutput VSMain(VSInput input) float4 PSMain(VSOutput input) : SV_Target0 { return input.Color; -} \ No newline at end of file +} diff --git a/libs/SDL3/test/testgpu/testgpu_dxbc.h b/libs/SDL3/test/testgpu/testgpu_dxbc.h deleted file mode 100644 index ab965787..00000000 --- a/libs/SDL3/test/testgpu/testgpu_dxbc.h +++ /dev/null @@ -1,333 +0,0 @@ -#if 0 -// -// Generated by Microsoft (R) HLSL Shader Compiler 10.1 -// -// -// Buffer Definitions: -// -// cbuffer UBO -// { -// -// float4x4 ModelViewProj; // Offset: 0 Size: 64 -// -// } -// -// -// Resource Bindings: -// -// Name Type Format Dim HLSL Bind Count -// ------------------------------ ---------- ------- ----------- -------------- ------ -// UBO cbuffer NA NA cb0 1 -// -// -// -// Input signature: -// -// Name Index Mask Register SysValue Format Used -// -------------------- ----- ------ -------- -------- ------- ------ -// TEXCOORD 0 xyz 0 NONE float xyz -// TEXCOORD 1 xyz 1 NONE float xyz -// -// -// Output signature: -// -// Name Index Mask Register SysValue Format Used -// -------------------- ----- ------ -------- -------- ------- ------ -// TEXCOORD 0 xyzw 0 NONE float xyzw -// SV_Position 0 xyzw 1 POS float xyzw -// -vs_5_0 -dcl_globalFlags refactoringAllowed -dcl_constantbuffer CB0[4], immediateIndexed -dcl_input v0.xyz -dcl_input v1.xyz -dcl_output o0.xyzw -dcl_output_siv o1.xyzw, position -dcl_temps 1 -mov o0.xyz, v1.xyzx -mov o0.w, l(1.000000) -mul r0.xyzw, v0.yyyy, cb0[1].xyzw -mad r0.xyzw, cb0[0].xyzw, v0.xxxx, r0.xyzw -mad r0.xyzw, cb0[2].xyzw, v0.zzzz, r0.xyzw -add o1.xyzw, r0.xyzw, cb0[3].xyzw -ret -// Approximately 7 instruction slots used -#endif - -const unsigned char D3D11_CubeVert[] = -{ - 68, 88, 66, 67, 114, 13, - 37, 16, 19, 3, 132, 73, - 243, 18, 23, 177, 140, 169, - 19, 240, 1, 0, 0, 0, - 156, 3, 0, 0, 5, 0, - 0, 0, 52, 0, 0, 0, - 64, 1, 0, 0, 140, 1, - 0, 0, 228, 1, 0, 0, - 0, 3, 0, 0, 82, 68, - 69, 70, 4, 1, 0, 0, - 1, 0, 0, 0, 96, 0, - 0, 0, 1, 0, 0, 0, - 60, 0, 0, 0, 0, 5, - 254, 255, 0, 1, 0, 0, - 220, 0, 0, 0, 82, 68, - 49, 49, 60, 0, 0, 0, - 24, 0, 0, 0, 32, 0, - 0, 0, 40, 0, 0, 0, - 36, 0, 0, 0, 12, 0, - 0, 0, 0, 0, 0, 0, - 92, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 1, 0, - 0, 0, 85, 66, 79, 0, - 92, 0, 0, 0, 1, 0, - 0, 0, 120, 0, 0, 0, - 64, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 160, 0, 0, 0, 0, 0, - 0, 0, 64, 0, 0, 0, - 2, 0, 0, 0, 184, 0, - 0, 0, 0, 0, 0, 0, - 255, 255, 255, 255, 0, 0, - 0, 0, 255, 255, 255, 255, - 0, 0, 0, 0, 77, 111, - 100, 101, 108, 86, 105, 101, - 119, 80, 114, 111, 106, 0, - 102, 108, 111, 97, 116, 52, - 120, 52, 0, 171, 3, 0, - 3, 0, 4, 0, 4, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 174, 0, 0, 0, 77, 105, - 99, 114, 111, 115, 111, 102, - 116, 32, 40, 82, 41, 32, - 72, 76, 83, 76, 32, 83, - 104, 97, 100, 101, 114, 32, - 67, 111, 109, 112, 105, 108, - 101, 114, 32, 49, 48, 46, - 49, 0, 73, 83, 71, 78, - 68, 0, 0, 0, 2, 0, - 0, 0, 8, 0, 0, 0, - 56, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 3, 0, 0, 0, 0, 0, - 0, 0, 7, 7, 0, 0, - 56, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, - 3, 0, 0, 0, 1, 0, - 0, 0, 7, 7, 0, 0, - 84, 69, 88, 67, 79, 79, - 82, 68, 0, 171, 171, 171, - 79, 83, 71, 78, 80, 0, - 0, 0, 2, 0, 0, 0, - 8, 0, 0, 0, 56, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 3, 0, - 0, 0, 0, 0, 0, 0, - 15, 0, 0, 0, 65, 0, - 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 3, 0, - 0, 0, 1, 0, 0, 0, - 15, 0, 0, 0, 84, 69, - 88, 67, 79, 79, 82, 68, - 0, 83, 86, 95, 80, 111, - 115, 105, 116, 105, 111, 110, - 0, 171, 171, 171, 83, 72, - 69, 88, 20, 1, 0, 0, - 80, 0, 1, 0, 69, 0, - 0, 0, 106, 8, 0, 1, - 89, 0, 0, 4, 70, 142, - 32, 0, 0, 0, 0, 0, - 4, 0, 0, 0, 95, 0, - 0, 3, 114, 16, 16, 0, - 0, 0, 0, 0, 95, 0, - 0, 3, 114, 16, 16, 0, - 1, 0, 0, 0, 101, 0, - 0, 3, 242, 32, 16, 0, - 0, 0, 0, 0, 103, 0, - 0, 4, 242, 32, 16, 0, - 1, 0, 0, 0, 1, 0, - 0, 0, 104, 0, 0, 2, - 1, 0, 0, 0, 54, 0, - 0, 5, 114, 32, 16, 0, - 0, 0, 0, 0, 70, 18, - 16, 0, 1, 0, 0, 0, - 54, 0, 0, 5, 130, 32, - 16, 0, 0, 0, 0, 0, - 1, 64, 0, 0, 0, 0, - 128, 63, 56, 0, 0, 8, - 242, 0, 16, 0, 0, 0, - 0, 0, 86, 21, 16, 0, - 0, 0, 0, 0, 70, 142, - 32, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 50, 0, - 0, 10, 242, 0, 16, 0, - 0, 0, 0, 0, 70, 142, - 32, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 6, 16, - 16, 0, 0, 0, 0, 0, - 70, 14, 16, 0, 0, 0, - 0, 0, 50, 0, 0, 10, - 242, 0, 16, 0, 0, 0, - 0, 0, 70, 142, 32, 0, - 0, 0, 0, 0, 2, 0, - 0, 0, 166, 26, 16, 0, - 0, 0, 0, 0, 70, 14, - 16, 0, 0, 0, 0, 0, - 0, 0, 0, 8, 242, 32, - 16, 0, 1, 0, 0, 0, - 70, 14, 16, 0, 0, 0, - 0, 0, 70, 142, 32, 0, - 0, 0, 0, 0, 3, 0, - 0, 0, 62, 0, 0, 1, - 83, 84, 65, 84, 148, 0, - 0, 0, 7, 0, 0, 0, - 1, 0, 0, 0, 0, 0, - 0, 0, 4, 0, 0, 0, - 4, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 2, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0 -}; -#if 0 -// -// Generated by Microsoft (R) HLSL Shader Compiler 10.1 -// -// -// -// Input signature: -// -// Name Index Mask Register SysValue Format Used -// -------------------- ----- ------ -------- -------- ------- ------ -// TEXCOORD 0 xyzw 0 NONE float xyzw -// SV_Position 0 xyzw 1 POS float -// -// -// Output signature: -// -// Name Index Mask Register SysValue Format Used -// -------------------- ----- ------ -------- -------- ------- ------ -// SV_Target 0 xyzw 0 TARGET float xyzw -// -ps_5_0 -dcl_globalFlags refactoringAllowed -dcl_input_ps linear v0.xyzw -dcl_output o0.xyzw -mov o0.xyzw, v0.xyzw -ret -// Approximately 2 instruction slots used -#endif - -const unsigned char D3D11_CubeFrag[] = -{ - 68, 88, 66, 67, 100, 36, - 97, 53, 102, 218, 120, 105, - 57, 43, 222, 229, 58, 45, - 195, 237, 1, 0, 0, 0, - 12, 2, 0, 0, 5, 0, - 0, 0, 52, 0, 0, 0, - 160, 0, 0, 0, 248, 0, - 0, 0, 44, 1, 0, 0, - 112, 1, 0, 0, 82, 68, - 69, 70, 100, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 60, 0, 0, 0, 0, 5, - 255, 255, 0, 1, 0, 0, - 60, 0, 0, 0, 82, 68, - 49, 49, 60, 0, 0, 0, - 24, 0, 0, 0, 32, 0, - 0, 0, 40, 0, 0, 0, - 36, 0, 0, 0, 12, 0, - 0, 0, 0, 0, 0, 0, - 77, 105, 99, 114, 111, 115, - 111, 102, 116, 32, 40, 82, - 41, 32, 72, 76, 83, 76, - 32, 83, 104, 97, 100, 101, - 114, 32, 67, 111, 109, 112, - 105, 108, 101, 114, 32, 49, - 48, 46, 49, 0, 73, 83, - 71, 78, 80, 0, 0, 0, - 2, 0, 0, 0, 8, 0, - 0, 0, 56, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 3, 0, 0, 0, - 0, 0, 0, 0, 15, 15, - 0, 0, 65, 0, 0, 0, - 0, 0, 0, 0, 1, 0, - 0, 0, 3, 0, 0, 0, - 1, 0, 0, 0, 15, 0, - 0, 0, 84, 69, 88, 67, - 79, 79, 82, 68, 0, 83, - 86, 95, 80, 111, 115, 105, - 116, 105, 111, 110, 0, 171, - 171, 171, 79, 83, 71, 78, - 44, 0, 0, 0, 1, 0, - 0, 0, 8, 0, 0, 0, - 32, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 3, 0, 0, 0, 0, 0, - 0, 0, 15, 0, 0, 0, - 83, 86, 95, 84, 97, 114, - 103, 101, 116, 0, 171, 171, - 83, 72, 69, 88, 60, 0, - 0, 0, 80, 0, 0, 0, - 15, 0, 0, 0, 106, 8, - 0, 1, 98, 16, 0, 3, - 242, 16, 16, 0, 0, 0, - 0, 0, 101, 0, 0, 3, - 242, 32, 16, 0, 0, 0, - 0, 0, 54, 0, 0, 5, - 242, 32, 16, 0, 0, 0, - 0, 0, 70, 30, 16, 0, - 0, 0, 0, 0, 62, 0, - 0, 1, 83, 84, 65, 84, - 148, 0, 0, 0, 2, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 2, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0 -}; diff --git a/libs/SDL3/test/testgpu_simple_clear.c b/libs/SDL3/test/testgpu_simple_clear.c index 940dd37d..b2afba88 100644 --- a/libs/SDL3/test/testgpu_simple_clear.c +++ b/libs/SDL3/test/testgpu_simple_clear.c @@ -82,28 +82,32 @@ SDL_AppResult SDL_AppIterate(void *appstate) } SDL_GPUTexture *swapchainTexture; - if (!SDL_AcquireGPUSwapchainTexture(cmdbuf, state->windows[0], &swapchainTexture, NULL, NULL)) { + if (!SDL_WaitAndAcquireGPUSwapchainTexture(cmdbuf, state->windows[0], &swapchainTexture, NULL, NULL)) { SDL_Log("SDL_AcquireGPUSwapchainTexture failed: %s", SDL_GetError()); return SDL_APP_FAILURE; } - if (swapchainTexture != NULL) { + + if (swapchainTexture != NULL) { const double currentTime = (double)SDL_GetPerformanceCounter() / SDL_GetPerformanceFrequency(); SDL_GPURenderPass *renderPass; - SDL_GPUColorTargetInfo color_target_info; + SDL_GPUColorTargetInfo color_target_info; SDL_zero(color_target_info); - color_target_info.texture = swapchainTexture; - color_target_info.clear_color.r = (float)(0.5 + 0.5 * SDL_sin(currentTime)); - color_target_info.clear_color.g = (float)(0.5 + 0.5 * SDL_sin(currentTime + SDL_PI_D * 2 / 3)); - color_target_info.clear_color.b = (float)(0.5 + 0.5 * SDL_sin(currentTime + SDL_PI_D * 4 / 3)); - color_target_info.clear_color.a = 1.0f; - color_target_info.load_op = SDL_GPU_LOADOP_CLEAR; - color_target_info.store_op = SDL_GPU_STOREOP_STORE; + color_target_info.texture = swapchainTexture; + color_target_info.clear_color.r = (float)(0.5 + 0.5 * SDL_sin(currentTime)); + color_target_info.clear_color.g = (float)(0.5 + 0.5 * SDL_sin(currentTime + SDL_PI_D * 2 / 3)); + color_target_info.clear_color.b = (float)(0.5 + 0.5 * SDL_sin(currentTime + SDL_PI_D * 4 / 3)); + color_target_info.clear_color.a = 1.0f; + color_target_info.load_op = SDL_GPU_LOADOP_CLEAR; + color_target_info.store_op = SDL_GPU_STOREOP_STORE; - renderPass = SDL_BeginGPURenderPass(cmdbuf, &color_target_info, 1, NULL); - SDL_EndGPURenderPass(renderPass); - } + renderPass = SDL_BeginGPURenderPass(cmdbuf, &color_target_info, 1, NULL); + SDL_EndGPURenderPass(renderPass); - SDL_SubmitGPUCommandBuffer(cmdbuf); + SDL_SubmitGPUCommandBuffer(cmdbuf); + } else { + /* Swapchain is unavailable, cancel work */ + SDL_CancelGPUCommandBuffer(cmdbuf); + } frames++; diff --git a/libs/SDL3/test/testgpu_spinning_cube.c b/libs/SDL3/test/testgpu_spinning_cube.c index 9fe01a1a..97a229b8 100644 --- a/libs/SDL3/test/testgpu_spinning_cube.c +++ b/libs/SDL3/test/testgpu_spinning_cube.c @@ -22,7 +22,6 @@ /* Regenerate the shaders with testgpu/build-shaders.sh */ #include "testgpu/testgpu_spirv.h" -#include "testgpu/testgpu_dxbc.h" #include "testgpu/testgpu_dxil.h" #include "testgpu/testgpu_metallib.h" @@ -342,20 +341,20 @@ Render(SDL_Window *window, const int windownum) SDL_Log("Failed to acquire command buffer :%s", SDL_GetError()); quit(2); } - if (!SDL_AcquireGPUSwapchainTexture(cmd, state->windows[windownum], &swapchainTexture, &drawablew, &drawableh)) { + if (!SDL_WaitAndAcquireGPUSwapchainTexture(cmd, state->windows[windownum], &swapchainTexture, &drawablew, &drawableh)) { SDL_Log("Failed to acquire swapchain texture: %s", SDL_GetError()); quit(2); } if (swapchainTexture == NULL) { - /* No swapchain was acquired, probably too many frames in flight */ - SDL_SubmitGPUCommandBuffer(cmd); + /* Swapchain is unavailable, cancel work */ + SDL_CancelGPUCommandBuffer(cmd); return; } /* * Do some rotation with Euler angles. It is not a fixed axis as - * quaterions would be, but the effect is cool. + * quaternions would be, but the effect is cool. */ rotate_matrix((float)winstate->angle_x, 1.0f, 0.0f, 0.0f, matrix_modelview); rotate_matrix((float)winstate->angle_y, 0.0f, 1.0f, 0.0f, matrix_rotate); @@ -471,12 +470,7 @@ load_shader(bool is_vertex) createinfo.props = 0; SDL_GPUShaderFormat format = SDL_GetGPUShaderFormats(gpu_device); - if (format & SDL_GPU_SHADERFORMAT_DXBC) { - createinfo.format = SDL_GPU_SHADERFORMAT_DXBC; - createinfo.code = is_vertex ? D3D11_CubeVert : D3D11_CubeFrag; - createinfo.code_size = is_vertex ? SDL_arraysize(D3D11_CubeVert) : SDL_arraysize(D3D11_CubeFrag); - createinfo.entrypoint = is_vertex ? "VSMain" : "PSMain"; - } else if (format & SDL_GPU_SHADERFORMAT_DXIL) { + if (format & SDL_GPU_SHADERFORMAT_DXIL) { createinfo.format = SDL_GPU_SHADERFORMAT_DXIL; createinfo.code = is_vertex ? D3D12_CubeVert : D3D12_CubeFrag; createinfo.code_size = is_vertex ? SDL_arraysize(D3D12_CubeVert) : SDL_arraysize(D3D12_CubeFrag); diff --git a/libs/SDL3/test/testrelative.c b/libs/SDL3/test/testrelative.c index a0e4b7b8..545303dc 100644 --- a/libs/SDL3/test/testrelative.c +++ b/libs/SDL3/test/testrelative.c @@ -32,7 +32,12 @@ static void DrawRects(SDL_Renderer *renderer) SDL_RenderFillRect(renderer, &rect); SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); - SDLTest_DrawString(renderer, 0.f, 0.f, "Relative Mode: Enabled"); + + if (SDL_GetWindowRelativeMouseMode(SDL_GetRenderWindow(renderer))) { + SDLTest_DrawString(renderer, 0.f, 0.f, "Relative Mode: Enabled"); + } else { + SDLTest_DrawString(renderer, 0.f, 0.f, "Relative Mode: Disabled"); + } } static void CenterMouse(void) diff --git a/libs/SDL3/test/testtray.c b/libs/SDL3/test/testtray.c new file mode 100644 index 00000000..df3a5488 --- /dev/null +++ b/libs/SDL3/test/testtray.c @@ -0,0 +1,599 @@ +#include +#include +#include + +static void SDLCALL tray_quit(void *ptr, SDL_TrayEntry *entry) +{ + SDL_Event e; + e.type = SDL_EVENT_QUIT; + SDL_PushEvent(&e); +} + +static void SDLCALL apply_icon(void *ptr, const char * const *filelist, int filter) +{ + if (!*filelist) { + return; + } + + SDL_Surface *icon = SDL_LoadBMP(*filelist); + + if (!icon) { + SDL_Log("Couldn't load icon '%s': %s", *filelist, SDL_GetError()); + return; + } + + SDL_Tray *tray = (SDL_Tray *) ptr; + SDL_SetTrayIcon(tray, icon); + + SDL_DestroySurface(icon); +} + +static void SDLCALL change_icon(void *ptr, SDL_TrayEntry *entry) +{ + SDL_DialogFileFilter filters[] = { + { "BMP image files", "bmp" }, + { "All files", "*" }, + }; + + SDL_ShowOpenFileDialog(apply_icon, ptr, NULL, filters, 2, NULL, 0); +} + +static void SDLCALL print_entry(void *ptr, SDL_TrayEntry *entry) +{ + SDL_Log("Clicked on button '%s'\n", SDL_GetTrayEntryLabel(entry)); +} + +static void SDLCALL set_entry_enabled(void *ptr, SDL_TrayEntry *entry) +{ + SDL_TrayEntry *target = (SDL_TrayEntry *) ptr; + SDL_SetTrayEntryEnabled(target, true); +} + +static void SDLCALL set_entry_disabled(void *ptr, SDL_TrayEntry *entry) +{ + SDL_TrayEntry *target = (SDL_TrayEntry *) ptr; + SDL_SetTrayEntryEnabled(target, false); +} + +static void SDLCALL set_entry_checked(void *ptr, SDL_TrayEntry *entry) +{ + SDL_TrayEntry *target = (SDL_TrayEntry *) ptr; + SDL_SetTrayEntryChecked(target, true); +} + +static void SDLCALL set_entry_unchecked(void *ptr, SDL_TrayEntry *entry) +{ + SDL_TrayEntry *target = (SDL_TrayEntry *) ptr; + SDL_SetTrayEntryChecked(target, false); +} + +static void SDLCALL remove_entry(void *ptr, SDL_TrayEntry *entry) +{ + SDL_TrayEntry *target = (SDL_TrayEntry *) ptr; + SDL_RemoveTrayEntry(target); + + SDL_TrayMenu *ctrl_submenu = SDL_GetTrayEntryParent(entry); + SDL_TrayEntry *ctrl_entry = SDL_GetTrayMenuParentEntry(ctrl_submenu); + + if (!ctrl_entry) { + SDL_Log("Attempt to remove a menu that isn't a submenu. This shouldn't happen.\n"); + return; + } + + SDL_RemoveTrayEntry(ctrl_entry); +} + +static void SDLCALL append_button_to(void *ptr, SDL_TrayEntry *entry) +{ + SDL_TrayMenu *menu = (SDL_TrayMenu *) ptr; + SDL_TrayMenu *submenu; + SDL_TrayEntry *new_ctrl; + SDL_TrayEntry *new_ctrl_remove; + SDL_TrayEntry *new_ctrl_enabled; + SDL_TrayEntry *new_ctrl_disabled; + SDL_TrayEntry *new_example; + + new_ctrl = SDL_InsertTrayEntryAt(SDL_GetTrayEntryParent(entry), -1, "New button", SDL_TRAYENTRY_SUBMENU); + + if (!new_ctrl) { + SDL_Log("Couldn't insert entry in control tray: %s\n", SDL_GetError()); + return; + } + + /* ---------- */ + + submenu = SDL_CreateTraySubmenu(new_ctrl); + + if (!new_ctrl) { + SDL_Log("Couldn't create control tray entry submenu: %s\n", SDL_GetError()); + SDL_RemoveTrayEntry(new_ctrl); + return; + } + + /* ---------- */ + + new_example = SDL_InsertTrayEntryAt(menu, -1, "New button", SDL_TRAYENTRY_BUTTON); + + if (new_example == NULL) { + SDL_Log("Couldn't insert entry in example tray: %s\n", SDL_GetError()); + SDL_RemoveTrayEntry(new_ctrl); + return; + } + + SDL_SetTrayEntryCallback(new_example, print_entry, NULL); + + /* ---------- */ + + new_ctrl_remove = SDL_InsertTrayEntryAt(submenu, -1, "Remove", SDL_TRAYENTRY_BUTTON); + + if (new_ctrl_remove == NULL) { + SDL_Log("Couldn't insert new_ctrl_remove: %s\n", SDL_GetError()); + SDL_RemoveTrayEntry(new_ctrl); + SDL_RemoveTrayEntry(new_example); + return; + } + + SDL_SetTrayEntryCallback(new_ctrl_remove, remove_entry, new_example); + + /* ---------- */ + + new_ctrl_enabled = SDL_InsertTrayEntryAt(submenu, -1, "Enable", SDL_TRAYENTRY_BUTTON); + + if (new_ctrl_enabled == NULL) { + SDL_Log("Couldn't insert new_ctrl_enabled: %s\n", SDL_GetError()); + SDL_RemoveTrayEntry(new_ctrl); + SDL_RemoveTrayEntry(new_example); + return; + } + + SDL_SetTrayEntryCallback(new_ctrl_enabled, set_entry_enabled, new_example); + + /* ---------- */ + + new_ctrl_disabled = SDL_InsertTrayEntryAt(submenu, -1, "Disable", SDL_TRAYENTRY_BUTTON); + + if (new_ctrl_disabled == NULL) { + SDL_Log("Couldn't insert new_ctrl_disabled: %s\n", SDL_GetError()); + SDL_RemoveTrayEntry(new_ctrl); + SDL_RemoveTrayEntry(new_example); + return; + } + + SDL_SetTrayEntryCallback(new_ctrl_disabled, set_entry_disabled, new_example); +} + +static void SDLCALL append_checkbox_to(void *ptr, SDL_TrayEntry *entry) +{ + SDL_TrayMenu *menu = (SDL_TrayMenu *) ptr; + SDL_TrayMenu *submenu; + SDL_TrayEntry *new_ctrl; + SDL_TrayEntry *new_ctrl_remove; + SDL_TrayEntry *new_ctrl_enabled; + SDL_TrayEntry *new_ctrl_disabled; + SDL_TrayEntry *new_ctrl_checked; + SDL_TrayEntry *new_ctrl_unchecked; + SDL_TrayEntry *new_example; + + new_ctrl = SDL_InsertTrayEntryAt(SDL_GetTrayEntryParent(entry), -1, "New checkbox", SDL_TRAYENTRY_SUBMENU); + + if (!new_ctrl) { + SDL_Log("Couldn't insert entry in control tray: %s\n", SDL_GetError()); + return; + } + + /* ---------- */ + + submenu = SDL_CreateTraySubmenu(new_ctrl); + + if (!new_ctrl) { + SDL_Log("Couldn't create control tray entry submenu: %s\n", SDL_GetError()); + SDL_RemoveTrayEntry(new_ctrl); + return; + } + + /* ---------- */ + + new_example = SDL_InsertTrayEntryAt(menu, -1, "New checkbox", SDL_TRAYENTRY_CHECKBOX); + + if (new_example == NULL) { + SDL_Log("Couldn't insert entry in example tray: %s\n", SDL_GetError()); + SDL_RemoveTrayEntry(new_ctrl); + return; + } + + SDL_SetTrayEntryCallback(new_example, print_entry, NULL); + + /* ---------- */ + + new_ctrl_remove = SDL_InsertTrayEntryAt(submenu, -1, "Remove", SDL_TRAYENTRY_BUTTON); + + if (new_ctrl_remove == NULL) { + SDL_Log("Couldn't insert new_ctrl_remove: %s\n", SDL_GetError()); + SDL_RemoveTrayEntry(new_ctrl); + SDL_RemoveTrayEntry(new_example); + return; + } + + SDL_SetTrayEntryCallback(new_ctrl_remove, remove_entry, new_example); + + /* ---------- */ + + new_ctrl_enabled = SDL_InsertTrayEntryAt(submenu, -1, "Enable", SDL_TRAYENTRY_BUTTON); + + if (new_ctrl_enabled == NULL) { + SDL_Log("Couldn't insert new_ctrl_enabled: %s\n", SDL_GetError()); + SDL_RemoveTrayEntry(new_ctrl); + SDL_RemoveTrayEntry(new_example); + return; + } + + SDL_SetTrayEntryCallback(new_ctrl_enabled, set_entry_enabled, new_example); + + /* ---------- */ + + new_ctrl_disabled = SDL_InsertTrayEntryAt(submenu, -1, "Disable", SDL_TRAYENTRY_BUTTON); + + if (new_ctrl_disabled == NULL) { + SDL_Log("Couldn't insert new_ctrl_disabled: %s\n", SDL_GetError()); + SDL_RemoveTrayEntry(new_ctrl); + SDL_RemoveTrayEntry(new_example); + return; + } + + SDL_SetTrayEntryCallback(new_ctrl_disabled, set_entry_disabled, new_example); + + /* ---------- */ + + new_ctrl_checked = SDL_InsertTrayEntryAt(submenu, -1, "Check", SDL_TRAYENTRY_BUTTON); + + if (new_ctrl_checked == NULL) { + SDL_Log("Couldn't insert new_ctrl_checked: %s\n", SDL_GetError()); + SDL_RemoveTrayEntry(new_ctrl); + SDL_RemoveTrayEntry(new_example); + return; + } + + SDL_SetTrayEntryCallback(new_ctrl_checked, set_entry_checked, new_example); + + /* ---------- */ + + new_ctrl_unchecked = SDL_InsertTrayEntryAt(submenu, -1, "Uncheck", SDL_TRAYENTRY_BUTTON); + + if (new_ctrl_unchecked == NULL) { + SDL_Log("Couldn't insert new_ctrl_unchecked: %s\n", SDL_GetError()); + SDL_RemoveTrayEntry(new_ctrl); + SDL_RemoveTrayEntry(new_example); + return; + } + + SDL_SetTrayEntryCallback(new_ctrl_unchecked, set_entry_unchecked, new_example); +} + +static void SDLCALL append_separator_to(void *ptr, SDL_TrayEntry *entry) +{ + SDL_TrayMenu *menu = (SDL_TrayMenu *) ptr; + SDL_TrayMenu *submenu; + SDL_TrayEntry *new_ctrl; + SDL_TrayEntry *new_ctrl_remove; + SDL_TrayEntry *new_example; + + new_ctrl = SDL_InsertTrayEntryAt(SDL_GetTrayEntryParent(entry), -1, "[Separator]", SDL_TRAYENTRY_SUBMENU); + + if (!new_ctrl) { + SDL_Log("Couldn't insert entry in control tray: %s\n", SDL_GetError()); + return; + } + + /* ---------- */ + + submenu = SDL_CreateTraySubmenu(new_ctrl); + + if (!new_ctrl) { + SDL_Log("Couldn't create control tray entry submenu: %s\n", SDL_GetError()); + SDL_RemoveTrayEntry(new_ctrl); + return; + } + + /* ---------- */ + + new_example = SDL_InsertTrayEntryAt(menu, -1, NULL, SDL_TRAYENTRY_BUTTON); + + if (new_example == NULL) { + SDL_Log("Couldn't insert separator in example tray: %s\n", SDL_GetError()); + SDL_RemoveTrayEntry(new_ctrl); + return; + } + + /* ---------- */ + + new_ctrl_remove = SDL_InsertTrayEntryAt(submenu, -1, "Remove", SDL_TRAYENTRY_BUTTON); + + if (new_ctrl_remove == NULL) { + SDL_Log("Couldn't insert new_ctrl_remove: %s\n", SDL_GetError()); + SDL_RemoveTrayEntry(new_ctrl); + SDL_RemoveTrayEntry(new_example); + return; + } + + SDL_SetTrayEntryCallback(new_ctrl_remove, remove_entry, new_example); +} + +static void SDLCALL append_submenu_to(void *ptr, SDL_TrayEntry *entry) +{ + SDL_TrayMenu *menu = (SDL_TrayMenu *) ptr; + SDL_TrayMenu *submenu; + SDL_TrayMenu *entry_submenu; + SDL_TrayEntry *new_ctrl; + SDL_TrayEntry *new_ctrl_remove; + SDL_TrayEntry *new_ctrl_enabled; + SDL_TrayEntry *new_ctrl_disabled; + SDL_TrayEntry *new_example; + + new_ctrl = SDL_InsertTrayEntryAt(SDL_GetTrayEntryParent(entry), -1, "New submenu", SDL_TRAYENTRY_SUBMENU); + + if (!new_ctrl) { + SDL_Log("Couldn't insert entry in control tray: %s\n", SDL_GetError()); + return; + } + + /* ---------- */ + + submenu = SDL_CreateTraySubmenu(new_ctrl); + + if (!new_ctrl) { + SDL_Log("Couldn't create control tray entry submenu: %s\n", SDL_GetError()); + SDL_RemoveTrayEntry(new_ctrl); + return; + } + + /* ---------- */ + + new_example = SDL_InsertTrayEntryAt(menu, -1, "New submenu", SDL_TRAYENTRY_SUBMENU); + + if (new_example == NULL) { + SDL_Log("Couldn't insert entry in example tray: %s\n", SDL_GetError()); + SDL_RemoveTrayEntry(new_ctrl); + return; + } + + SDL_SetTrayEntryCallback(new_example, print_entry, NULL); + + /* ---------- */ + + entry_submenu = SDL_CreateTraySubmenu(new_example); + + if (entry_submenu == NULL) { + SDL_Log("Couldn't create new entry submenu: %s\n", SDL_GetError()); + SDL_RemoveTrayEntry(new_ctrl); + SDL_RemoveTrayEntry(new_example); + return; + } + + /* ---------- */ + + new_ctrl_remove = SDL_InsertTrayEntryAt(submenu, -1, "Remove", SDL_TRAYENTRY_BUTTON); + + if (new_ctrl_remove == NULL) { + SDL_Log("Couldn't insert new_ctrl_remove: %s\n", SDL_GetError()); + SDL_RemoveTrayEntry(new_ctrl); + SDL_RemoveTrayEntry(new_example); + return; + } + + SDL_SetTrayEntryCallback(new_ctrl_remove, remove_entry, new_example); + + /* ---------- */ + + new_ctrl_enabled = SDL_InsertTrayEntryAt(submenu, -1, "Enable", SDL_TRAYENTRY_BUTTON); + + if (new_ctrl_enabled == NULL) { + SDL_Log("Couldn't insert new_ctrl_enabled: %s\n", SDL_GetError()); + SDL_RemoveTrayEntry(new_ctrl); + SDL_RemoveTrayEntry(new_example); + return; + } + + SDL_SetTrayEntryCallback(new_ctrl_enabled, set_entry_enabled, new_example); + + /* ---------- */ + + new_ctrl_disabled = SDL_InsertTrayEntryAt(submenu, -1, "Disable", SDL_TRAYENTRY_BUTTON); + + if (new_ctrl_disabled == NULL) { + SDL_Log("Couldn't insert new_ctrl_disabled: %s\n", SDL_GetError()); + SDL_RemoveTrayEntry(new_ctrl); + SDL_RemoveTrayEntry(new_example); + return; + } + + SDL_SetTrayEntryCallback(new_ctrl_disabled, set_entry_disabled, new_example); + + /* ---------- */ + + SDL_InsertTrayEntryAt(submenu, -1, NULL, 0); + + /* ---------- */ + + SDL_TrayEntry *entry_newbtn = SDL_InsertTrayEntryAt(submenu, -1, "Create button", SDL_TRAYENTRY_BUTTON); + + if (entry_newbtn == NULL) { + SDL_Log("Couldn't insert entry_newbtn: %s\n", SDL_GetError()); + SDL_RemoveTrayEntry(new_ctrl); + SDL_RemoveTrayEntry(new_example); + return; + } + + SDL_SetTrayEntryCallback(entry_newbtn, append_button_to, entry_submenu); + + /* ---------- */ + + SDL_TrayEntry *entry_newchk = SDL_InsertTrayEntryAt(submenu, -1, "Create checkbox", SDL_TRAYENTRY_BUTTON); + + if (entry_newchk == NULL) { + SDL_Log("Couldn't insert entry_newchk: %s\n", SDL_GetError()); + SDL_RemoveTrayEntry(new_ctrl); + SDL_RemoveTrayEntry(new_example); + return; + } + + SDL_SetTrayEntryCallback(entry_newchk, append_checkbox_to, entry_submenu); + + /* ---------- */ + + SDL_TrayEntry *entry_newsub = SDL_InsertTrayEntryAt(submenu, -1, "Create submenu", SDL_TRAYENTRY_BUTTON); + + if (entry_newsub == NULL) { + SDL_Log("Couldn't insert entry_newsub: %s\n", SDL_GetError()); + SDL_RemoveTrayEntry(new_ctrl); + SDL_RemoveTrayEntry(new_example); + return; + } + + SDL_SetTrayEntryCallback(entry_newsub, append_submenu_to, entry_submenu); + + /* ---------- */ + + SDL_TrayEntry *entry_newsep = SDL_InsertTrayEntryAt(submenu, -1, "Create separator", SDL_TRAYENTRY_BUTTON); + + if (entry_newsep == NULL) { + SDL_Log("Couldn't insert entry_newsep: %s\n", SDL_GetError()); + SDL_RemoveTrayEntry(new_ctrl); + SDL_RemoveTrayEntry(new_example); + return; + } + + SDL_SetTrayEntryCallback(entry_newsep, append_separator_to, entry_submenu); + + /* ---------- */ + + SDL_InsertTrayEntryAt(submenu, -1, NULL, 0); +} + +int main(int argc, char **argv) +{ + SDLTest_CommonState *state; + int i; + + /* Initialize test framework */ + state = SDLTest_CommonCreateState(argv, 0); + if (state == NULL) { + return 1; + } + + /* Parse commandline */ + for (i = 1; i < argc;) { + int consumed; + + consumed = SDLTest_CommonArg(state, i); + + if (consumed <= 0) { + static const char *options[] = { NULL }; + SDLTest_CommonLogUsage(state, argv[0], options); + return 1; + } + + i += consumed; + } + + if (!SDL_Init(SDL_INIT_VIDEO)) { + SDL_Log("SDL_Init failed (%s)", SDL_GetError()); + return 1; + } + + /* TODO: Resource paths? */ + SDL_Surface *icon = SDL_LoadBMP("../test/sdl-test_round.bmp"); + + if (!icon) { + SDL_Log("Couldn't load icon 1, proceeding without: %s", SDL_GetError()); + } + + SDL_Surface *icon2 = SDL_LoadBMP("../test/speaker.bmp"); + + if (!icon2) { + SDL_Log("Couldn't load icon 2, proceeding without: %s", SDL_GetError()); + } + + SDL_Tray *tray = SDL_CreateTray(icon, "SDL Tray control menu"); + + if (!tray) { + SDL_Log("Couldn't create control tray: %s", SDL_GetError()); + goto quit; + } + + SDL_Tray *tray2 = SDL_CreateTray(icon2, "SDL Tray example"); + + if (!tray2) { + SDL_Log("Couldn't create example tray: %s", SDL_GetError()); + goto clean_tray1; + } + + SDL_DestroySurface(icon); + SDL_DestroySurface(icon2); + +#define CHECK(name) \ + if (!name) { \ + SDL_Log("Couldn't create " #name ": %s", SDL_GetError()); \ + goto clean_all; \ + } + + SDL_TrayMenu *menu = SDL_CreateTrayMenu(tray); + CHECK(menu); + + SDL_TrayMenu *menu2 = SDL_CreateTrayMenu(tray2); + CHECK(menu2); + + SDL_TrayEntry *entry_quit = SDL_InsertTrayEntryAt(menu, -1, "Quit", SDL_TRAYENTRY_BUTTON); + CHECK(entry_quit); + + SDL_SetTrayEntryCallback(entry_quit, tray_quit, NULL); + + SDL_InsertTrayEntryAt(menu, -1, NULL, 0); + + SDL_TrayEntry *entry_icon = SDL_InsertTrayEntryAt(menu, -1, "Change icon", SDL_TRAYENTRY_BUTTON); + CHECK(entry_icon); + + SDL_SetTrayEntryCallback(entry_icon, change_icon, tray2); + + SDL_InsertTrayEntryAt(menu, -1, NULL, 0); + + SDL_TrayEntry *entry_newbtn = SDL_InsertTrayEntryAt(menu, -1, "Create button", SDL_TRAYENTRY_BUTTON); + CHECK(entry_newbtn); + + SDL_SetTrayEntryCallback(entry_newbtn, append_button_to, menu2); + + SDL_TrayEntry *entry_newchk = SDL_InsertTrayEntryAt(menu, -1, "Create checkbox", SDL_TRAYENTRY_BUTTON); + CHECK(entry_newchk); + + SDL_SetTrayEntryCallback(entry_newchk, append_checkbox_to, menu2); + + SDL_TrayEntry *entry_newsub = SDL_InsertTrayEntryAt(menu, -1, "Create submenu", SDL_TRAYENTRY_BUTTON); + CHECK(entry_newsub); + + SDL_SetTrayEntryCallback(entry_newsub, append_submenu_to, menu2); + + SDL_TrayEntry *entry_newsep = SDL_InsertTrayEntryAt(menu, -1, "Create separator", SDL_TRAYENTRY_BUTTON); + CHECK(entry_newsep); + + SDL_SetTrayEntryCallback(entry_newsep, append_separator_to, menu2); + + SDL_InsertTrayEntryAt(menu, -1, NULL, 0); + + SDL_Event e; + while (SDL_WaitEvent(&e)) { + if (e.type == SDL_EVENT_QUIT) { + break; + } + } + +clean_all: + SDL_DestroyTray(tray2); + +clean_tray1: + SDL_DestroyTray(tray); + +quit: + SDL_Quit(); + SDLTest_CommonDestroyState(state); + + return 0; +} diff --git a/libs/SDL3/test/testwm.c b/libs/SDL3/test/testwm.c index c91ec591..f6a1f029 100644 --- a/libs/SDL3/test/testwm.c +++ b/libs/SDL3/test/testwm.c @@ -277,6 +277,8 @@ int main(int argc, char *argv[]) SDL_RenderClear(renderer); } +SDL_StopTextInput(state->windows[0]); +SDL_StopTextInput(state->windows[0]); /* Main render loop */ done = 0; #ifdef SDL_PLATFORM_EMSCRIPTEN