diff --git a/third-party/SDL/.github/PULL_REQUEST_TEMPLATE.md b/third-party/SDL/.github/PULL_REQUEST_TEMPLATE.md index 533be8573d..5fd28bcd23 100644 --- a/third-party/SDL/.github/PULL_REQUEST_TEMPLATE.md +++ b/third-party/SDL/.github/PULL_REQUEST_TEMPLATE.md @@ -1,3 +1,5 @@ +- [ ] I confirm that I am the author of this code and release it to the SDL project under the Zlib license. This contribution does not contain code from other sources, including code generated by a Large Language Model ("AI"). + ## Description diff --git a/third-party/SDL/.github/actions/setup-gdk-desktop/action.yml b/third-party/SDL/.github/actions/setup-gdk-desktop/action.yml index 10427ace3c..2834cdc424 100644 --- a/third-party/SDL/.github/actions/setup-gdk-desktop/action.yml +++ b/third-party/SDL/.github/actions/setup-gdk-desktop/action.yml @@ -30,7 +30,7 @@ runs: echo "cache-key=gdk-${{ inputs.ref }}-${{ inputs.edition }}" >> $Env:GITHUB_OUTPUT - name: 'Restore cached GDK' id: cache-restore - uses: actions/cache/restore@v4 + uses: actions/cache/restore@v5 with: path: '${{ steps.calc.outputs.gdk-path }}' key: ${{ steps.calc.outputs.cache-key }} @@ -58,7 +58,7 @@ runs: --no-user-props - name: 'Cache GDK' if: ${{ !steps.cache-restore.outputs.cache-hit }} - uses: actions/cache/save@v4 + uses: actions/cache/save@v5 with: path: '${{ steps.calc.outputs.gdk-path }}' key: ${{ steps.calc.outputs.cache-key }} diff --git a/third-party/SDL/.github/actions/setup-loongarch64-toolchain/action.yml b/third-party/SDL/.github/actions/setup-loongarch64-toolchain/action.yml index e7f9ddc724..61c1e53ff3 100644 --- a/third-party/SDL/.github/actions/setup-loongarch64-toolchain/action.yml +++ b/third-party/SDL/.github/actions/setup-loongarch64-toolchain/action.yml @@ -17,7 +17,7 @@ outputs: runs: using: 'composite' steps: - - uses: actions/cache/restore@v4 + - uses: actions/cache/restore@v5 id: restore-cache with: path: /opt/cross-tools @@ -34,7 +34,7 @@ runs: mkdir -p /opt tar -C /opt -x -f /tmp/toolchain.tar.xz - - uses: actions/cache/save@v4 + - uses: actions/cache/save@v5 if: ${{ !steps.restore-cache.outputs.cache-hit }} with: path: /opt/cross-tools diff --git a/third-party/SDL/.github/actions/setup-msvc-libusb/action.yml b/third-party/SDL/.github/actions/setup-msvc-libusb/action.yml index b8732f9c41..628b608d4c 100644 --- a/third-party/SDL/.github/actions/setup-msvc-libusb/action.yml +++ b/third-party/SDL/.github/actions/setup-msvc-libusb/action.yml @@ -17,7 +17,7 @@ runs: steps: - name: 'Restore cached libusb-${{ inputs.version }}.7z' id: cache-restore - uses: actions/cache/restore@v4 + uses: actions/cache/restore@v5 with: path: 'C:\temp\libusb-${{ inputs.version }}.7z' key: libusb-msvc-${{ inputs.version }} @@ -28,7 +28,7 @@ runs: Invoke-WebRequest "https://github.com/libusb/libusb/releases/download/v${{ inputs.version }}/libusb-${{ inputs.version }}.7z" -OutFile "C:\temp\libusb-${{ inputs.version }}.7z" - name: 'Cache libusb-${{ inputs.version }}.7z' if: ${{ !steps.cache-restore.outputs.cache-hit }} - uses: actions/cache/save@v4 + uses: actions/cache/save@v5 with: path: 'C:\temp\libusb-${{ inputs.version }}.7z' key: libusb-msvc-${{ inputs.version }} diff --git a/third-party/SDL/.github/actions/setup-ngage-sdk/action.yml b/third-party/SDL/.github/actions/setup-ngage-sdk/action.yml index fa83418ba2..e270002044 100644 --- a/third-party/SDL/.github/actions/setup-ngage-sdk/action.yml +++ b/third-party/SDL/.github/actions/setup-ngage-sdk/action.yml @@ -7,7 +7,7 @@ inputs: runs: using: 'composite' steps: - - uses: actions/setup-python@v5 + - uses: actions/setup-python@v6 with: python-version: '3.x' - name: 'Verify platform' @@ -52,7 +52,7 @@ runs: echo "extras-branch=${extras_branch}" >> ${GITHUB_OUTPUT} # - name: 'Restore cached ${{ steps.calc.outputs.archive }}' # id: cache-restore -# uses: actions/cache/restore@v4 +# uses: actions/cache/restore@v5 # with: # path: '${{ runner.temp }}' # key: ${{ steps.calc.outputs.cache-key }} @@ -68,7 +68,7 @@ runs: # - name: 'Cache ${{ steps.calc.outputs.archive }}' # if: ${{ !steps.cache-restore.outputs.cache-hit || steps.cache-restore.outputs.cache-hit == 'false' }} -# uses: actions/cache/save@v4 +# uses: actions/cache/save@v5 # with: # path: | # ${{ runner.temp }}/apps.zip diff --git a/third-party/SDL/.github/actions/setup-ninja/action.yml b/third-party/SDL/.github/actions/setup-ninja/action.yml index a1d3ad9834..49cc32db69 100644 --- a/third-party/SDL/.github/actions/setup-ninja/action.yml +++ b/third-party/SDL/.github/actions/setup-ninja/action.yml @@ -36,7 +36,7 @@ runs: echo "cache-key=${archive}-${{ inputs.version }}-${{ runner.os }}-${{ runner.arch }}" >> ${GITHUB_OUTPUT} - name: 'Restore cached ${{ steps.calc.outputs.archive }}' id: cache-restore - uses: actions/cache/restore@v4 + uses: actions/cache/restore@v5 with: path: '${{ runner.temp }}/${{ steps.calc.outputs.archive }}' key: ${{ steps.calc.outputs.cache-key }} @@ -47,7 +47,7 @@ runs: Invoke-WebRequest "https://github.com/ninja-build/ninja/releases/download/v${{ inputs.version }}/${{ steps.calc.outputs.archive }}" -OutFile "${{ runner.temp }}/${{ steps.calc.outputs.archive }}" - name: 'Cache ${{ steps.calc.outputs.archive }}' if: ${{ !steps.cache-restore.outputs.cache-hit || steps.cache-restore.outputs.cache-hit == 'false' }} - uses: actions/cache/save@v4 + uses: actions/cache/save@v5 with: path: '${{ runner.temp }}/${{ steps.calc.outputs.archive }}' key: ${{ steps.calc.outputs.cache-key }} diff --git a/third-party/SDL/.github/actions/setup-vita-gles/action.yml b/third-party/SDL/.github/actions/setup-vita-gles/action.yml index e263737b31..31ef799e13 100644 --- a/third-party/SDL/.github/actions/setup-vita-gles/action.yml +++ b/third-party/SDL/.github/actions/setup-vita-gles/action.yml @@ -33,7 +33,7 @@ runs: exit 1 ;; esac - - uses: actions/cache/restore@v4 + - uses: actions/cache/restore@v5 id: restore-cache with: path: /vita/dependencies @@ -81,7 +81,7 @@ runs: wget https://github.com/SonicMastr/gl4es4vita/releases/download/v$gl4es4vita_version-vita/vitasdk_stubs.zip -P/tmp unzip /tmp/vitasdk_stubs.zip -d/vita/dependencies/lib - - uses: actions/cache/save@v4 + - uses: actions/cache/save@v5 if: ${{ !steps.restore-cache.outputs.cache-hit }} with: path: /vita/dependencies diff --git a/third-party/SDL/.github/workflows/create-test-plan.py b/third-party/SDL/.github/workflows/create-test-plan.py old mode 100644 new mode 100755 diff --git a/third-party/SDL/.github/workflows/generic.yml b/third-party/SDL/.github/workflows/generic.yml index 19cf4910ee..183146679b 100644 --- a/third-party/SDL/.github/workflows/generic.yml +++ b/third-party/SDL/.github/workflows/generic.yml @@ -35,7 +35,7 @@ jobs: echo "os=${{ matrix.platform.os }}" echo "" echo "Add [sdl-ci-filter ${{ matrix.platform.key }}] to your commit message to reduce the number of jobs." - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: 'Set up ninja' if: ${{ matrix.platform.setup-ninja }} uses: ./.github/actions/setup-ninja @@ -44,11 +44,11 @@ jobs: uses: ./.github/actions/setup-msvc-libusb with: arch: ${{ matrix.platform.setup-libusb-arch }} - - uses: mymindstorm/setup-emsdk@v14 + - uses: mymindstorm/setup-emsdk@v15 if: ${{ matrix.platform.platform == 'emscripten' }} with: version: 3.1.35 - - uses: browser-actions/setup-chrome@v1 + - uses: browser-actions/setup-chrome@v2 id: setup-chrome if: ${{ matrix.platform.platform == 'emscripten' }} with: @@ -76,7 +76,7 @@ jobs: run: | # We cannot use GitHub expressions in the controller job echo "ANDROID_NDK_HOME=${{ steps.setup-ndk.outputs.ndk-path }}" >>$GITHUB_ENV - - uses: actions/setup-java@v4 + - uses: actions/setup-java@v5 if: ${{ matrix.platform.java }} with: distribution: 'temurin' @@ -171,7 +171,7 @@ jobs: echo "timestamp=$(date -u "+%Y%m%d%H%M_%S")" >> "$GITHUB_OUTPUT" - name: 'Restore ccache' if: ${{ matrix.platform.ccache }} - uses: actions/cache/restore@v4 + uses: actions/cache/restore@v5 id: restore-ccache with: path: ${{ runner.temp }}/ccache @@ -319,7 +319,7 @@ jobs: - name: 'Build (cross-platform-actions, BSD)' id: cpactions if: ${{ matrix.platform.cpactions }} - uses: cross-platform-actions/action@v0.29.0 + uses: cross-platform-actions/action@v0.32.0 with: operating_system: '${{ matrix.platform.cpactions-os }}' architecture: '${{ matrix.platform.cpactions-arch }}' @@ -349,7 +349,7 @@ jobs: - name: Add msbuild to PATH id: setup-msbuild if: ${{ matrix.platform.msvc-project != '' }} - uses: microsoft/setup-msbuild@v2 + uses: microsoft/setup-msbuild@v3 - name: Build msbuild if: ${{ matrix.platform.msvc-project != '' }} run: | @@ -393,7 +393,7 @@ jobs: ccache -s - name: 'Save ccache' if: ${{ matrix.platform.ccache }} - uses: actions/cache/save@v4 + uses: actions/cache/save@v5 with: path: ${{ runner.temp }}/ccache key: ${{ steps.restore-ccache.outputs.cache-primary-key }} @@ -413,7 +413,7 @@ jobs: run: | find ./ -iname '*.so' | xargs -L1 ./build-scripts/check_elf_alignment.sh - name: 'Upload binary package' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 if: ${{ always() && matrix.platform.artifact != '' && (steps.package.outcome == 'success' || steps.cpactions.outcome == 'success') && (matrix.platform.enable-artifacts || steps.tests.outcome == 'failure') }} with: if-no-files-found: error @@ -422,14 +422,14 @@ jobs: build/dist/SDL3* build/include* - name: 'Upload minidumps' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 if: ${{ always() && steps.tests.outcome == 'failure' && (matrix.platform.platform == 'msvc' || matrix.platform.platform == 'msys2') }} with: if-no-files-found: ignore name: '${{ matrix.platform.artifact }}-minidumps' path: build/**/*.dmp - name: "Upload Android test apk's" - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 if: ${{ matrix.platform.enable-artifacts && always() && matrix.platform.artifact != '' && steps.apks.outcome == 'success' }} with: if-no-files-found: error diff --git a/third-party/SDL/.github/workflows/release.yml b/third-party/SDL/.github/workflows/release.yml index cb578e6d25..ba43ece625 100644 --- a/third-party/SDL/.github/workflows/release.yml +++ b/third-party/SDL/.github/workflows/release.yml @@ -20,15 +20,15 @@ jobs: src-zip: ${{ steps.releaser.outputs.src-zip }} steps: - name: 'Set up Python' - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: '3.11' - name: 'Fetch build-release.py' - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: sparse-checkout: 'build-scripts/build-release.py' - name: 'Set up SDL sources' - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: path: 'SDL' fetch-depth: 0 @@ -43,7 +43,7 @@ jobs: --github \ --debug - name: 'Store source archives' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: sources path: '${{ github.workspace}}/dist' @@ -61,7 +61,7 @@ jobs: runs-on: ubuntu-latest steps: - name: 'Download source archives' - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: name: sources path: '${{ github.workspace }}' @@ -114,15 +114,15 @@ jobs: dmg: ${{ steps.releaser.outputs.dmg }} steps: - name: 'Set up Python' - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: '3.11' - name: 'Fetch build-release.py' - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: sparse-checkout: 'build-scripts/build-release.py' - name: 'Download source archives' - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: name: sources path: '${{ github.workspace }}' @@ -143,7 +143,7 @@ jobs: --github \ --debug - name: 'Store DMG image file' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: dmg path: '${{ github.workspace }}/dist' @@ -153,12 +153,12 @@ jobs: runs-on: macos-latest steps: - name: 'Download source archives' - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: name: sources path: '${{ github.workspace }}' - name: 'Download ${{ needs.dmg.outputs.dmg }}' - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: name: dmg path: '${{ github.workspace }}' @@ -322,15 +322,15 @@ jobs: VC-devel: ${{ steps.releaser.outputs.VC-devel }} steps: - name: 'Set up Python' - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: '3.11' - name: 'Fetch build-release.py' - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: sparse-checkout: 'build-scripts/build-release.py' - name: 'Download source archives' - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: name: sources path: '${{ github.workspace }}' @@ -351,7 +351,7 @@ jobs: --github ` --debug - name: 'Store MSVC archives' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: win32 path: '${{ github.workspace }}/dist' @@ -361,16 +361,16 @@ jobs: runs-on: windows-latest steps: - name: 'Fetch .github/actions/setup-ninja/action.yml' - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: sparse-checkout: '.github/actions/setup-ninja/action.yml' - name: 'Download source archives' - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: name: sources path: '${{ github.workspace }}' - name: 'Download MSVC binaries' - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: name: win32 path: '${{ github.workspace }}' @@ -481,11 +481,11 @@ jobs: mingw-devel-tar-xz: ${{ steps.releaser.outputs.mingw-devel-tar-xz }} steps: - name: 'Set up Python' - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: '3.11' - name: 'Fetch build-release.py' - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: sparse-checkout: 'build-scripts/build-release.py' - name: 'Install Mingw toolchain' @@ -493,7 +493,7 @@ jobs: sudo apt-get update -y sudo apt-get install -y gcc-mingw-w64 g++-mingw-w64 ninja-build - name: 'Download source archives' - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: name: sources path: '${{ github.workspace }}' @@ -513,7 +513,7 @@ jobs: --github \ --debug - name: 'Store MinGW archives' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: mingw path: '${{ github.workspace }}/dist' @@ -527,12 +527,12 @@ jobs: sudo apt-get update -y sudo apt-get install -y gcc-mingw-w64 g++-mingw-w64 ninja-build - name: 'Download source archives' - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: name: sources path: '${{ github.workspace }}' - name: 'Download MinGW binaries' - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: name: mingw path: '${{ github.workspace }}' @@ -582,11 +582,11 @@ jobs: android-aar: ${{ steps.releaser.outputs.android-aar }} steps: - name: 'Set up Python' - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: '3.11' - name: 'Fetch build-release.py' - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: sparse-checkout: 'build-scripts/build-release.py' - name: 'Setup Android NDK' @@ -596,7 +596,7 @@ jobs: local-cache: false ndk-version: r28c - name: 'Setup Java JDK' - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: '11' @@ -605,7 +605,7 @@ jobs: sudo apt-get update -y sudo apt-get install -y ninja-build - name: 'Download source archives' - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: name: sources path: '${{ github.workspace }}' @@ -627,7 +627,7 @@ jobs: --github \ --debug - name: 'Store Android archive(s)' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: android path: '${{ github.workspace }}/dist' @@ -637,20 +637,20 @@ jobs: runs-on: ubuntu-latest steps: - name: 'Set up Python' - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: '3.11' - - uses: actions/setup-java@v4 + - uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: '17' - name: 'Download source archives' - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: name: sources path: '${{ github.workspace }}' - name: 'Download Android .aar archive' - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: name: android path: '${{ github.workspace }}' diff --git a/third-party/SDL/AGENTS.md b/third-party/SDL/AGENTS.md new file mode 100644 index 0000000000..7709baa11a --- /dev/null +++ b/third-party/SDL/AGENTS.md @@ -0,0 +1,20 @@ +AI must not be used to generate code for contributions to this project. + +"AI" in this case means a Large Language Model ("LLM"), such as ChatGPT, +Claude, Copilot, Grok, etc. + +AI-generated code is based upon sources of unknown origins and may not be +compatible with the Zlib license, or may introduce conflicting license terms +if they include code from other projects. + +AI can be used to identify issues with contributions to this project, but the +solutions to those issues should be authored by humans. + +We have found that AI will frequently hallucinate issues that are not actually +problems in practice, report incorrect information, and describe problems that +are actually not issues at all. If AI identifies a problem with this codebase, +please make sure you understand what it is saying and have independently +confirmed that the issue exists before submitting a bug report or pull request. + +Any pull request to this project will ask you to confirm that you are the +author and that you are contributing your changes under the Zlib license. diff --git a/third-party/SDL/CLAUDE.md b/third-party/SDL/CLAUDE.md new file mode 100644 index 0000000000..c8408cf2c1 --- /dev/null +++ b/third-party/SDL/CLAUDE.md @@ -0,0 +1,22 @@ +AI must not be used to generate code for contributions to this project. + +"AI" in this case means a Large Language Model ("LLM"), such as ChatGPT, +Claude, Copilot, Grok, etc. + +AI-generated code is based upon sources of unknown origins and may not be +compatible with the Zlib license, or may introduce conflicting license terms +if they include code from other projects. + +AI can be used to identify issues with contributions to this project, but the +solutions to those issues should be authored by humans. + +We have found that AI will frequently hallucinate issues that are not actually +problems in practice, report incorrect information, and describe problems that +are actually not issues at all. If AI identifies a problem with this codebase, +please make sure you understand what it is saying and have independently +confirmed that the issue exists before submitting a bug report or pull request. + +Any pull request to this project will ask you to confirm that you are the +author and that you are contributing your changes under the Zlib license. + + diff --git a/third-party/SDL/CMakeLists.txt b/third-party/SDL/CMakeLists.txt index b01acc3bc5..faf6d900cc 100644 --- a/third-party/SDL/CMakeLists.txt +++ b/third-party/SDL/CMakeLists.txt @@ -5,7 +5,7 @@ if(NOT DEFINED CMAKE_BUILD_TYPE) endif() # See docs/release_checklist.md -project(SDL3 LANGUAGES C VERSION "3.4.4") +project(SDL3 LANGUAGES C VERSION "3.4.10") if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) set(SDL3_MAINPROJECT ON) @@ -1097,7 +1097,7 @@ if(SDL_LIBC) ) if(WINDOWS) list(APPEND symbols_to_check - _copysign _fseeki64 _strrev _ui64toa _uitoa _ultoa _wcsdup + _copysign _fseeki64 _strrev _ui64toa _ultoa _wcsdup ) else() list(APPEND symbols_to_check @@ -1179,34 +1179,33 @@ if(SDL_LIBC) check_symbol_exists(posix_fallocate "fcntl.h" HAVE_POSIX_FALLOCATE) check_symbol_exists(posix_spawn_file_actions_addchdir "spawn.h" HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR) check_symbol_exists(posix_spawn_file_actions_addchdir_np "spawn.h" HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR_NP) + endif() + if(SDL_SYSTEM_ICONV) + check_c_source_compiles(" + #define LIBICONV_PLUG 1 /* in case libiconv header is in include path */ + #include + #include + int main(int argc, char **argv) { + return !iconv_open(NULL,NULL); + }" ICONV_IN_LIBC) - if(SDL_SYSTEM_ICONV) - check_c_source_compiles(" - #define LIBICONV_PLUG 1 /* in case libiconv header is in include path */ - #include - #include - int main(int argc, char **argv) { - return !iconv_open(NULL,NULL); - }" ICONV_IN_LIBC) + cmake_push_check_state() + list(APPEND CMAKE_REQUIRED_LIBRARIES iconv) + check_c_source_compiles(" + #include + #include + int main(int argc, char **argv) { + return !iconv_open(NULL,NULL); + }" ICONV_IN_LIBICONV) + cmake_pop_check_state() - cmake_push_check_state() - list(APPEND CMAKE_REQUIRED_LIBRARIES iconv) - check_c_source_compiles(" - #include - #include - int main(int argc, char **argv) { - return !iconv_open(NULL,NULL); - }" ICONV_IN_LIBICONV) - cmake_pop_check_state() - - if(ICONV_IN_LIBC OR ICONV_IN_LIBICONV) - set(HAVE_ICONV 1) - set(HAVE_SYSTEM_ICONV TRUE) - if(ICONV_IN_LIBICONV AND (SDL_LIBICONV OR (NOT ICONV_IN_LIBC))) - sdl_link_dependency(iconv LIBS iconv) - set(SDL_USE_LIBICONV 1) - set(HAVE_LIBICONV TRUE) - endif() + if(ICONV_IN_LIBC OR ICONV_IN_LIBICONV) + set(HAVE_ICONV 1) + set(HAVE_SYSTEM_ICONV TRUE) + if(ICONV_IN_LIBICONV AND (SDL_LIBICONV OR (NOT ICONV_IN_LIBC))) + sdl_link_dependency(iconv LIBS iconv) + set(SDL_USE_LIBICONV 1) + set(HAVE_LIBICONV TRUE) endif() endif() @@ -3235,6 +3234,7 @@ elseif(PS2) gskit dmakit ps2_drivers + atomic ) elseif(N3DS) sdl_glob_sources("${SDL3_SOURCE_DIR}/src/main/n3ds/*.c") diff --git a/third-party/SDL/VisualC-GDK/clean.sh b/third-party/SDL/VisualC-GDK/clean.sh old mode 100644 new mode 100755 diff --git a/third-party/SDL/VisualC/clean.sh b/third-party/SDL/VisualC/clean.sh old mode 100644 new mode 100755 diff --git a/third-party/SDL/VisualC/examples/generate.py b/third-party/SDL/VisualC/examples/generate.py old mode 100644 new mode 100755 diff --git a/third-party/SDL/Xcode/SDL/Info-Framework.plist b/third-party/SDL/Xcode/SDL/Info-Framework.plist index 0bc735b95a..7f1a985a4c 100644 --- a/third-party/SDL/Xcode/SDL/Info-Framework.plist +++ b/third-party/SDL/Xcode/SDL/Info-Framework.plist @@ -19,10 +19,10 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 3.4.4 + 3.4.10 CFBundleSignature SDLX CFBundleVersion - 3.4.4 + 3.4.10 diff --git a/third-party/SDL/Xcode/SDL/SDL.xcodeproj/project.pbxproj b/third-party/SDL/Xcode/SDL/SDL.xcodeproj/project.pbxproj index fde6645c8d..141e637275 100644 --- a/third-party/SDL/Xcode/SDL/SDL.xcodeproj/project.pbxproj +++ b/third-party/SDL/Xcode/SDL/SDL.xcodeproj/project.pbxproj @@ -3162,7 +3162,7 @@ CLANG_ENABLE_OBJC_ARC = YES; DEPLOYMENT_POSTPROCESSING = YES; DYLIB_COMPATIBILITY_VERSION = 401.0.0; - DYLIB_CURRENT_VERSION = 401.4.0; + DYLIB_CURRENT_VERSION = 401.10.0; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_ALTIVEC_EXTENSIONS = YES; @@ -3197,7 +3197,7 @@ "@loader_path/Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.13; - MARKETING_VERSION = 3.4.4; + MARKETING_VERSION = 3.4.10; OTHER_LDFLAGS = "$(CONFIG_FRAMEWORK_LDFLAGS)"; PRODUCT_BUNDLE_IDENTIFIER = org.libsdl.SDL3; PRODUCT_NAME = SDL3; @@ -3228,7 +3228,7 @@ CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; DYLIB_COMPATIBILITY_VERSION = 401.0.0; - DYLIB_CURRENT_VERSION = 401.4.0; + DYLIB_CURRENT_VERSION = 401.10.0; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; @@ -3260,7 +3260,7 @@ "@loader_path/Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.13; - MARKETING_VERSION = 3.4.4; + MARKETING_VERSION = 3.4.10; ONLY_ACTIVE_ARCH = YES; OTHER_LDFLAGS = "$(CONFIG_FRAMEWORK_LDFLAGS)"; PRODUCT_BUNDLE_IDENTIFIER = org.libsdl.SDL3; diff --git a/third-party/SDL/Xcode/SDL/pkg-support/SDL.info b/third-party/SDL/Xcode/SDL/pkg-support/SDL.info index 3bb0293b48..9353947b16 100644 --- a/third-party/SDL/Xcode/SDL/pkg-support/SDL.info +++ b/third-party/SDL/Xcode/SDL/pkg-support/SDL.info @@ -1,4 +1,4 @@ -Title SDL 3.4.4 +Title SDL 3.4.10 Version 1 Description SDL Library for macOS (http://www.libsdl.org) DefaultLocation /Library/Frameworks diff --git a/third-party/SDL/android-project/app/proguard-rules.pro b/third-party/SDL/android-project/app/proguard-rules.pro index 0fb7ae09ac..a13768966c 100644 --- a/third-party/SDL/android-project/app/proguard-rules.pro +++ b/third-party/SDL/android-project/app/proguard-rules.pro @@ -48,7 +48,7 @@ boolean showTextInput(int, int, int, int, int); boolean supportsRelativeMouse(); int openFileDescriptor(java.lang.String, java.lang.String); - boolean showFileDialog(java.lang.String[], boolean, boolean, int); + boolean showFileDialog(java.lang.String[], boolean, int, java.lang.String, int); java.lang.String getPreferredLocales(); java.lang.String formatLocale(java.util.Locale); } @@ -68,6 +68,7 @@ } -keep,includedescriptorclasses,allowoptimization class org.libsdl.app.SDLControllerManager { + void joystickSetSensorsEnabled(int, boolean); void pollInputDevices(); void joystickSetLED(int, int, int, int); void pollHapticDevices(); diff --git a/third-party/SDL/android-project/app/src/main/java/org/libsdl/app/HIDDeviceBLESteamController.java b/third-party/SDL/android-project/app/src/main/java/org/libsdl/app/HIDDeviceBLESteamController.java index bf1ca2149d..bcd8806c41 100644 --- a/third-party/SDL/android-project/app/src/main/java/org/libsdl/app/HIDDeviceBLESteamController.java +++ b/third-party/SDL/android-project/app/src/main/java/org/libsdl/app/HIDDeviceBLESteamController.java @@ -19,9 +19,13 @@ import android.os.*; import java.lang.Runnable; import java.util.Arrays; +import java.util.HashMap; import java.util.LinkedList; import java.util.UUID; +import java.util.regex.Pattern; +import java.util.regex.Matcher; + class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDevice { private static final String TAG = "hidapi"; @@ -33,10 +37,19 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe private boolean mIsConnected = false; private boolean mIsChromebook = false; private boolean mIsReconnecting = false; + private boolean mHasEnabledNotifications = false; + private boolean mHasSeenInputUpdate = false; private boolean mFrozen = false; private LinkedList mOperations; GattOperation mCurrentOperation = null; private Handler mHandler; + private int mProductId = -1; + private int mReportId = 0; + private UUID mInputCharacteristic; + + private static final int D0G_BLE2_PID = 0x1106; + private static final int TRITON_BLE_PID = 0x1303; + private static final int TRANSPORT_AUTO = 0; private static final int TRANSPORT_BREDR = 1; @@ -45,10 +58,14 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe private static final int CHROMEBOOK_CONNECTION_CHECK_INTERVAL = 10000; static final UUID steamControllerService = UUID.fromString("100F6C32-1735-4313-B402-38567131E5F3"); - static final UUID inputCharacteristic = UUID.fromString("100F6C33-1735-4313-B402-38567131E5F3"); + static final UUID inputCharacteristicD0G = UUID.fromString("100F6C33-1735-4313-B402-38567131E5F3"); + static final UUID inputCharacteristicTriton_0x45 = UUID.fromString("100F6C7A-1735-4313-B402-38567131E5F3"); + static final UUID inputCharacteristicTriton_0x47 = UUID.fromString("100F6C7C-1735-4313-B402-38567131E5F3"); static final UUID reportCharacteristic = UUID.fromString("100F6C34-1735-4313-B402-38567131E5F3"); static private final byte[] enterValveMode = new byte[] { (byte)0xC0, (byte)0x87, 0x03, 0x08, 0x07, 0x00 }; + private HashMap mOutputReportChars = new HashMap(); + static class GattOperation { private enum Operation { CHR_READ, @@ -61,6 +78,7 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe byte[] mValue; BluetoothGatt mGatt; boolean mResult = true; + int mDelayMs = 0; private GattOperation(BluetoothGatt gatt, GattOperation.Operation operation, UUID uuid) { mGatt = gatt; @@ -68,6 +86,13 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe mUuid = uuid; } + private GattOperation(BluetoothGatt gatt, GattOperation.Operation operation, UUID uuid, int delayMs) { + mGatt = gatt; + mOp = operation; + mUuid = uuid; + mDelayMs = delayMs; + } + private GattOperation(BluetoothGatt gatt, GattOperation.Operation operation, UUID uuid, byte[] value) { mGatt = gatt; mOp = operation; @@ -75,6 +100,14 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe mValue = value; } + private GattOperation(BluetoothGatt gatt, GattOperation.Operation operation, UUID uuid, byte[] value, int delayMs) { + mGatt = gatt; + mOp = operation; + mUuid = uuid; + mValue = value; + mDelayMs = delayMs; + } + public void run() { // This is executed in main thread BluetoothGattCharacteristic chr; @@ -136,6 +169,8 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe return mResult; } + public int getDelayMs() { return mDelayMs; } + private BluetoothGattCharacteristic getCharacteristic(UUID uuid) { BluetoothGattService valveService = mGatt.getService(steamControllerService); if (valveService == null) @@ -154,6 +189,10 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe static public GattOperation enableNotification(BluetoothGatt gatt, UUID uuid) { return new GattOperation(gatt, Operation.ENABLE_NOTIFICATION, uuid); } + + static public GattOperation enableNotification(BluetoothGatt gatt, UUID uuid, int delayMs) { + return new GattOperation(gatt, Operation.ENABLE_NOTIFICATION, uuid, delayMs); + } } HIDDeviceBLESteamController(HIDDeviceManager manager, BluetoothDevice device) { @@ -166,6 +205,8 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe mHandler = new Handler(Looper.getMainLooper()); mGatt = connectGatt(); + mHasEnabledNotifications = false; + mHasSeenInputUpdate = false; // final HIDDeviceBLESteamController finalThis = this; // mHandler.postDelayed(new Runnable() { // @Override @@ -314,8 +355,45 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe Log.v(TAG, "Found Valve steam controller service " + service.getUuid()); for (BluetoothGattCharacteristic chr : service.getCharacteristics()) { - if (chr.getUuid().equals(inputCharacteristic)) { - Log.v(TAG, "Found input characteristic"); + if (chr.getUuid().equals(inputCharacteristicTriton_0x45)) { + Log.v(TAG, "Found Triton input characteristic 0x45"); + mProductId = TRITON_BLE_PID; + mReportId = 0x45; + mInputCharacteristic = chr.getUuid(); + } else if (chr.getUuid().equals(inputCharacteristicTriton_0x47)) { + Log.v(TAG, "Found Triton input characteristic 0x47"); + mProductId = TRITON_BLE_PID; + mReportId = 0x47; + mInputCharacteristic = chr.getUuid(); + } else if (chr.getUuid().equals(inputCharacteristicD0G)) { + Log.v(TAG, "Found D0G input characteristic"); + mProductId = D0G_BLE2_PID; + mReportId = 0x03; + mInputCharacteristic = chr.getUuid(); + } else { + Pattern reportPattern = Pattern.compile("100F6C([0-9A-Z]{2})", Pattern.CASE_INSENSITIVE); + Matcher matcher = reportPattern.matcher(chr.getUuid().toString()); + + if (matcher.find()) { + try { + int reportId = Integer.parseInt(matcher.group(1), 16); + + reportId -= 0x35; + if (reportId >= 0x80) { + // This is a Triton output report characteristic that we need to care about. + Log.v(TAG, "Found Triton output report 0x" + Integer.toString(reportId, 16)); + mOutputReportChars.put(reportId, chr); + } + } + catch (NumberFormatException nfe) { + Log.w(TAG, "Could not parse report characteristic " + chr.getUuid().toString() + ": " + nfe.toString()); + } + } + } + } + + for (BluetoothGattCharacteristic chr : service.getCharacteristics()) { + if (chr.getUuid().equals(mInputCharacteristic)) { // Start notifications BluetoothGattDescriptor cccd = chr.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")); if (cccd != null) { @@ -372,21 +450,30 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe mCurrentOperation = mOperations.removeFirst(); } - // Run in main thread - mHandler.post(new Runnable() { - @Override - public void run() { - synchronized (mOperations) { - if (mCurrentOperation == null) { - Log.e(TAG, "Current operation null in executor?"); - return; - } + Runnable gattOperationRunnable = new Runnable() { + @Override + public void run() { + synchronized (mOperations) { + if (mCurrentOperation == null) { + Log.e(TAG, "Current operation null in executor?"); + return; + } - mCurrentOperation.run(); - // now wait for the GATT callback and when it comes, finish this operation + mCurrentOperation.run(); + // now wait for the GATT callback and when it comes, finish this operation + } } - } - }); + }; + + if (mCurrentOperation.getDelayMs() == 0) { + // Run in main thread + mHandler.post(gattOperationRunnable); + } + else { + // If we have a delay on this operation, wait before we post it. + mHandler.postDelayed(gattOperationRunnable, mCurrentOperation.getDelayMs()); + } + } private void queueGattOperation(GattOperation op) { @@ -397,8 +484,39 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe } private void enableNotification(UUID chrUuid) { - GattOperation op = HIDDeviceBLESteamController.GattOperation.enableNotification(mGatt, chrUuid); + // Add a 500ms delay to notification write for Amazon Fire TV devices, as otherwise if we do this too quickly after connecting + // it will return success and then silently drop the operation on the floor. + GattOperation op = HIDDeviceBLESteamController.GattOperation.enableNotification(mGatt, chrUuid, 500); queueGattOperation(op); + + // Amazon Fire devices can also silently timeout on writeDescriptor, so + // set up a little delayed check that will attempt to write a second time. + // + // While this only seems to be needed on Amazon Fire TV devices at present, it + // doesn't hurt to have a retry on other devices as well. + // + final HIDDeviceBLESteamController finalThis = this; + final UUID finalUuid = chrUuid; + mHandler.postDelayed(new Runnable() { + @Override + public void run() { + if (!finalThis.mHasEnabledNotifications) { + + if (finalThis.mHasSeenInputUpdate) { + // Amazon Five devices may have enabled notifications on the input characteristic and not given us a callback. If we've seen + // input reports, though, somewhat by definition notifications are enabled. + Log.w(TAG, "WriteDescriptor has never returned, but we've seen input reports. Moving on with controller initialization."); + finalThis.mHasEnabledNotifications = true; + finalThis.enableValveMode(); + return; + } + + // Give one more try. + GattOperation retry = HIDDeviceBLESteamController.GattOperation.enableNotification(finalThis.mGatt, finalUuid, 500); + finalThis.queueGattOperation(retry); + } + } + }, 1000); } void writeCharacteristic(UUID uuid, byte[] value) { @@ -448,8 +566,16 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe mIsConnected = false; gatt.disconnect(); mGatt = connectGatt(false); - } - else { + } else { + if (getProductId() == TRITON_BLE_PID) { + // Android will not properly play well with Data Length Extensions without manually requesting a large MTU, + // and Triton controllers require DLE support. + // + // 517 is basically a "magic number" as far as Android's bluetooth code is concerned, so do not change + // this value. It is functionally "please enable data length extensions" on some Android builds. + mGatt.requestMtu(517); + } + probeService(this); } } @@ -474,7 +600,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, true); + mManager.HIDDeviceConnected(getId(), getIdentifier(), getVendorId(), getProductId(), getSerialNumber(), getVersion(), getManufacturerName(), getProductName(), 0, 0, 0, 0, true, mReportId); setRegistered(); } } @@ -487,7 +613,8 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe // Enable this for verbose logging of controller input reports //Log.v(TAG, "onCharacteristicChanged uuid=" + characteristic.getUuid() + " data=" + HexDump.dumpHexString(characteristic.getValue())); - if (characteristic.getUuid().equals(inputCharacteristic) && !mFrozen) { + if (characteristic.getUuid().equals(mInputCharacteristic) && !mFrozen) { + mHasSeenInputUpdate = true; mManager.HIDDeviceInputReport(getId(), characteristic.getValue()); } } @@ -497,19 +624,36 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe //Log.v(TAG, "onDescriptorRead status=" + status); } + private void enableValveMode() + { + BluetoothGattService valveService = mGatt.getService(steamControllerService); + if (valveService == null) + return; + + BluetoothGattCharacteristic reportChr = valveService.getCharacteristic(reportCharacteristic); + if (reportChr != null) { + if (getProductId() == TRITON_BLE_PID) { + // For Triton we just mark things registered. + Log.v(TAG, "Registering Triton Steam Controller with ID: " + getId()); + mManager.HIDDeviceConnected(getId(), getIdentifier(), getVendorId(), getProductId(), getSerialNumber(), getVersion(), getManufacturerName(), getProductName(), 0, 0, 0, 0, true, mReportId); + setRegistered(); + } else { + // For the original controller, we need to manually enter Valve mode. + Log.v(TAG, "Writing report characteristic to enter valve mode"); + reportChr.setValue(enterValveMode); + mGatt.writeCharacteristic(reportChr); + } + } + } + @Override public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { BluetoothGattCharacteristic chr = descriptor.getCharacteristic(); //Log.v(TAG, "onDescriptorWrite status=" + status + " uuid=" + chr.getUuid() + " descriptor=" + descriptor.getUuid()); - if (chr.getUuid().equals(inputCharacteristic)) { - boolean hasWrittenInputDescriptor = true; - BluetoothGattCharacteristic reportChr = chr.getService().getCharacteristic(reportCharacteristic); - if (reportChr != null) { - Log.v(TAG, "Writing report characteristic to enter valve mode"); - reportChr.setValue(enterValveMode); - gatt.writeCharacteristic(reportChr); - } + if (chr.getUuid().equals(mInputCharacteristic)) { + mHasEnabledNotifications = true; + enableValveMode(); } finishCurrentGattOperation(); @@ -548,9 +692,20 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe @Override public int getProductId() { - // We don't have an easy way to query from the Bluetooth device, but we know what it is - final int D0G_BLE2_PID = 0x1106; - return D0G_BLE2_PID; + if (mProductId > 0) { + // We've already set a product ID. + return mProductId; + } + + if (mDevice.getName().startsWith("Steam Ctrl")) { + // We're a newer Triton device + mProductId = TRITON_BLE_PID; + } else { + // We're an OG Steam Controller + mProductId = D0G_BLE2_PID; + } + + return mProductId; } @Override @@ -601,10 +756,29 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe writeCharacteristic(reportCharacteristic, actual_report); return report.length; } else { - //Log.v(TAG, "writeOutputReport " + HexDump.dumpHexString(report)); - writeCharacteristic(reportCharacteristic, report); - return report.length; + // If we're an original-recipe Steam Controller we just write to the characteristic directly. + if (getProductId() == D0G_BLE2_PID) { + //Log.v(TAG, "writeOutputReport " + HexDump.dumpHexString(report)); + writeCharacteristic(reportCharacteristic, report); + return report.length; + } + + // If we're a Triton, we need to find the correct report characteristic. + if (report.length > 0) { + int reportId = report[0] & 0xFF; + BluetoothGattCharacteristic targetedReportCharacteristic = mOutputReportChars.get(reportId); + if (targetedReportCharacteristic != null) { + byte[] actual_report = Arrays.copyOfRange(report, 1, report.length - 1); + //Log.v(TAG, "writeOutputReport 0x" + Integer.toString(reportId, 16) + " " + HexDump.dumpHexString(report)); + writeCharacteristic(targetedReportCharacteristic.getUuid(), actual_report); + return report.length; + } else { + Log.w(TAG, "Got report write request for unknown report type 0x" + Integer.toString(reportId, 16)); + } + } } + + return -1; } @Override diff --git a/third-party/SDL/android-project/app/src/main/java/org/libsdl/app/HIDDeviceManager.java b/third-party/SDL/android-project/app/src/main/java/org/libsdl/app/HIDDeviceManager.java index 1fb2bfb4a7..691416c1c9 100644 --- a/third-party/SDL/android-project/app/src/main/java/org/libsdl/app/HIDDeviceManager.java +++ b/third-party/SDL/android-project/app/src/main/java/org/libsdl/app/HIDDeviceManager.java @@ -256,6 +256,7 @@ public class HIDDeviceManager { 0x24c6, // PowerA 0x2c22, // Qanba 0x2dc8, // 8BitDo + 0x3537, // GameSir 0x37d7, // Flydigi 0x9886, // ASTRO Gaming }; @@ -360,7 +361,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(), false); + 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, 0); } } } @@ -529,7 +530,13 @@ public class HIDDeviceManager { return false; } - return bluetoothDevice.getName().equals("SteamController") && ((bluetoothDevice.getType() & BluetoothDevice.DEVICE_TYPE_LE) != 0); + // Steam Controllers will always support Bluetooth Low Energy + if ((bluetoothDevice.getType() & BluetoothDevice.DEVICE_TYPE_LE) == 0) { + return false; + } + + // Match on the name either the original Steam Controller or the new second-generation one advertise with. + return bluetoothDevice.getName().equals("SteamController") || bluetoothDevice.getName().startsWith("Steam Ctrl"); } private void close() { @@ -681,7 +688,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, boolean bBluetooth); + 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, int reportID); native void HIDDeviceOpenPending(int deviceID); native void HIDDeviceOpenResult(int deviceID, boolean opened); native void HIDDeviceDisconnected(int deviceID); diff --git a/third-party/SDL/android-project/app/src/main/java/org/libsdl/app/HIDDeviceUSB.java b/third-party/SDL/android-project/app/src/main/java/org/libsdl/app/HIDDeviceUSB.java index f9e9389802..8954639733 100644 --- a/third-party/SDL/android-project/app/src/main/java/org/libsdl/app/HIDDeviceUSB.java +++ b/third-party/SDL/android-project/app/src/main/java/org/libsdl/app/HIDDeviceUSB.java @@ -21,6 +21,7 @@ class HIDDeviceUSB implements HIDDevice { protected InputThread mInputThread; protected boolean mRunning; protected boolean mFrozen; + protected boolean mClaimed; public HIDDeviceUSB(HIDDeviceManager manager, UsbDevice usbDevice, int interface_index) { mManager = manager; @@ -29,6 +30,7 @@ class HIDDeviceUSB implements HIDDevice { mInterface = mDevice.getInterface(mInterfaceIndex).getId(); mDeviceId = manager.getDeviceIDForIdentifier(getIdentifier()); mRunning = false; + mClaimed = false; } String getIdentifier() { @@ -114,6 +116,7 @@ class HIDDeviceUSB implements HIDDevice { close(); return false; } + mClaimed = true; // Find the endpoints for (int j = 0; j < iface.getEndpointCount(); j++) { @@ -132,9 +135,12 @@ class HIDDeviceUSB implements HIDDevice { } } - // Make sure the required endpoints were present - if (mInputEndpoint == null || mOutputEndpoint == null) { + // Make sure the required endpoints were present. The original Steam Controller and the wireless dongle for it do NOT + // actually have -- or require -- output endpoints, so we need to accept only an input one for them or else we'll fall + // back to the Android system gamepad functionality (and lose our paddles et al). + if (mInputEndpoint == null) { Log.w(TAG, "Missing required endpoint on USB device " + getDeviceName()); + mConnection.releaseInterface(iface); close(); return false; } @@ -154,6 +160,11 @@ class HIDDeviceUSB implements HIDDevice { return -1; } + if (!mClaimed) { + Log.w(TAG, "writeReport() called but some other process currently owns the USB device"); + return -1; + } + if (feature) { int res = -1; int offset = 0; @@ -185,6 +196,11 @@ class HIDDeviceUSB implements HIDDevice { } return length; } else { + if (mOutputEndpoint == null) + { + Log.e(TAG, "Tried to write an output report to an interface with no output endpoint!"); + return -1; + } int res = mConnection.bulkTransfer(mOutputEndpoint, report, report.length, 1000); if (res != report.length) { Log.w(TAG, "writeOutputReport() returned " + res + " on device " + getDeviceName()); @@ -205,6 +221,12 @@ class HIDDeviceUSB implements HIDDevice { Log.w(TAG, "readReport() called with no device connection"); return false; } + if (!mClaimed) { + if (feature) { + return false; + } + return true; + } if (report_number == 0x0) { /* Offset the return buffer by 1, so that the report ID @@ -258,10 +280,13 @@ class HIDDeviceUSB implements HIDDevice { mInputThread = null; } if (mConnection != null) { - UsbInterface iface = mDevice.getInterface(mInterfaceIndex); - mConnection.releaseInterface(iface); + if (mClaimed) { + UsbInterface iface = mDevice.getInterface(mInterfaceIndex); + mConnection.releaseInterface(iface); + } mConnection.close(); mConnection = null; + mClaimed = false; } } @@ -274,6 +299,22 @@ class HIDDeviceUSB implements HIDDevice { @Override public void setFrozen(boolean frozen) { mFrozen = frozen; + + /* If we have a valid device connection and the claim state doesn't match what we want, try to correct that. */ + if (mConnection != null && mClaimed == mFrozen) { + UsbInterface iface = mDevice.getInterface(mInterfaceIndex); + if (frozen) { + mClaimed = !mConnection.releaseInterface(iface); + if (mClaimed) { + Log.e(TAG, "Tried to release claim on USB device, but failed!"); + } + } else { + mClaimed = mConnection.claimInterface(iface, true); + if (!mClaimed) { + Log.e(TAG, "Tried to regain claim on USB device, but failed!"); + } + } + } } protected class InputThread extends Thread { diff --git a/third-party/SDL/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java b/third-party/SDL/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java index b919192d7e..d7d22a0d8c 100644 --- a/third-party/SDL/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java +++ b/third-party/SDL/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java @@ -61,7 +61,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh private static final String TAG = "SDL"; private static final int SDL_MAJOR_VERSION = 3; private static final int SDL_MINOR_VERSION = 4; - private static final int SDL_MICRO_VERSION = 4; + private static final int SDL_MICRO_VERSION = 10; /* // Display InputType.SOURCE/CLASS of events and devices // @@ -530,7 +530,8 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh if (mHIDDeviceManager != null) { mHIDDeviceManager.setFrozen(true); - } + } + if (!mHasMultiWindow) { pauseNativeThread(); } @@ -543,7 +544,8 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh if (mHIDDeviceManager != null) { mHIDDeviceManager.setFrozen(false); - } + } + if (!mHasMultiWindow) { resumeNativeThread(); } @@ -570,7 +572,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh public static int getNaturalOrientation() { int result = SDL_ORIENTATION_UNKNOWN; - Activity activity = (Activity)getContext(); + Activity activity = getContext(); if (activity != null) { Configuration config = activity.getResources().getConfiguration(); Display display = activity.getWindowManager().getDefaultDisplay(); @@ -590,7 +592,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh public static int getCurrentRotation() { int result = 0; - Activity activity = (Activity)getContext(); + Activity activity = getContext(); if (activity != null) { Display display = activity.getWindowManager().getDefaultDisplay(); switch (display.getRotation()) { @@ -616,6 +618,14 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh super.onWindowFocusChanged(hasFocus); Log.v(TAG, "onWindowFocusChanged(): " + hasFocus); + // If we are gaining focus, we can always try to restore our USB devices. If we are losing focus, + // only try to relinquish them if we don't have background events allowed (for multi-window Android setups). + if (hasFocus || !SDLActivity.nativeGetHintBoolean("SDL_JOYSTICK_ALLOW_BACKGROUND_EVENTS", false)) { + if (mHIDDeviceManager != null) { + mHIDDeviceManager.setFrozen(!hasFocus); + } + } + if (SDLActivity.mBrokenLibraries) { return; } @@ -1292,7 +1302,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh public static double getDiagonal() { DisplayMetrics metrics = new DisplayMetrics(); - Activity activity = (Activity)getContext(); + Activity activity = getContext(); if (activity == null) { return 0.0; } @@ -1481,11 +1491,11 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh if (SDLControllerManager.isDeviceSDLJoystick(deviceId)) { // Note that we process events with specific key codes here if (event.getAction() == KeyEvent.ACTION_DOWN) { - if (SDLControllerManager.onNativePadDown(deviceId, keyCode)) { + if (SDLControllerManager.onNativePadDown(deviceId, keyCode, event.getScanCode())) { return true; } } else if (event.getAction() == KeyEvent.ACTION_UP) { - if (SDLControllerManager.onNativePadUp(deviceId, keyCode)) { + if (SDLControllerManager.onNativePadUp(deviceId, keyCode, event.getScanCode())) { return true; } } @@ -1940,7 +1950,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh return; } - Activity activity = (Activity)getContext(); + Activity activity = getContext(); if (activity.checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) { activity.requestPermissions(new String[]{permission}, requestCode); } else { @@ -1963,7 +1973,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh Intent i = new Intent(Intent.ACTION_VIEW); i.setData(Uri.parse(url)); - int flags = Intent.FLAG_ACTIVITY_NO_HISTORY + int flags = Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NEW_DOCUMENT; i.addFlags(flags); diff --git a/third-party/SDL/android-project/app/src/main/java/org/libsdl/app/SDLControllerManager.java b/third-party/SDL/android-project/app/src/main/java/org/libsdl/app/SDLControllerManager.java index 7655ecfd6f..8681d050b7 100644 --- a/third-party/SDL/android-project/app/src/main/java/org/libsdl/app/SDLControllerManager.java +++ b/third-party/SDL/android-project/app/src/main/java/org/libsdl/app/SDLControllerManager.java @@ -10,6 +10,10 @@ import android.hardware.lights.Light; import android.hardware.lights.LightsRequest; import android.hardware.lights.LightsManager; import android.hardware.lights.LightState; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; import android.graphics.Color; import android.os.Build; import android.os.VibrationEffect; @@ -30,16 +34,18 @@ public class SDLControllerManager static native void nativeAddJoystick(int device_id, String name, String desc, int vendor_id, int product_id, int button_mask, - int naxes, int axis_mask, int nhats, boolean can_rumble, boolean has_rgb_led); + int naxes, int axis_mask, int nhats, boolean can_rumble, boolean has_rgb_led, + boolean has_accelerometer, boolean has_gyroscope); static native void nativeRemoveJoystick(int device_id); static native void nativeAddHaptic(int device_id, String name); static native void nativeRemoveHaptic(int device_id); - static public native boolean onNativePadDown(int device_id, int keycode); - static public native boolean onNativePadUp(int device_id, int keycode); + static public native boolean onNativePadDown(int device_id, int keycode, int scancode); + static public native boolean onNativePadUp(int device_id, int keycode, int scancode); static native void onNativeJoy(int device_id, int axis, float value); static native void onNativeHat(int device_id, int hat_id, int x, int y); + static native void onNativeJoySensor(int device_id, int sensor_type, long sensor_timestamp, float x, float y, float z); protected static SDLJoystickHandler mJoystickHandler; protected static SDLHapticHandler mHapticHandler; @@ -81,6 +87,13 @@ public class SDLControllerManager mJoystickHandler.setLED(device_id, red, green, blue); } + /** + * This method is called by SDL using JNI. + */ + static void joystickSetSensorsEnabled(int device_id, boolean enabled) { + mJoystickHandler.setSensorsEnabled(device_id, enabled); + } + /** * This method is called by SDL using JNI. */ @@ -153,6 +166,10 @@ class SDLJoystickHandler { ArrayList hats; ArrayList lights; LightsManager.LightsSession lightsSession; + SensorManager sensorManager; + SDLJoySensorListener sensorListener; + Sensor accelerometerSensor; + Sensor gyroscopeSensor; } static class RangeComparator implements Comparator { @Override @@ -225,12 +242,13 @@ class SDLJoystickHandler { joystick.desc = getJoystickDescriptor(joystickDevice); joystick.axes = new ArrayList(); joystick.hats = new ArrayList(); + java.util.Set axisStrsSet = new java.util.HashSet(); joystick.lights = new ArrayList(); List ranges = joystickDevice.getMotionRanges(); Collections.sort(ranges, new RangeComparator()); for (InputDevice.MotionRange range : ranges) { - if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) { + if (((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) && axisStrsSet.add(range.getAxis())) { if (range.getAxis() == MotionEvent.AXIS_HAT_X || range.getAxis() == MotionEvent.AXIS_HAT_Y) { joystick.hats.add(range); } else { @@ -241,6 +259,8 @@ class SDLJoystickHandler { boolean can_rumble = false; boolean has_rgb_led = false; + boolean has_accelerometer = false; + boolean has_gyroscope = false; if (Build.VERSION.SDK_INT >= 31 /* Android 12.0 (S) */) { VibratorManager vibratorManager = joystickDevice.getVibratorManager(); int[] vibrators = vibratorManager.getVibratorIds(); @@ -258,12 +278,26 @@ class SDLJoystickHandler { joystick.lightsSession = lightsManager.openSession(); has_rgb_led = true; } + SensorManager sensorManager = joystickDevice.getSensorManager(); + if (sensorManager != null) { + joystick.sensorManager = sensorManager; + joystick.sensorListener = new SDLJoySensorListener(joystick.device_id); + joystick.accelerometerSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); + if (joystick.accelerometerSensor != null) { + has_accelerometer = true; + } + joystick.gyroscopeSensor = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE); + if (joystick.gyroscopeSensor != null) { + has_gyroscope = true; + } + } } mJoysticks.add(joystick); SDLControllerManager.nativeAddJoystick(joystick.device_id, joystick.name, joystick.desc, getVendorId(joystickDevice), getProductId(joystickDevice), - getButtonMask(joystickDevice), joystick.axes.size(), getAxisMask(joystick.axes), joystick.hats.size()/2, can_rumble, has_rgb_led); + getButtonMask(joystickDevice), joystick.axes.size(), getAxisMask(joystick.axes), joystick.hats.size()/2, can_rumble, has_rgb_led, + has_accelerometer, has_gyroscope); } } } @@ -508,6 +542,31 @@ class SDLJoystickHandler { } joystick.lightsSession.requestLights(lightsRequest.build()); } + + void setSensorsEnabled(int device_id, boolean enabled) { + if (Build.VERSION.SDK_INT < 31 /* Android 12.0 (S) */) { + return; + } + SDLJoystick joystick = getJoystick(device_id); + if (joystick == null || joystick.sensorManager == null) { + return; + } + if (enabled) { + if (joystick.accelerometerSensor != null) { + SDLSensorManager.registerListener(joystick.sensorManager, joystick.sensorListener, joystick.accelerometerSensor, SensorManager.SENSOR_DELAY_GAME); + } + if (joystick.gyroscopeSensor != null) { + SDLSensorManager.registerListener(joystick.sensorManager, joystick.sensorListener, joystick.gyroscopeSensor, SensorManager.SENSOR_DELAY_GAME); + } + } else { + if (joystick.accelerometerSensor != null) { + SDLSensorManager.unregisterListener(joystick.sensorManager, joystick.sensorListener, joystick.accelerometerSensor); + } + if (joystick.gyroscopeSensor != null) { + SDLSensorManager.unregisterListener(joystick.sensorManager, joystick.sensorListener, joystick.gyroscopeSensor); + } + } + } } class SDLHapticHandler_API31 extends SDLHapticHandler { @@ -933,3 +992,19 @@ class SDLGenericMotionListener_API29 extends SDLGenericMotionListener_API26 { return penDevice.isExternal() ? SDL_PEN_DEVICE_TYPE_INDIRECT : SDL_PEN_DEVICE_TYPE_DIRECT; } } + +class SDLJoySensorListener implements SensorEventListener { + int device_id; + + public SDLJoySensorListener(int device_id) { + this.device_id = device_id; + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) {} + + @Override + public void onSensorChanged(SensorEvent event) { + SDLControllerManager.onNativeJoySensor(device_id, event.sensor.getType(), event.timestamp, event.values[0], event.values[1], event.values[2]); + } +} diff --git a/third-party/SDL/android-project/app/src/main/java/org/libsdl/app/SDLSensorManager.java b/third-party/SDL/android-project/app/src/main/java/org/libsdl/app/SDLSensorManager.java new file mode 100644 index 0000000000..586e3fab6e --- /dev/null +++ b/third-party/SDL/android-project/app/src/main/java/org/libsdl/app/SDLSensorManager.java @@ -0,0 +1,32 @@ +package org.libsdl.app; + +import android.hardware.Sensor; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; + +// This class coordinates synchronized access to sensor manager registration +// +// This prevents a java.util.ConcurrentModificationException exception on +// Android 16, specifically on the Samsung Tab S9 Ultra. + +class SDLSensorManager +{ + static private SDLSensorManager mManager = new SDLSensorManager(); + + public static void registerListener(SensorManager manager, SensorEventListener listener, Sensor sensor, int samplingPeriodUs) { + mManager.RegisterListener(manager, listener, sensor, samplingPeriodUs); + } + + public static void unregisterListener(SensorManager manager, SensorEventListener listener, Sensor sensor) { + mManager.UnregisterListener(manager, listener, sensor); + } + + private synchronized void RegisterListener(SensorManager manager, SensorEventListener listener, Sensor sensor, int samplingPeriodUs) { + manager.registerListener(listener, sensor, samplingPeriodUs, null); + } + + private synchronized void UnregisterListener(SensorManager manager, SensorEventListener listener, Sensor sensor) { + manager.unregisterListener(listener, sensor); + } +} + diff --git a/third-party/SDL/android-project/app/src/main/java/org/libsdl/app/SDLSurface.java b/third-party/SDL/android-project/app/src/main/java/org/libsdl/app/SDLSurface.java index 1579b73345..196cf04ec2 100644 --- a/third-party/SDL/android-project/app/src/main/java/org/libsdl/app/SDLSurface.java +++ b/third-party/SDL/android-project/app/src/main/java/org/libsdl/app/SDLSurface.java @@ -45,6 +45,9 @@ public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback, // Is SurfaceView ready for rendering protected boolean mIsSurfaceReady; + // Is on-screen keyboard visible + protected boolean mKeyboardVisible; + // Pinch events private final ScaleGestureDetector scaleGestureDetector; @@ -208,6 +211,18 @@ public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback, WindowInsets.Type.displayCutout()); SDLActivity.onNativeInsetsChanged(combined.left, combined.right, combined.top, combined.bottom); + + if (insets.isVisible(WindowInsets.Type.ime())) { + if (!mKeyboardVisible) { + mKeyboardVisible = true; + SDLActivity.onNativeScreenKeyboardShown(); + } + } else { + if (mKeyboardVisible) { + mKeyboardVisible = false; + SDLActivity.onNativeScreenKeyboardHidden(); + } + } } // Pass these to any child views in case they need them @@ -313,11 +328,11 @@ public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback, protected void enableSensor(int sensortype, boolean enabled) { // TODO: This uses getDefaultSensor - what if we have >1 accels? if (enabled) { - mSensorManager.registerListener(this, + SDLSensorManager.registerListener(mSensorManager, this, mSensorManager.getDefaultSensor(sensortype), - SensorManager.SENSOR_DELAY_GAME, null); + SensorManager.SENSOR_DELAY_GAME); } else { - mSensorManager.unregisterListener(this, + SDLSensorManager.unregisterListener(mSensorManager, this, mSensorManager.getDefaultSensor(sensortype)); } } diff --git a/third-party/SDL/android-project/gradlew b/third-party/SDL/android-project/gradlew old mode 100644 new mode 100755 diff --git a/third-party/SDL/build-scripts/SDL_migration.cocci b/third-party/SDL/build-scripts/SDL_migration.cocci index 6b844b3af4..abfc50551a 100644 --- a/third-party/SDL/build-scripts/SDL_migration.cocci +++ b/third-party/SDL/build-scripts/SDL_migration.cocci @@ -111,6 +111,42 @@ expression e1; - SDL_GetEventState(e1) + SDL_EventEnabled(e1) + +// SDL_JoystickEventState() - replaced with SDL_SetJoystickEventsEnabled() +@@ +@@ +( +- SDL_JoystickEventState(SDL_IGNORE) ++ SDL_SetJoystickEventsEnabled(false) +| +- SDL_JoystickEventState(SDL_DISABLE) ++ SDL_SetJoystickEventsEnabled(false) +| +- SDL_JoystickEventState(SDL_ENABLE) ++ SDL_SetJoystickEventsEnabled(true) +| +- SDL_JoystickEventState(SDL_QUERY) ++ SDL_JoystickEventsEnabled() +) + + +// SDL_GameControllerEventState() - replaced with SDL_SetGamepadEventsEnabled() +@@ +@@ +( +- SDL_GameControllerEventState(SDL_IGNORE) ++ SDL_SetGamepadEventsEnabled(false) +| +- SDL_GameControllerEventState(SDL_DISABLE) ++ SDL_SetGamepadEventsEnabled(false) +| +- SDL_GameControllerEventState(SDL_ENABLE) ++ SDL_SetGamepadEventsEnabled(true) +| +- SDL_GameControllerEventState(SDL_QUERY) ++ SDL_GamepadEventsEnabled() +) + @@ expression e; @@ @@ -3688,6 +3724,11 @@ typedef SDL_JoystickGUID, SDL_GUID; + SDL_GUID @@ @@ +- SDL_JoystickGetGUIDString ++ SDL_GUIDToString + (...) +@@ +@@ - SDL_GUIDFromString + SDL_StringToGUID (...) diff --git a/third-party/SDL/build-scripts/add-source-to-projects.pl b/third-party/SDL/build-scripts/add-source-to-projects.pl old mode 100644 new mode 100755 diff --git a/third-party/SDL/build-scripts/androidbuildlibs.sh b/third-party/SDL/build-scripts/androidbuildlibs.sh old mode 100644 new mode 100755 diff --git a/third-party/SDL/build-scripts/build-release.py b/third-party/SDL/build-scripts/build-release.py old mode 100644 new mode 100755 diff --git a/third-party/SDL/build-scripts/build-web-examples.pl b/third-party/SDL/build-scripts/build-web-examples.pl old mode 100644 new mode 100755 diff --git a/third-party/SDL/build-scripts/check_android_jni.py b/third-party/SDL/build-scripts/check_android_jni.py old mode 100644 new mode 100755 diff --git a/third-party/SDL/build-scripts/check_elf_alignment.sh b/third-party/SDL/build-scripts/check_elf_alignment.sh old mode 100644 new mode 100755 diff --git a/third-party/SDL/build-scripts/check_stdlib_usage.py b/third-party/SDL/build-scripts/check_stdlib_usage.py old mode 100644 new mode 100755 diff --git a/third-party/SDL/build-scripts/clang-format-src.sh b/third-party/SDL/build-scripts/clang-format-src.sh old mode 100644 new mode 100755 diff --git a/third-party/SDL/build-scripts/codechecker-buildbot.sh b/third-party/SDL/build-scripts/codechecker-buildbot.sh old mode 100644 new mode 100755 diff --git a/third-party/SDL/build-scripts/create-android-project.py b/third-party/SDL/build-scripts/create-android-project.py old mode 100644 new mode 100755 diff --git a/third-party/SDL/build-scripts/create-release.py b/third-party/SDL/build-scripts/create-release.py old mode 100644 new mode 100755 diff --git a/third-party/SDL/build-scripts/create_tbds.py b/third-party/SDL/build-scripts/create_tbds.py old mode 100644 new mode 100755 diff --git a/third-party/SDL/build-scripts/fnsince.pl b/third-party/SDL/build-scripts/fnsince.pl old mode 100644 new mode 100755 diff --git a/third-party/SDL/build-scripts/git-pre-push-hook.pl b/third-party/SDL/build-scripts/git-pre-push-hook.pl old mode 100644 new mode 100755 diff --git a/third-party/SDL/build-scripts/makecasefoldhashtable.pl b/third-party/SDL/build-scripts/makecasefoldhashtable.pl old mode 100644 new mode 100755 diff --git a/third-party/SDL/build-scripts/pkg-support/android/aar/__main__.py.in b/third-party/SDL/build-scripts/pkg-support/android/aar/__main__.py.in old mode 100644 new mode 100755 diff --git a/third-party/SDL/build-scripts/rename_api.py b/third-party/SDL/build-scripts/rename_api.py old mode 100644 new mode 100755 diff --git a/third-party/SDL/build-scripts/rename_headers.py b/third-party/SDL/build-scripts/rename_headers.py old mode 100644 new mode 100755 diff --git a/third-party/SDL/build-scripts/rename_macros.py b/third-party/SDL/build-scripts/rename_macros.py old mode 100644 new mode 100755 diff --git a/third-party/SDL/build-scripts/rename_symbols.py b/third-party/SDL/build-scripts/rename_symbols.py old mode 100644 new mode 100755 diff --git a/third-party/SDL/build-scripts/rename_types.py b/third-party/SDL/build-scripts/rename_types.py old mode 100644 new mode 100755 diff --git a/third-party/SDL/build-scripts/setup-gdk-desktop.py b/third-party/SDL/build-scripts/setup-gdk-desktop.py old mode 100644 new mode 100755 diff --git a/third-party/SDL/build-scripts/showrev.sh b/third-party/SDL/build-scripts/showrev.sh old mode 100644 new mode 100755 diff --git a/third-party/SDL/build-scripts/test-versioning.sh b/third-party/SDL/build-scripts/test-versioning.sh old mode 100644 new mode 100755 diff --git a/third-party/SDL/build-scripts/update-copyright.sh b/third-party/SDL/build-scripts/update-copyright.sh old mode 100644 new mode 100755 diff --git a/third-party/SDL/build-scripts/update-version.sh b/third-party/SDL/build-scripts/update-version.sh old mode 100644 new mode 100755 diff --git a/third-party/SDL/build-scripts/updaterev.sh b/third-party/SDL/build-scripts/updaterev.sh old mode 100644 new mode 100755 diff --git a/third-party/SDL/build-scripts/wikiheaders.pl b/third-party/SDL/build-scripts/wikiheaders.pl old mode 100644 new mode 100755 index 2092912bc9..d2a1fab4cb --- a/third-party/SDL/build-scripts/wikiheaders.pl +++ b/third-party/SDL/build-scripts/wikiheaders.pl @@ -2912,6 +2912,11 @@ __EOF__ $brief = shift @briefsplit; $brief = dewikify($wikitype, $brief); + # Hack: `apropros` doesn't like escaped character things like `\[char46]` for `.`...since almost every + # manpage will end their Brief section with a period and it won't wordwrap to risk being a groff control + # character, just replace it. + $brief =~ s/\\\[char46\]/./g; + if (defined $remarks) { $remarks = dewikify($wikitype, join("\n", @briefsplit) . $remarks); } diff --git a/third-party/SDL/cmake/PreseedEmscriptenCache.cmake b/third-party/SDL/cmake/PreseedEmscriptenCache.cmake index 78504a5d5f..ff6a2e20f6 100644 --- a/third-party/SDL/cmake/PreseedEmscriptenCache.cmake +++ b/third-party/SDL/cmake/PreseedEmscriptenCache.cmake @@ -132,7 +132,6 @@ if(EMSCRIPTEN) set(LIBC_HAS__LTOA "" CACHE INTERNAL "Have symbol _ltoa") set(LIBC_HAS__STRREV "" CACHE INTERNAL "Have symbol _strrev") set(LIBC_HAS__UI64TOA "" CACHE INTERNAL "Have symbol _ui64toa") - set(LIBC_HAS__UITOA "" CACHE INTERNAL "Have symbol _uitoa") set(LIBC_HAS__ULTOA "" CACHE INTERNAL "Have symbol _ultoa") set(LIBC_HAS__WCSDUP "" CACHE INTERNAL "Have symbol _wcsdup") set(LIBC_IS_GLIBC "" CACHE INTERNAL "Have symbol __GLIBC__") diff --git a/third-party/SDL/cmake/PreseedMSVCCache.cmake b/third-party/SDL/cmake/PreseedMSVCCache.cmake index 81f7d01ed4..f93d2b8ff6 100644 --- a/third-party/SDL/cmake/PreseedMSVCCache.cmake +++ b/third-party/SDL/cmake/PreseedMSVCCache.cmake @@ -154,7 +154,6 @@ if(MSVC) set(LIBC_HAS__LTOA "1" CACHE INTERNAL "Have symbol _ltoa") set(LIBC_HAS__STRREV "1" CACHE INTERNAL "Have symbol _strrev") set(LIBC_HAS__UI64TOA "1" CACHE INTERNAL "Have symbol _ui64toa") - set(LIBC_HAS__UITOA "" CACHE INTERNAL "Have symbol _uitoa") set(LIBC_HAS__ULTOA "1" CACHE INTERNAL "Have symbol _ultoa") set(LIBC_HAS__WCSDUP "1" CACHE INTERNAL "Have symbol _wcsdup") set(LIBC_IS_GLIBC "" CACHE INTERNAL "Have symbol __GLIBC__") diff --git a/third-party/SDL/cmake/PreseedNokiaNGageCache.cmake b/third-party/SDL/cmake/PreseedNokiaNGageCache.cmake index 9873727af8..e404785319 100644 --- a/third-party/SDL/cmake/PreseedNokiaNGageCache.cmake +++ b/third-party/SDL/cmake/PreseedNokiaNGageCache.cmake @@ -135,7 +135,6 @@ if(NGAGESDK) set(LIBC_HAS__LTOA "" CACHE INTERNAL "Have symbol _ltoa") set(LIBC_HAS__STRREV "" CACHE INTERNAL "Have symbol _strrev") set(LIBC_HAS__UI64TOA "" CACHE INTERNAL "Have symbol _ui64toa") - set(LIBC_HAS__UITOA "" CACHE INTERNAL "Have symbol _uitoa") set(LIBC_HAS__ULTOA "" CACHE INTERNAL "Have symbol _ultoa") set(LIBC_HAS__WCSDUP "" CACHE INTERNAL "Have symbol _wcsdup") set(LIBC_IS_GLIBC "" CACHE INTERNAL "Have symbol __GLIBC__") diff --git a/third-party/SDL/cmake/sdlcompilers.cmake b/third-party/SDL/cmake/sdlcompilers.cmake index ab62c5012e..cd4dde0f6f 100644 --- a/third-party/SDL/cmake/sdlcompilers.cmake +++ b/third-party/SDL/cmake/sdlcompilers.cmake @@ -11,7 +11,7 @@ macro(SDL_DetectCompiler) if(MSVC) set(MSVC_CLANG TRUE) endif() - elseif(CMAKE_COMPILER_IS_GNUCC) + elseif(CMAKE_C_COMPILER_ID STREQUAL "GNU") set(USE_GCC TRUE) elseif(CMAKE_C_COMPILER_ID MATCHES "^Intel$") set(USE_INTELCC TRUE) diff --git a/third-party/SDL/cmake/sdlcpu.cmake b/third-party/SDL/cmake/sdlcpu.cmake index a27e7329c4..fa7d4aa820 100644 --- a/third-party/SDL/cmake/sdlcpu.cmake +++ b/third-party/SDL/cmake/sdlcpu.cmake @@ -1,6 +1,6 @@ function(SDL_DetectTargetCPUArchitectures DETECTED_ARCHS) - set(known_archs EMSCRIPTEN ARM32 ARM64 ARM64EC LOONGARCH64 POWERPC32 POWERPC64 RISCV32 RISCV64 X86 X64) + set(known_archs EMSCRIPTEN ARM32 ARM64 ARM64EC LOONGARCH64 MIPS32 MIPS64 POWERPC32 POWERPC64 RISCV32 RISCV64 X86 X64) if(APPLE AND CMAKE_OSX_ARCHITECTURES) foreach(known_arch IN LISTS known_archs) @@ -37,6 +37,8 @@ function(SDL_DetectTargetCPUArchitectures DETECTED_ARCHS) set(arch_check_ARM64EC "defined(_M_ARM64EC)") set(arch_check_EMSCRIPTEN "defined(__EMSCRIPTEN__)") set(arch_check_LOONGARCH64 "defined(__loongarch64)") + set(arch_check_MIPS32 "(defined(__mips__) && !defined(__mips64))") + set(arch_check_MIPS64 "(defined(__mips__) && defined(__mips64))") set(arch_check_POWERPC32 "(defined(__PPC__) || defined(__powerpc__)) && !defined(__powerpc64__)") set(arch_check_POWERPC64 "defined(__PPC64__) || defined(__powerpc64__)") set(arch_check_RISCV32 "defined(__riscv) && defined(__riscv_xlen) && __riscv_xlen == 32") diff --git a/third-party/SDL/cmake/test/test_pkgconfig.sh b/third-party/SDL/cmake/test/test_pkgconfig.sh old mode 100644 new mode 100755 diff --git a/third-party/SDL/cmake/xxd.py b/third-party/SDL/cmake/xxd.py old mode 100644 new mode 100755 diff --git a/third-party/SDL/docs/README-linux.md b/third-party/SDL/docs/README-linux.md index edd7019ed9..ced2ebbd58 100644 --- a/third-party/SDL/docs/README-linux.md +++ b/third-party/SDL/docs/README-linux.md @@ -20,7 +20,7 @@ Ubuntu 18.04, all available features enabled: libaudio-dev libfribidi-dev libjack-dev libsndio-dev libx11-dev libxext-dev \ libxrandr-dev libxcursor-dev libxfixes-dev libxi-dev libxss-dev libxtst-dev \ libxkbcommon-dev libdrm-dev libgbm-dev libgl1-mesa-dev libgles2-mesa-dev \ - libegl1-mesa-dev libdbus-1-dev libibus-1.0-dev libudev-dev libthai-dev + libegl1-mesa-dev libdbus-1-dev libibus-1.0-dev libudev-dev libthai-dev libusb-1.0-0-dev Ubuntu 22.04+ can also add `libpipewire-0.3-dev libwayland-dev libdecor-0-dev liburing-dev` to that command line. diff --git a/third-party/SDL/docs/README-migration.md b/third-party/SDL/docs/README-migration.md index 1a8fbaa6d6..6fe62dce21 100644 --- a/third-party/SDL/docs/README-migration.md +++ b/third-party/SDL/docs/README-migration.md @@ -1447,7 +1447,7 @@ The following functions have been removed: * SDL_GetTextureUserData() - use SDL_GetTextureProperties() instead * SDL_RenderGetIntegerScale() * SDL_RenderSetIntegerScale() - this is now explicit with SDL_LOGICAL_PRESENTATION_INTEGER_SCALE -* SDL_RenderTargetSupported() - render targets are always supported +* SDL_RenderTargetSupported() - render targets are usually supported; just create a texture with SDL_TEXTUREACCESS_TARGET and see if it fails. * SDL_SetTextureUserData() - use SDL_GetTextureProperties() instead The following enums have been renamed: @@ -1461,7 +1461,7 @@ The following symbols have been removed: * SDL_RENDERER_ACCELERATED - all renderers except `SDL_SOFTWARE_RENDERER` are accelerated * SDL_RENDERER_PRESENTVSYNC - replaced with SDL_PROP_RENDERER_CREATE_PRESENT_VSYNC_NUMBER during renderer creation and SDL_PROP_RENDERER_VSYNC_NUMBER after renderer creation * SDL_RENDERER_SOFTWARE - you can check whether the name of the renderer is `SDL_SOFTWARE_RENDERER` -* SDL_RENDERER_TARGETTEXTURE - all renderers support target texture functionality +* SDL_RENDERER_TARGETTEXTURE - most renderers support target texture functionality; just create a texture with SDL_TEXTUREACCESS_TARGET and see if it fails. * SDL_ScaleModeBest - use SDL_SCALEMODE_LINEAR instead ## SDL_rwops.h diff --git a/third-party/SDL/examples/camera/01-read-and-draw/read-and-draw.c b/third-party/SDL/examples/camera/01-read-and-draw/read-and-draw.c index 7682da95de..b3649f4b58 100644 --- a/third-party/SDL/examples/camera/01-read-and-draw/read-and-draw.c +++ b/third-party/SDL/examples/camera/01-read-and-draw/read-and-draw.c @@ -44,6 +44,7 @@ SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[]) return SDL_APP_FAILURE; } else if (devcount == 0) { SDL_Log("Couldn't find any camera devices! Please connect a camera and try again."); + SDL_free(devices); return SDL_APP_FAILURE; } diff --git a/third-party/SDL/examples/demo/02-woodeneye-008/woodeneye-008.c b/third-party/SDL/examples/demo/02-woodeneye-008/woodeneye-008.c index 97a72015e2..18431e7859 100644 --- a/third-party/SDL/examples/demo/02-woodeneye-008/woodeneye-008.c +++ b/third-party/SDL/examples/demo/02-woodeneye-008/woodeneye-008.c @@ -206,7 +206,7 @@ static void draw(SDL_Renderer *renderer, const float (*edges)[6], const Player p for (i = 0; i < players_len; i++) { const Player *player = &players[i]; float mod_x = (float)(i % part_hor); - float mod_y = (float)(i / part_hor); + float mod_y = (float)i / part_hor; float hor_origin = (mod_x + 0.5f) * size_hor; float ver_origin = (mod_y + 0.5f) * size_ver; float cam_origin = (float)(0.5 * SDL_sqrt(size_hor * size_hor + size_ver * size_ver)); @@ -477,4 +477,4 @@ SDL_AppResult SDL_AppIterate(void *appstate) void SDL_AppQuit(void *appstate, SDL_AppResult result) { SDL_free(appstate); // just free the memory, SDL will clean up the window/renderer for us. -} \ No newline at end of file +} diff --git a/third-party/SDL/examples/demo/04-bytepusher/bytepusher.c b/third-party/SDL/examples/demo/04-bytepusher/bytepusher.c index e5c4e5c382..25525735bf 100644 --- a/third-party/SDL/examples/demo/04-bytepusher/bytepusher.c +++ b/third-party/SDL/examples/demo/04-bytepusher/bytepusher.c @@ -33,7 +33,6 @@ typedef struct { SDL_Renderer* renderer; SDL_Palette* palette; SDL_Texture* texture; - SDL_Texture* rendertarget; /* we need this render target for text to look good */ SDL_AudioStream* audiostream; char status[SCREEN_W / 8]; int status_ticks; @@ -75,6 +74,8 @@ static bool load(BytePusher* vm, SDL_IOStream* stream, bool closeio) { size_t bytes_read = 0; bool ok = true; + vm->display_help = true; // will set to false if load succeeds. + SDL_memset(vm->ram, 0, RAM_SIZE); if (!stream) { @@ -199,13 +200,11 @@ SDL_AppResult SDL_AppInit(void** appstate, int argc, char* argv[]) { } vm->texture = SDL_CreateTexture(vm->renderer, SDL_PIXELFORMAT_INDEX8, SDL_TEXTUREACCESS_STREAMING, SCREEN_W, SCREEN_H); - vm->rendertarget = SDL_CreateTexture(vm->renderer, SDL_PIXELFORMAT_UNKNOWN, SDL_TEXTUREACCESS_TARGET, SCREEN_W, SCREEN_H); - if (!vm->texture || !vm->rendertarget) { + if (!vm->texture) { return SDL_APP_FAILURE; } SDL_SetTexturePalette(vm->texture, vm->palette); SDL_SetTextureScaleMode(vm->texture, SDL_SCALEMODE_NEAREST); - SDL_SetTextureScaleMode(vm->rendertarget, SDL_SCALEMODE_NEAREST); if (!(vm->audiostream = SDL_OpenAudioDeviceStream( SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &audiospec, NULL, NULL @@ -274,26 +273,26 @@ SDL_AppResult SDL_AppIterate(void* appstate) { if (updated) { const void *pixels = &vm->ram[(Uint32)vm->ram[IO_SCREEN_PAGE] << 16]; SDL_UpdateTexture(vm->texture, NULL, pixels, SCREEN_W); - - SDL_SetRenderTarget(vm->renderer, vm->rendertarget); - SDL_RenderTexture(vm->renderer, vm->texture, 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); + + 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."); + } else { + SDL_RenderTexture(vm->renderer, vm->texture, NULL, NULL); + } + + if (vm->status_ticks > 0) { + if (updated) { + vm->status_ticks--; + } + print(vm, 4, SCREEN_H - 12, vm->status); + } + SDL_RenderPresent(vm->renderer); return SDL_APP_CONTINUE; @@ -387,7 +386,6 @@ void SDL_AppQuit(void* appstate, SDL_AppResult result) { if (appstate) { BytePusher* vm = (BytePusher*)appstate; SDL_DestroyAudioStream(vm->audiostream); - SDL_DestroyTexture(vm->rendertarget); SDL_DestroyTexture(vm->texture); SDL_DestroyPalette(vm->palette); SDL_DestroyRenderer(vm->renderer); diff --git a/third-party/SDL/examples/renderer/05-rectangles/rectangles.c b/third-party/SDL/examples/renderer/05-rectangles/rectangles.c index e326a40e7e..ad00b60790 100644 --- a/third-party/SDL/examples/renderer/05-rectangles/rectangles.c +++ b/third-party/SDL/examples/renderer/05-rectangles/rectangles.c @@ -90,7 +90,7 @@ SDL_AppResult SDL_AppIterate(void *appstate) /* ...and also fill a bunch of rectangles at once... */ for (i = 0; i < SDL_arraysize(rects); i++) { - const float w = (float) (WINDOW_WIDTH / SDL_arraysize(rects)); + const float w = ((float) WINDOW_WIDTH / SDL_arraysize(rects)); const float h = i * 8.0f; rects[i].x = i * w; rects[i].y = WINDOW_HEIGHT - h; diff --git a/third-party/SDL/examples/renderer/18-debug-text/debug-text.c b/third-party/SDL/examples/renderer/18-debug-text/debug-text.c index f5150d210b..7207698342 100644 --- a/third-party/SDL/examples/renderer/18-debug-text/debug-text.c +++ b/third-party/SDL/examples/renderer/18-debug-text/debug-text.c @@ -66,7 +66,7 @@ SDL_AppResult SDL_AppIterate(void *appstate) 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_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! */ diff --git a/third-party/SDL/include/SDL3/SDL.h b/third-party/SDL/include/SDL3/SDL.h index 489c5ad61b..8fbafd5588 100644 --- a/third-party/SDL/include/SDL3/SDL.h +++ b/third-party/SDL/include/SDL3/SDL.h @@ -20,7 +20,7 @@ */ /** - * Main include header for the SDL library, version 3.4.4 + * Main include header for the SDL library, version 3.4.10 * * It is almost always best to include just this one header instead of * picking out individual headers included here. There are exceptions to diff --git a/third-party/SDL/include/SDL3/SDL_assert.h b/third-party/SDL/include/SDL3/SDL_assert.h index fb6746235b..6c8a47d004 100644 --- a/third-party/SDL/include/SDL3/SDL_assert.h +++ b/third-party/SDL/include/SDL3/SDL_assert.h @@ -265,7 +265,7 @@ disable assertions. */ #define SDL_NULL_WHILE_LOOP_CONDITION (0) -#elif defined(_MSC_VER) /* Avoid /W4 warnings. */ +#elif defined(_MSC_VER) && !defined(__clang__) /* 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! */ #define SDL_NULL_WHILE_LOOP_CONDITION (0,0) diff --git a/third-party/SDL/include/SDL3/SDL_atomic.h b/third-party/SDL/include/SDL3/SDL_atomic.h index 1ec0753da3..f1a8e0638d 100644 --- a/third-party/SDL/include/SDL3/SDL_atomic.h +++ b/third-party/SDL/include/SDL3/SDL_atomic.h @@ -156,6 +156,8 @@ extern SDL_DECLSPEC void SDLCALL SDL_UnlockSpinlock(SDL_SpinLock *lock); */ #define SDL_CompilerBarrier() DoCompilerSpecificReadWriteBarrier() +#elif SDL_HAS_BUILTIN(__atomic_signal_fence) || (defined(__GNUC__) && (__GNUC__ >= 5)) +#define SDL_CompilerBarrier() __atomic_signal_fence(__ATOMIC_SEQ_CST) #elif defined(_MSC_VER) && (_MSC_VER > 1200) && !defined(__clang__) void _ReadWriteBarrier(void); #pragma intrinsic(_ReadWriteBarrier) @@ -167,8 +169,9 @@ void _ReadWriteBarrier(void); extern __inline void SDL_CompilerBarrier(void); #pragma aux SDL_CompilerBarrier = "" parm [] modify exact []; #else +/* We don't unlock here to avoid possible infinite recursion */ #define SDL_CompilerBarrier() \ -{ SDL_SpinLock _tmp = 0; SDL_LockSpinlock(&_tmp); SDL_UnlockSpinlock(&_tmp); } +{ SDL_SpinLock _tmp = 0; SDL_LockSpinlock(&_tmp); } #endif /** @@ -275,12 +278,19 @@ extern SDL_DECLSPEC void SDLCALL SDL_MemoryBarrierAcquireFunction(void); */ #define SDL_MemoryBarrierAcquire() SDL_MemoryBarrierAcquireFunction() +#elif SDL_HAS_BUILTIN(__atomic_thread_fence) || (defined(__GNUC__) && (__GNUC__ >= 5)) +#define SDL_MemoryBarrierRelease() __atomic_thread_fence(__ATOMIC_RELEASE) +#define SDL_MemoryBarrierAcquire() __atomic_thread_fence(__ATOMIC_ACQUIRE) #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__) #define SDL_MemoryBarrierRelease() __asm__ __volatile__ ("dmb ish" : : : "memory") -#define SDL_MemoryBarrierAcquire() __asm__ __volatile__ ("dmb ish" : : : "memory") +#define SDL_MemoryBarrierAcquire() __asm__ __volatile__ ("dmb ishld" : : : "memory") +#elif defined(_MSC_VER) && (defined(_M_ARM64) || defined(_M_ARM64EC)) +#include +#define SDL_MemoryBarrierRelease() __dmb(_ARM64_BARRIER_ISH) +#define SDL_MemoryBarrierAcquire() __dmb(_ARM64_BARRIER_ISHLD) #elif defined(__GNUC__) && defined(__arm__) #if 0 /* defined(SDL_PLATFORM_LINUX) || defined(SDL_PLATFORM_ANDROID) */ /* Information from: diff --git a/third-party/SDL/include/SDL3/SDL_audio.h b/third-party/SDL/include/SDL3/SDL_audio.h index c72fe04cc3..46a73b1c90 100644 --- a/third-party/SDL/include/SDL3/SDL_audio.h +++ b/third-party/SDL/include/SDL3/SDL_audio.h @@ -434,7 +434,8 @@ typedef struct SDL_AudioSpec * - It can handle incoming data in any variable size. * - It can handle input/output format changes on the fly. * - It can remap audio channels between inputs and outputs. - * - You push data as you have it, and pull it when you need it + * - You push data as you have it, and pull it when you need it; the + * stream will buffer data as needed. * - It can also function as a basic audio data queue even if you just have * sound that needs to pass from one place to another. * - You can hook callbacks up to them when more data is added or requested, @@ -1054,8 +1055,15 @@ extern SDL_DECLSPEC SDL_AudioDeviceID SDLCALL SDL_GetAudioStreamDevice(SDL_Audio /** * Create a new audio stream. * - * \param src_spec the format details of the input audio. - * \param dst_spec the format details of the output audio. + * Note that `src_spec` or `dst_spec` may be NULL, but any attempts to + * put or get data from an audio stream will fail until it has valid + * specs assigned to both ends of the stream. Specs can be assigned later + * through SDL_SetAudioStreamFormat(), or binding the stream to an audio + * device (which will set the format of only the input or output, + * depending on what kind of device the stream was bound to). + * + * \param src_spec the format details of the input audio. May be NULL. + * \param dst_spec the format details of the output audio. May be NULL. * \returns a new audio stream on success or NULL on failure; call * SDL_GetError() for more information. * @@ -1137,6 +1145,9 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetAudioStreamFormat(SDL_AudioStream *strea * be ignored, but this will not report an error. The other side's format can * be changed. * + * `src_spec` and `dst_spec` may each be NULL; a NULL spec signals not to + * change the current format for that side of the stream. + * * \param stream the stream the format is being changed. * \param src_spec the new format of the audio input; if NULL, it is not * changed. diff --git a/third-party/SDL/include/SDL3/SDL_clipboard.h b/third-party/SDL/include/SDL3/SDL_clipboard.h index 696825376a..487f9746cb 100644 --- a/third-party/SDL/include/SDL3/SDL_clipboard.h +++ b/third-party/SDL/include/SDL3/SDL_clipboard.h @@ -252,7 +252,7 @@ typedef void (SDLCALL *SDL_ClipboardCleanupCallback)(void *userdata); * \sa SDL_GetClipboardData * \sa SDL_HasClipboardData */ -extern SDL_DECLSPEC bool SDLCALL SDL_SetClipboardData(SDL_ClipboardDataCallback callback, SDL_ClipboardCleanupCallback cleanup, void *userdata, const char **mime_types, size_t num_mime_types); +extern SDL_DECLSPEC bool SDLCALL SDL_SetClipboardData(SDL_ClipboardDataCallback callback, SDL_ClipboardCleanupCallback cleanup, void *userdata, const char *const *mime_types, size_t num_mime_types); /** * Clear the clipboard data. diff --git a/third-party/SDL/include/SDL3/SDL_events.h b/third-party/SDL/include/SDL3/SDL_events.h index 398c677095..312623e67c 100644 --- a/third-party/SDL/include/SDL3/SDL_events.h +++ b/third-party/SDL/include/SDL3/SDL_events.h @@ -456,7 +456,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 in relative mode, SDL_TOUCH_MOUSEID for touch events, or 0 */ + SDL_MouseID which; /**< The mouse instance id in relative mode, SDL_TOUCH_MOUSEID for touch events, SDL_PEN_MOUSEID for pen events, or 0 */ SDL_MouseButtonFlags state; /**< The current button state */ float x; /**< X coordinate, relative to window */ float y; /**< Y coordinate, relative to window */ diff --git a/third-party/SDL/include/SDL3/SDL_gamepad.h b/third-party/SDL/include/SDL3/SDL_gamepad.h index 113452535b..9c88a770ae 100644 --- a/third-party/SDL/include/SDL3/SDL_gamepad.h +++ b/third-party/SDL/include/SDL3/SDL_gamepad.h @@ -167,11 +167,11 @@ typedef enum SDL_GamepadButton SDL_GAMEPAD_BUTTON_DPAD_DOWN, SDL_GAMEPAD_BUTTON_DPAD_LEFT, SDL_GAMEPAD_BUTTON_DPAD_RIGHT, - SDL_GAMEPAD_BUTTON_MISC1, /**< Additional button (e.g. Xbox Series X share button, PS5 microphone button, Nintendo Switch Pro capture button, Amazon Luna microphone button, Google Stadia capture button) */ - SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1, /**< Upper or primary paddle, under your right hand (e.g. Xbox Elite paddle P1, DualSense Edge RB button, Right Joy-Con SR button) */ - SDL_GAMEPAD_BUTTON_LEFT_PADDLE1, /**< Upper or primary paddle, under your left hand (e.g. Xbox Elite paddle P3, DualSense Edge LB button, Left Joy-Con SL button) */ - SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2, /**< Lower or secondary paddle, under your right hand (e.g. Xbox Elite paddle P2, DualSense Edge right Fn button, Right Joy-Con SL button) */ - SDL_GAMEPAD_BUTTON_LEFT_PADDLE2, /**< Lower or secondary paddle, under your left hand (e.g. Xbox Elite paddle P4, DualSense Edge left Fn button, Left Joy-Con SR button) */ + SDL_GAMEPAD_BUTTON_MISC1, /**< Additional button (e.g. Xbox Series X share button, PS5 microphone button, Nintendo Switch Pro capture button, Steam Controller QAM button, Amazon Luna microphone button, Google Stadia capture button) */ + SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1, /**< Upper or primary paddle, under your right hand (e.g. Xbox Elite paddle P1, DualSense Edge RB button, Right Joy-Con SR button, Steam Controller R4 button) */ + SDL_GAMEPAD_BUTTON_LEFT_PADDLE1, /**< Upper or primary paddle, under your left hand (e.g. Xbox Elite paddle P3, DualSense Edge LB button, Left Joy-Con SL button, Steam Controller L4 button) */ + SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2, /**< Lower or secondary paddle, under your right hand (e.g. Xbox Elite paddle P2, DualSense Edge right Fn button, Right Joy-Con SL button, Steam Controller R5 button) */ + SDL_GAMEPAD_BUTTON_LEFT_PADDLE2, /**< Lower or secondary paddle, under your left hand (e.g. Xbox Elite paddle P4, DualSense Edge left Fn button, Left Joy-Con SR button, Steam Controller L5 button) */ SDL_GAMEPAD_BUTTON_TOUCHPAD, /**< PS4/PS5 touchpad button */ SDL_GAMEPAD_BUTTON_MISC2, /**< Additional button */ SDL_GAMEPAD_BUTTON_MISC3, /**< Additional button (e.g. Nintendo GameCube left trigger click) */ diff --git a/third-party/SDL/include/SDL3/SDL_hints.h b/third-party/SDL/include/SDL3/SDL_hints.h index 74f5bca07e..ba2ff351e0 100644 --- a/third-party/SDL/include/SDL3/SDL_hints.h +++ b/third-party/SDL/include/SDL3/SDL_hints.h @@ -2772,7 +2772,7 @@ extern "C" { * (default) * - "1": Cursors will automatically match the display content scale (e.g. a * 2x sized cursor will be used when the window is on a monitor with 200% - * scale). This is currently implemented on Windows and Wayland. + * scale). This is currently implemented on Windows. * * This hint needs to be set before creating cursors. * @@ -4044,6 +4044,31 @@ extern "C" { */ #define SDL_HINT_VIDEO_WIN_D3DCOMPILER "SDL_VIDEO_WIN_D3DCOMPILER" +/** + * A variable controlling whether the X Synchronization Extension is enabled. + * + * If set, this can result in smoother window resizing when rendering using + * OpenGL, however, there are some conditions: + * + * - It is only activated on windows created with the `SDL_WINDOW_OPENGL` flag + * (windows using an SDL OpenGL renderer have this automatically set). + * - When activated, presentation must be done with `SDL_GL_SwapWindow()` + * (`SDL_RenderPresent()` calls this internally for OpenGL renderers as well). + * + * Enabling this and presenting via an external mechanism will result in sync + * requests not being acked, and hangs and other odd window behavior may result. + * + * The variable can be set to the following values: + * + * - "0": The X Synchronization Extension is disabled. (default) + * - "1": The X Synchronization Extension is enabled. + * + * This hint should be set before creating a window. + * + * \since This hint is available since SDL 3.4.10. + */ +#define SDL_HINT_VIDEO_X11_ENABLE_XSYNC_EXT "SDL_VIDEO_X11_ENABLE_XSYNC_EXT" + /** * A variable controlling whether SDL should call XSelectInput() to enable * input events on X11 windows wrapped by SDL windows. diff --git a/third-party/SDL/include/SDL3/SDL_opengl_glext.h b/third-party/SDL/include/SDL3/SDL_opengl_glext.h index fa0f6c2a5b..ff6ad12cef 100644 --- a/third-party/SDL/include/SDL3/SDL_opengl_glext.h +++ b/third-party/SDL/include/SDL3/SDL_opengl_glext.h @@ -9807,15 +9807,15 @@ typedef void (APIENTRYP PFNGLUPLOADGPUMASKNVXPROC) (GLbitfield mask); typedef void (APIENTRYP PFNGLMULTICASTVIEWPORTARRAYVNVXPROC) (GLuint gpu, GLuint first, GLsizei count, const GLfloat *v); typedef void (APIENTRYP PFNGLMULTICASTVIEWPORTPOSITIONWSCALENVXPROC) (GLuint gpu, GLuint index, GLfloat xcoeff, GLfloat ycoeff); typedef void (APIENTRYP PFNGLMULTICASTSCISSORARRAYVNVXPROC) (GLuint gpu, GLuint first, GLsizei count, const GLint *v); -typedef GLuint (APIENTRYP PFNGLASYNCCOPYBUFFERSUBDATANVXPROC) (GLsizei waitSemaphoreCount, const GLuint *waitSemaphoreArray, const GLuint64 *fenceValueArray, GLuint readGPU, GLbitfield writeGPUMask, GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size, GLsizei signalSemaphoreCount, const GLuint *signalSemaphoreArray, const GLuint64 *signalValueArray); -typedef GLuint (APIENTRYP PFNGLASYNCCOPYIMAGESUBDATANVXPROC) (GLsizei waitSemaphoreCount, const GLuint *waitSemaphoreArray, const GLuint64 *waitValueArray, GLuint srcGPU, GLbitfield dstGPUMask, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth, GLsizei signalSemaphoreCount, const GLuint *signalSemaphoreArray, const GLuint64 *signalValueArray); +typedef GLuint (APIENTRYP PFNGLASYNCCOPYBUFFERSUBDATANVXPROC) (GLsizei waitSemaphoreCount, const GLuint *waitSemaphoreArray, const GLuint64 *fenceValueArray, GLuint readGpu, GLbitfield writeGpuMask, GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size, GLsizei signalSemaphoreCount, const GLuint *signalSemaphoreArray, const GLuint64 *signalValueArray); +typedef GLuint (APIENTRYP PFNGLASYNCCOPYIMAGESUBDATANVXPROC) (GLsizei waitSemaphoreCount, const GLuint *waitSemaphoreArray, const GLuint64 *waitValueArray, GLuint srcGpu, GLbitfield dstGpuMask, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth, GLsizei signalSemaphoreCount, const GLuint *signalSemaphoreArray, const GLuint64 *signalValueArray); #ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glUploadGPUMaskNVX (GLbitfield mask); +GLAPI void APIENTRY glUploadGpuMaskNVX (GLbitfield mask); GLAPI void APIENTRY glMulticastViewportArrayvNVX (GLuint gpu, GLuint first, GLsizei count, const GLfloat *v); GLAPI void APIENTRY glMulticastViewportPositionWScaleNVX (GLuint gpu, GLuint index, GLfloat xcoeff, GLfloat ycoeff); GLAPI void APIENTRY glMulticastScissorArrayvNVX (GLuint gpu, GLuint first, GLsizei count, const GLint *v); -GLAPI GLuint APIENTRY glAsyncCopyBufferSubDataNVX (GLsizei waitSemaphoreCount, const GLuint *waitSemaphoreArray, const GLuint64 *fenceValueArray, GLuint readGPU, GLbitfield writeGPUMask, GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size, GLsizei signalSemaphoreCount, const GLuint *signalSemaphoreArray, const GLuint64 *signalValueArray); -GLAPI GLuint APIENTRY glAsyncCopyImageSubDataNVX (GLsizei waitSemaphoreCount, const GLuint *waitSemaphoreArray, const GLuint64 *waitValueArray, GLuint srcGPU, GLbitfield dstGPUMask, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth, GLsizei signalSemaphoreCount, const GLuint *signalSemaphoreArray, const GLuint64 *signalValueArray); +GLAPI GLuint APIENTRY glAsyncCopyBufferSubDataNVX (GLsizei waitSemaphoreCount, const GLuint *waitSemaphoreArray, const GLuint64 *fenceValueArray, GLuint readGpu, GLbitfield writeGpuMask, GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size, GLsizei signalSemaphoreCount, const GLuint *signalSemaphoreArray, const GLuint64 *signalValueArray); +GLAPI GLuint APIENTRY glAsyncCopyImageSubDataNVX (GLsizei waitSemaphoreCount, const GLuint *waitSemaphoreArray, const GLuint64 *waitValueArray, GLuint srcGpu, GLbitfield dstGpuMask, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth, GLsizei signalSemaphoreCount, const GLuint *signalSemaphoreArray, const GLuint64 *signalValueArray); #endif #endif /* GL_NVX_gpu_multicast2 */ @@ -9824,11 +9824,11 @@ GLAPI GLuint APIENTRY glAsyncCopyImageSubDataNVX (GLsizei waitSemaphoreCount, co #define GL_LGPU_SEPARATE_STORAGE_BIT_NVX 0x0800 #define GL_MAX_LGPU_GPUS_NVX 0x92BA typedef void (APIENTRYP PFNGLLGPUNAMEDBUFFERSUBDATANVXPROC) (GLbitfield gpuMask, GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data); -typedef void (APIENTRYP PFNGLLGPUCOPYIMAGESUBDATANVXPROC) (GLuint sourceGPU, GLbitfield destinationGPUMask, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srxY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei width, GLsizei height, GLsizei depth); +typedef void (APIENTRYP PFNGLLGPUCOPYIMAGESUBDATANVXPROC) (GLuint sourceGpu, GLbitfield destinationGpuMask, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srxY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei width, GLsizei height, GLsizei depth); typedef void (APIENTRYP PFNGLLGPUINTERLOCKNVXPROC) (void); #ifdef GL_GLEXT_PROTOTYPES GLAPI void APIENTRY glLGPUNamedBufferSubDataNVX (GLbitfield gpuMask, GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data); -GLAPI void APIENTRY glLGPUCopyImageSubDataNVX (GLuint sourceGPU, GLbitfield destinationGPUMask, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srxY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei width, GLsizei height, GLsizei depth); +GLAPI void APIENTRY glLGPUCopyImageSubDataNVX (GLuint sourceGpu, GLbitfield destinationGpuMask, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srxY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei width, GLsizei height, GLsizei depth); GLAPI void APIENTRY glLGPUInterlockNVX (void); #endif #endif /* GL_NVX_linked_gpu_multicast */ @@ -9836,13 +9836,13 @@ GLAPI void APIENTRY glLGPUInterlockNVX (void); #ifndef GL_NVX_progress_fence #define GL_NVX_progress_fence 1 typedef GLuint (APIENTRYP PFNGLCREATEPROGRESSFENCENVXPROC) (void); -typedef void (APIENTRYP PFNGLSIGNALSEMAPHOREUI64NVXPROC) (GLuint signalGPU, GLsizei fenceObjectCount, const GLuint *semaphoreArray, const GLuint64 *fenceValueArray); -typedef void (APIENTRYP PFNGLWAITSEMAPHOREUI64NVXPROC) (GLuint waitGPU, GLsizei fenceObjectCount, const GLuint *semaphoreArray, const GLuint64 *fenceValueArray); +typedef void (APIENTRYP PFNGLSIGNALSEMAPHOREUI64NVXPROC) (GLuint signalGpu, GLsizei fenceObjectCount, const GLuint *semaphoreArray, const GLuint64 *fenceValueArray); +typedef void (APIENTRYP PFNGLWAITSEMAPHOREUI64NVXPROC) (GLuint waitGpu, GLsizei fenceObjectCount, const GLuint *semaphoreArray, const GLuint64 *fenceValueArray); typedef void (APIENTRYP PFNGLCLIENTWAITSEMAPHOREUI64NVXPROC) (GLsizei fenceObjectCount, const GLuint *semaphoreArray, const GLuint64 *fenceValueArray); #ifdef GL_GLEXT_PROTOTYPES GLAPI GLuint APIENTRY glCreateProgressFenceNVX (void); -GLAPI void APIENTRY glSignalSemaphoreui64NVX (GLuint signalGPU, GLsizei fenceObjectCount, const GLuint *semaphoreArray, const GLuint64 *fenceValueArray); -GLAPI void APIENTRY glWaitSemaphoreui64NVX (GLuint waitGPU, GLsizei fenceObjectCount, const GLuint *semaphoreArray, const GLuint64 *fenceValueArray); +GLAPI void APIENTRY glSignalSemaphoreui64NVX (GLuint signalGpu, GLsizei fenceObjectCount, const GLuint *semaphoreArray, const GLuint64 *fenceValueArray); +GLAPI void APIENTRY glWaitSemaphoreui64NVX (GLuint waitGpu, GLsizei fenceObjectCount, const GLuint *semaphoreArray, const GLuint64 *fenceValueArray); GLAPI void APIENTRY glClientWaitSemaphoreui64NVX (GLsizei fenceObjectCount, const GLuint *semaphoreArray, const GLuint64 *fenceValueArray); #endif #endif /* GL_NVX_progress_fence */ @@ -10433,25 +10433,25 @@ GLAPI void APIENTRY glFramebufferTextureFaceEXT (GLenum target, GLenum attachmen #define GL_MULTICAST_PROGRAMMABLE_SAMPLE_LOCATION_NV 0x9549 typedef void (APIENTRYP PFNGLRENDERGPUMASKNVPROC) (GLbitfield mask); typedef void (APIENTRYP PFNGLMULTICASTBUFFERSUBDATANVPROC) (GLbitfield gpuMask, GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data); -typedef void (APIENTRYP PFNGLMULTICASTCOPYBUFFERSUBDATANVPROC) (GLuint readGPU, GLbitfield writeGPUMask, GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); -typedef void (APIENTRYP PFNGLMULTICASTCOPYIMAGESUBDATANVPROC) (GLuint srcGPU, GLbitfield dstGPUMask, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth); -typedef void (APIENTRYP PFNGLMULTICASTBLITFRAMEBUFFERNVPROC) (GLuint srcGPU, GLuint dstGPU, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +typedef void (APIENTRYP PFNGLMULTICASTCOPYBUFFERSUBDATANVPROC) (GLuint readGpu, GLbitfield writeGpuMask, GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLMULTICASTCOPYIMAGESUBDATANVPROC) (GLuint srcGpu, GLbitfield dstGpuMask, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth); +typedef void (APIENTRYP PFNGLMULTICASTBLITFRAMEBUFFERNVPROC) (GLuint srcGpu, GLuint dstGpu, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); typedef void (APIENTRYP PFNGLMULTICASTFRAMEBUFFERSAMPLELOCATIONSFVNVPROC) (GLuint gpu, GLuint framebuffer, GLuint start, GLsizei count, const GLfloat *v); typedef void (APIENTRYP PFNGLMULTICASTBARRIERNVPROC) (void); -typedef void (APIENTRYP PFNGLMULTICASTWAITSYNCNVPROC) (GLuint signalGPU, GLbitfield waitGPUMask); +typedef void (APIENTRYP PFNGLMULTICASTWAITSYNCNVPROC) (GLuint signalGpu, GLbitfield waitGpuMask); typedef void (APIENTRYP PFNGLMULTICASTGETQUERYOBJECTIVNVPROC) (GLuint gpu, GLuint id, GLenum pname, GLint *params); typedef void (APIENTRYP PFNGLMULTICASTGETQUERYOBJECTUIVNVPROC) (GLuint gpu, GLuint id, GLenum pname, GLuint *params); typedef void (APIENTRYP PFNGLMULTICASTGETQUERYOBJECTI64VNVPROC) (GLuint gpu, GLuint id, GLenum pname, GLint64 *params); typedef void (APIENTRYP PFNGLMULTICASTGETQUERYOBJECTUI64VNVPROC) (GLuint gpu, GLuint id, GLenum pname, GLuint64 *params); #ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glRenderGPUMaskNV (GLbitfield mask); +GLAPI void APIENTRY glRenderGpuMaskNV (GLbitfield mask); GLAPI void APIENTRY glMulticastBufferSubDataNV (GLbitfield gpuMask, GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data); -GLAPI void APIENTRY glMulticastCopyBufferSubDataNV (GLuint readGPU, GLbitfield writeGPUMask, GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); -GLAPI void APIENTRY glMulticastCopyImageSubDataNV (GLuint srcGPU, GLbitfield dstGPUMask, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth); -GLAPI void APIENTRY glMulticastBlitFramebufferNV (GLuint srcGPU, GLuint dstGPU, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +GLAPI void APIENTRY glMulticastCopyBufferSubDataNV (GLuint readGpu, GLbitfield writeGpuMask, GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +GLAPI void APIENTRY glMulticastCopyImageSubDataNV (GLuint srcGpu, GLbitfield dstGpuMask, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth); +GLAPI void APIENTRY glMulticastBlitFramebufferNV (GLuint srcGpu, GLuint dstGpu, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); GLAPI void APIENTRY glMulticastFramebufferSampleLocationsfvNV (GLuint gpu, GLuint framebuffer, GLuint start, GLsizei count, const GLfloat *v); GLAPI void APIENTRY glMulticastBarrierNV (void); -GLAPI void APIENTRY glMulticastWaitSyncNV (GLuint signalGPU, GLbitfield waitGPUMask); +GLAPI void APIENTRY glMulticastWaitSyncNV (GLuint signalGpu, GLbitfield waitGpuMask); GLAPI void APIENTRY glMulticastGetQueryObjectivNV (GLuint gpu, GLuint id, GLenum pname, GLint *params); GLAPI void APIENTRY glMulticastGetQueryObjectuivNV (GLuint gpu, GLuint id, GLenum pname, GLuint *params); GLAPI void APIENTRY glMulticastGetQueryObjecti64vNV (GLuint gpu, GLuint id, GLenum pname, GLint64 *params); diff --git a/third-party/SDL/include/SDL3/SDL_stdinc.h b/third-party/SDL/include/SDL3/SDL_stdinc.h index 205f9bc3b6..b6403bea71 100644 --- a/third-party/SDL/include/SDL3/SDL_stdinc.h +++ b/third-party/SDL/include/SDL3/SDL_stdinc.h @@ -1290,8 +1290,9 @@ extern "C" { /** * 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 + * If SDL used alloca() to allocate this memory, this macro does nothing (other + * than insert `((void)(data)` so the compiler sees an expression) 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. * @@ -1303,10 +1304,10 @@ extern "C" { * * \sa SDL_stack_alloc */ -#define SDL_stack_free(data) +#define SDL_stack_free(data) ((void)(data)) #elif !defined(SDL_DISABLE_ALLOCA) #define SDL_stack_alloc(type, count) (type*)alloca(sizeof(type)*(count)) -#define SDL_stack_free(data) +#define SDL_stack_free(data) ((void)(data)) #else #define SDL_stack_alloc(type, count) (type*)SDL_malloc(sizeof(type)*(count)) #define SDL_stack_free(data) SDL_free(data) diff --git a/third-party/SDL/include/SDL3/SDL_version.h b/third-party/SDL/include/SDL3/SDL_version.h index 12f157f8f2..b5a30692ee 100644 --- a/third-party/SDL/include/SDL3/SDL_version.h +++ b/third-party/SDL/include/SDL3/SDL_version.h @@ -62,7 +62,7 @@ extern "C" { * * \since This macro is available since SDL 3.2.0. */ -#define SDL_MICRO_VERSION 4 +#define SDL_MICRO_VERSION 10 /** * This macro turns the version numbers into a numeric value. diff --git a/third-party/SDL/include/build_config/SDL_build_config.h.cmake b/third-party/SDL/include/build_config/SDL_build_config.h.cmake index 520b721771..17cb8f2574 100644 --- a/third-party/SDL/include/build_config/SDL_build_config.h.cmake +++ b/third-party/SDL/include/build_config/SDL_build_config.h.cmake @@ -107,7 +107,6 @@ #cmakedefine HAVE_STRTOK_R 1 #cmakedefine HAVE_ITOA 1 #cmakedefine HAVE__LTOA 1 -#cmakedefine HAVE__UITOA 1 #cmakedefine HAVE__ULTOA 1 #cmakedefine HAVE_STRTOL 1 #cmakedefine HAVE_STRTOUL 1 diff --git a/third-party/SDL/src/SDL.c b/third-party/SDL/src/SDL.c index b1a1a56354..3e61b8ad4a 100644 --- a/third-party/SDL/src/SDL.c +++ b/third-party/SDL/src/SDL.c @@ -173,7 +173,10 @@ const char *SDL_GetAppMetadataProperty(const char *name) } if (!value || !*value) { if (SDL_strcmp(name, SDL_PROP_APP_METADATA_NAME_STRING) == 0) { - value = "SDL Application"; + value = SDL_GetExeName(); + if (!value) { + value = "SDL Application"; + } } else if (SDL_strcmp(name, SDL_PROP_APP_METADATA_TYPE_STRING) == 0) { value = "application"; } diff --git a/third-party/SDL/src/SDL_error.c b/third-party/SDL/src/SDL_error.c index 618eb93d68..64c1bf06eb 100644 --- a/third-party/SDL/src/SDL_error.c +++ b/third-party/SDL/src/SDL_error.c @@ -43,22 +43,28 @@ bool SDL_SetErrorV(SDL_PRINTF_FORMAT_STRING const char *fmt, va_list ap) if (fmt) { int result; SDL_error *error = SDL_GetErrBuf(true); + + // use the other slot for the new error, so if this does + // SDL_SetError("%s", SDL_GetError()), we don't have a problem. + const int current = error->current ? 0 : 1; + SDL_ErrorInfo *errinfo = &error->info[current]; + error->current = current; + + errinfo->error = SDL_ErrorCodeGeneric; + va_list ap2; - - error->error = SDL_ErrorCodeGeneric; - va_copy(ap2, ap); - result = SDL_vsnprintf(error->str, error->len, fmt, ap2); + result = SDL_vsnprintf(errinfo->str, errinfo->len, fmt, ap2); va_end(ap2); - if (result >= 0 && (size_t)result >= error->len && error->realloc_func) { + if (result >= 0 && (size_t)result >= errinfo->len && error->realloc_func) { size_t len = (size_t)result + 1; - char *str = (char *)error->realloc_func(error->str, len); + char *str = (char *)error->realloc_func(errinfo->str, len); if (str) { - error->str = str; - error->len = len; + errinfo->str = str; + errinfo->len = len; va_copy(ap2, ap); - (void)SDL_vsnprintf(error->str, error->len, fmt, ap2); + (void)SDL_vsnprintf(errinfo->str, errinfo->len, fmt, ap2); va_end(ap2); } } @@ -67,7 +73,7 @@ bool SDL_SetErrorV(SDL_PRINTF_FORMAT_STRING const char *fmt, va_list ap) // Note that there are many recoverable errors that may happen internally and // can be safely ignored if the public API doesn't return an error code. #if 0 - SDL_LogError(SDL_LOG_CATEGORY_ERROR, "%s", error->str); + SDL_LogError(SDL_LOG_CATEGORY_ERROR, "%s", errinfo->str); #endif } @@ -82,9 +88,10 @@ const char *SDL_GetError(void) return ""; } - switch (error->error) { + const SDL_ErrorInfo *errinfo = &error->info[error->current]; + switch (errinfo->error) { case SDL_ErrorCodeGeneric: - return error->str; + return errinfo->str; case SDL_ErrorCodeOutOfMemory: return "Out of memory"; default: @@ -97,7 +104,8 @@ bool SDL_ClearError(void) SDL_error *error = SDL_GetErrBuf(false); if (error) { - error->error = SDL_ErrorCodeNone; + SDL_ErrorInfo *errinfo = &error->info[error->current]; + errinfo->error = SDL_ErrorCodeNone; } return true; } @@ -107,7 +115,8 @@ bool SDL_OutOfMemory(void) SDL_error *error = SDL_GetErrBuf(true); if (error) { - error->error = SDL_ErrorCodeOutOfMemory; + SDL_ErrorInfo *errinfo = &error->info[error->current]; + errinfo->error = SDL_ErrorCodeOutOfMemory; } return false; } diff --git a/third-party/SDL/src/SDL_error_c.h b/third-party/SDL/src/SDL_error_c.h index 6ea7703832..7d53783d88 100644 --- a/third-party/SDL/src/SDL_error_c.h +++ b/third-party/SDL/src/SDL_error_c.h @@ -34,11 +34,17 @@ typedef enum SDL_ErrorCodeOutOfMemory, } SDL_ErrorCode; -typedef struct SDL_error +typedef struct SDL_ErrorInfo { SDL_ErrorCode error; char *str; size_t len; +} SDL_ErrorInfo; + +typedef struct SDL_error +{ + SDL_ErrorInfo info[2]; // there are two, so you can do SDL_SetError("%s", SDL_GetError()) without stomping the buffer. + int current; SDL_realloc_func realloc_func; SDL_free_func free_func; } SDL_error; diff --git a/third-party/SDL/src/SDL_internal.h b/third-party/SDL/src/SDL_internal.h index e2debb96bc..475009366e 100644 --- a/third-party/SDL/src/SDL_internal.h +++ b/third-party/SDL/src/SDL_internal.h @@ -266,6 +266,11 @@ extern "C" { anything calling it without an extremely good reason. */ extern SDL_NORETURN void SDL_ExitProcess(int exitcode); +// Get just the process's binary name, no path. NULL if it doesn't make sense for a platform. +// Can be something not a file, like a package ID on Android. Meant to be human-readable, not appended to a path, etc. +// Calculates and caches the string on first call. String lives until SDL_Quit(). This is not a public API right now! +extern const char *SDL_GetExeName(void); + #ifdef HAVE_LIBC #define SDL_abort() abort() #else diff --git a/third-party/SDL/src/SDL_utils.c b/third-party/SDL/src/SDL_utils.c index 66b043796e..146d816341 100644 --- a/third-party/SDL/src/SDL_utils.c +++ b/third-party/SDL/src/SDL_utils.c @@ -111,7 +111,8 @@ void SDL_CalculateFraction(float x, int *numerator, int *denominator) bool SDL_startswith(const char *string, const char *prefix) { - if (SDL_strncmp(string, prefix, SDL_strlen(prefix)) == 0) { + if (string && prefix && + SDL_strncmp(string, prefix, SDL_strlen(prefix)) == 0) { return true; } return false; @@ -384,6 +385,39 @@ int SDL_URIToLocal(const char *src, char *dst) return -1; } +bool SDL_IsURI(const char *uri) +{ + /* A valid URI begins with a letter and is followed by any sequence of + * letters, digits, '+', '.', or '-'. + */ + if (!uri) { + return false; + } + + // The first character of the scheme must be a letter. + if (!((*uri >= 'a' && *uri <= 'z') || (*uri >= 'A' && *uri <= 'Z'))) { + return false; + } + + /* If the colon is found before encountering the end of the string or + * any invalid characters, the scheme can be considered valid. + */ + while (*uri) { + if (!((*uri >= 'a' && *uri <= 'z') || + (*uri >= 'A' && *uri <= 'Z') || + (*uri >= '0' && *uri <= '9') || + *uri == '+' || *uri == '-' || *uri == '.')) { + return false; + } + + if (*++uri == ':') { + return true; + } + } + + return false; +} + // This is a set of per-thread persistent strings that we can return from the SDL API. // This is used for short strings that might persist past the lifetime of the object // they are related to. @@ -588,4 +622,3 @@ void SDL_DebugLogBackend(const char *subsystem, const char *backend) { SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "SDL chose %s backend '%s'", subsystem, backend); } - diff --git a/third-party/SDL/src/SDL_utils_c.h b/third-party/SDL/src/SDL_utils_c.h index 266eb09249..a49eaa11b6 100644 --- a/third-party/SDL/src/SDL_utils_c.h +++ b/third-party/SDL/src/SDL_utils_c.h @@ -48,6 +48,9 @@ extern bool SDL_endswith(const char *string, const char *suffix); */ extern int SDL_URIToLocal(const char *src, char *dst); +/// Determine if a URI is valid by validating the scheme. +extern bool SDL_IsURI(const char *uri); + typedef enum { SDL_OBJECT_TYPE_UNKNOWN, diff --git a/third-party/SDL/src/atomic/SDL_atomic.c b/third-party/SDL/src/atomic/SDL_atomic.c index 40d57929ae..f14bd106dc 100644 --- a/third-party/SDL/src/atomic/SDL_atomic.c +++ b/third-party/SDL/src/atomic/SDL_atomic.c @@ -33,18 +33,16 @@ #include #endif -// The __atomic_load_n() intrinsic showed up in different times for different compilers. -#ifdef __clang__ -#if __has_builtin(__atomic_load_n) || defined(HAVE_GCC_ATOMICS) -/* !!! FIXME: this advertises as available in the NDK but uses an external symbol we don't have. - It might be in a later NDK or we might need an extra library? --ryan. */ -#ifndef SDL_PLATFORM_ANDROID +// The __atomic intrinsics showed up in different times for different compilers. +#if (defined(__GNUC__) && (__GNUC__ >= 5)) || (defined(__clang__) && defined(HAVE_GCC_ATOMICS)) +#define HAVE_ATOMIC_LOAD_N 1 +#define HAVE_ATOMIC_EXCHANGE_N 1 +#else +#if SDL_HAS_BUILTIN(__atomic_load_n) #define HAVE_ATOMIC_LOAD_N 1 #endif -#endif -#elif defined(__GNUC__) -#if (__GNUC__ >= 5) -#define HAVE_ATOMIC_LOAD_N 1 +#if SDL_HAS_BUILTIN(__atomic_exchange_n) +#define HAVE_ATOMIC_EXCHANGE_N 1 #endif #endif @@ -182,7 +180,14 @@ int SDL_SetAtomicInt(SDL_AtomicInt *a, int v) #ifdef HAVE_MSC_ATOMICS SDL_COMPILE_TIME_ASSERT(atomic_set, sizeof(long) == sizeof(a->value)); return _InterlockedExchange((long *)&a->value, v); +#elif defined(HAVE_ATOMIC_EXCHANGE_N) + return __atomic_exchange_n(&a->value, v, __ATOMIC_SEQ_CST); #elif defined(HAVE_GCC_ATOMICS) + // __sync_lock_test_and_set() is designed for locking rather than a + // generic atomic exchange, so it only provides an acquire barrier + // and may not store the exact value on all architectures. We prefer + // __atomic_exchange_n() instead on all modern compilers. + __sync_synchronize(); return __sync_lock_test_and_set(&a->value, v); #elif defined(SDL_PLATFORM_SOLARIS) SDL_COMPILE_TIME_ASSERT(atomic_set, sizeof(uint_t) == sizeof(a->value)); @@ -201,7 +206,14 @@ Uint32 SDL_SetAtomicU32(SDL_AtomicU32 *a, Uint32 v) #ifdef HAVE_MSC_ATOMICS SDL_COMPILE_TIME_ASSERT(atomic_set, sizeof(long) == sizeof(a->value)); return _InterlockedExchange((long *)&a->value, v); +#elif defined(HAVE_ATOMIC_EXCHANGE_N) + return __atomic_exchange_n(&a->value, v, __ATOMIC_SEQ_CST); #elif defined(HAVE_GCC_ATOMICS) + // __sync_lock_test_and_set() is designed for locking rather than a + // generic atomic exchange, so it only provides an acquire barrier + // and may not store the exact value on all architectures. We prefer + // __atomic_exchange_n() instead on all modern compilers. + __sync_synchronize(); return __sync_lock_test_and_set(&a->value, v); #elif defined(SDL_PLATFORM_SOLARIS) SDL_COMPILE_TIME_ASSERT(atomic_set, sizeof(uint_t) == sizeof(a->value)); @@ -219,7 +231,14 @@ void *SDL_SetAtomicPointer(void **a, void *v) { #ifdef HAVE_MSC_ATOMICS return _InterlockedExchangePointer(a, v); +#elif defined(HAVE_ATOMIC_EXCHANGE_N) + return __atomic_exchange_n(a, v, __ATOMIC_SEQ_CST); #elif defined(HAVE_GCC_ATOMICS) + // __sync_lock_test_and_set() is designed for locking rather than a + // generic atomic exchange, so it only provides an acquire barrier + // and may not store the exact value on all architectures. We prefer + // __atomic_exchange_n() instead on all modern compilers. + __sync_synchronize(); return __sync_lock_test_and_set(a, v); #elif defined(SDL_PLATFORM_SOLARIS) return atomic_swap_ptr(a, v); @@ -278,9 +297,15 @@ int SDL_GetAtomicInt(SDL_AtomicInt *a) { #ifdef HAVE_ATOMIC_LOAD_N return __atomic_load_n(&a->value, __ATOMIC_SEQ_CST); -#elif defined(HAVE_MSC_ATOMICS) - SDL_COMPILE_TIME_ASSERT(atomic_get, sizeof(long) == sizeof(a->value)); - return _InterlockedOr((long *)&a->value, 0); +#elif defined(HAVE_MSC_ATOMICS) && (defined(_M_ARM64) || defined(_M_ARM64EC)) + SDL_COMPILE_TIME_ASSERT(atomic_get_int, sizeof(__int32) == sizeof(a->value)); + return (int)__ldar32((unsigned __int32 *)&a->value); +#elif defined(HAVE_MSC_ATOMICS) && (defined(_M_X64) || defined(_M_IX86)) + SDL_COMPILE_TIME_ASSERT(atomic_get_int, sizeof(int) == sizeof(a->value)); + SDL_CompilerBarrier(); + int value = *(volatile int *)&a->value; + SDL_CompilerBarrier(); + return value; #elif defined(HAVE_GCC_ATOMICS) return __sync_or_and_fetch(&a->value, 0); #elif defined(SDL_PLATFORM_MACOS) // this is deprecated in 10.12 sdk; favor gcc atomics. @@ -300,9 +325,15 @@ Uint32 SDL_GetAtomicU32(SDL_AtomicU32 *a) { #ifdef HAVE_ATOMIC_LOAD_N return __atomic_load_n(&a->value, __ATOMIC_SEQ_CST); -#elif defined(HAVE_MSC_ATOMICS) - SDL_COMPILE_TIME_ASSERT(atomic_get, sizeof(long) == sizeof(a->value)); - return (Uint32)_InterlockedOr((long *)&a->value, 0); +#elif defined(HAVE_MSC_ATOMICS) && (defined(_M_ARM64) || defined(_M_ARM64EC)) + SDL_COMPILE_TIME_ASSERT(atomic_get_u32, sizeof(__int32) == sizeof(a->value)); + return __ldar32((unsigned __int32 *)&a->value); +#elif defined(HAVE_MSC_ATOMICS) && (defined(_M_X64) || defined(_M_IX86)) + SDL_COMPILE_TIME_ASSERT(atomic_get_u32, sizeof(Uint32) == sizeof(a->value)); + SDL_CompilerBarrier(); + Uint32 value = *(volatile Uint32 *)&a->value; + SDL_CompilerBarrier(); + return value; #elif defined(HAVE_GCC_ATOMICS) return __sync_or_and_fetch(&a->value, 0); #elif defined(SDL_PLATFORM_MACOS) // this is deprecated in 10.12 sdk; favor gcc atomics. @@ -323,8 +354,14 @@ void *SDL_GetAtomicPointer(void **a) { #ifdef HAVE_ATOMIC_LOAD_N return __atomic_load_n(a, __ATOMIC_SEQ_CST); -#elif defined(HAVE_MSC_ATOMICS) - return _InterlockedCompareExchangePointer(a, NULL, NULL); +#elif defined(HAVE_MSC_ATOMICS) && (defined(_M_ARM64) || defined(_M_ARM64EC)) + SDL_COMPILE_TIME_ASSERT(atomic_get_ptr, sizeof(__int64) == sizeof(*a)); + return (void *)__ldar64((unsigned __int64 *)a); +#elif defined(HAVE_MSC_ATOMICS) && (defined(_M_X64) || defined(_M_IX86)) + SDL_CompilerBarrier(); + void *value = *(void * volatile *)a; + SDL_CompilerBarrier(); + return value; #elif defined(HAVE_GCC_ATOMICS) return __sync_val_compare_and_swap(a, (void *)0, (void *)0); #elif defined(SDL_PLATFORM_SOLARIS) diff --git a/third-party/SDL/src/atomic/SDL_spinlock.c b/third-party/SDL/src/atomic/SDL_spinlock.c index 1eb8e3583d..c048866604 100644 --- a/third-party/SDL/src/atomic/SDL_spinlock.c +++ b/third-party/SDL/src/atomic/SDL_spinlock.c @@ -50,8 +50,9 @@ bool SDL_TryLockSpinlock(SDL_SpinLock *lock) #if defined(HAVE_GCC_ATOMICS) || defined(HAVE_GCC_SYNC_LOCK_TEST_AND_SET) return __sync_lock_test_and_set(lock, 1) == 0; -#elif defined(_MSC_VER) && (defined(_M_ARM) || defined(_M_ARM64)) - return _InterlockedExchange_acq(lock, 1) == 0; +#elif defined(_MSC_VER) && (defined(_M_ARM) || defined(_M_ARM64) || defined(_M_ARM64EC)) + SDL_COMPILE_TIME_ASSERT(locksize, sizeof(*lock) == sizeof(long)); + return _InterlockedExchange_acq((long *)lock, 1) == 0; #elif defined(_MSC_VER) SDL_COMPILE_TIME_ASSERT(locksize, sizeof(*lock) == sizeof(long)); @@ -166,19 +167,17 @@ void SDL_UnlockSpinlock(SDL_SpinLock *lock) #if defined(HAVE_GCC_ATOMICS) || defined(HAVE_GCC_SYNC_LOCK_TEST_AND_SET) __sync_lock_release(lock); -#elif defined(_MSC_VER) && (defined(_M_ARM) || defined(_M_ARM64)) - _InterlockedExchange_rel(lock, 0); - -#elif defined(_MSC_VER) - _ReadWriteBarrier(); - *lock = 0; +#elif defined(_MSC_VER) && (defined(_M_ARM) || defined(_M_ARM64) || defined(_M_ARM64EC)) + SDL_COMPILE_TIME_ASSERT(locksize, sizeof(*lock) == sizeof(long)); + _InterlockedExchange_rel((long *)lock, 0); #elif defined(SDL_PLATFORM_SOLARIS) // Used for Solaris when not using gcc. - *lock = 0; membar_producer(); + *lock = 0; #else + SDL_MemoryBarrierRelease(); *lock = 0; #endif } diff --git a/third-party/SDL/src/audio/wasapi/SDL_wasapi.c b/third-party/SDL/src/audio/wasapi/SDL_wasapi.c index c01f007ff1..c28e797e3d 100644 --- a/third-party/SDL/src/audio/wasapi/SDL_wasapi.c +++ b/third-party/SDL/src/audio/wasapi/SDL_wasapi.c @@ -64,6 +64,22 @@ static const IID SDL_IID_IAudioClient3 = { 0x7ed4ee07, 0x8e67, 0x4cd4, { 0x8c, 0 static bool immdevice_initialized = false; static bool supports_recording_on_playback_devices = false; +#ifdef __IAudioClient2_INTERFACE_DEFINED__ +// AUDCLNT_STREAMOPTIONS and AudioClientProperties->Options were +// added in Windows 8.1: This ugliness is here to make sure that +// we can build against older SDK versions. +#define SDL_AUDCLNT_STREAMOPTIONS_RAW 0x1 +typedef union SDL_AudioClientProperties { + AudioClientProperties a; + struct _SDL_AudioClientProperties { + UINT32 cbSize; + BOOL bIsOffload; + AUDIO_STREAM_CATEGORY eCategory; + int Options; // AUDCLNT_STREAMOPTIONS + } s; +} SDL_AudioClientProperties; +#endif // + // 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. @@ -741,10 +757,10 @@ static bool mgmtthrtask_PrepDevice(void *userdata) IAudioClient2 *client2 = NULL; ret = IAudioClient_QueryInterface(client, &SDL_IID_IAudioClient2, (void **)&client2); if (SUCCEEDED(ret)) { - AudioClientProperties audioProps; + SDL_AudioClientProperties audioProps; SDL_zero(audioProps); - audioProps.cbSize = sizeof(audioProps); + audioProps.a.cbSize = sizeof(audioProps); // Setting AudioCategory_GameChat breaks audio on several devices, including Behringer U-PHORIA UM2 and RODE NT-USB Mini. // We'll disable this for now until we understand more about what's happening. @@ -752,25 +768,28 @@ static bool mgmtthrtask_PrepDevice(void *userdata) const char *hint = SDL_GetHint(SDL_HINT_AUDIO_DEVICE_STREAM_ROLE); if (hint && *hint) { if (SDL_strcasecmp(hint, "Communications") == 0) { - audioProps.eCategory = AudioCategory_Communications; + audioProps.a.eCategory = AudioCategory_Communications; } else if (SDL_strcasecmp(hint, "Game") == 0) { // We'll add support for GameEffects as distinct from GameMedia later when we add stream roles - audioProps.eCategory = AudioCategory_GameEffects; + audioProps.a.eCategory = AudioCategory_GameEffects; } else if (SDL_strcasecmp(hint, "GameChat") == 0) { - audioProps.eCategory = AudioCategory_GameChat; + audioProps.a.eCategory = AudioCategory_GameChat; } else if (SDL_strcasecmp(hint, "Movie") == 0) { - audioProps.eCategory = AudioCategory_Movie; + audioProps.a.eCategory = AudioCategory_Movie; } else if (SDL_strcasecmp(hint, "Media") == 0) { - audioProps.eCategory = AudioCategory_Media; + audioProps.a.eCategory = AudioCategory_Media; } } #endif - if (SDL_GetHintBoolean(SDL_HINT_AUDIO_DEVICE_RAW_STREAM, false)) { - audioProps.Options = AUDCLNT_STREAMOPTIONS_RAW; + if (WIN_IsWindows81OrGreater() && + SDL_GetHintBoolean(SDL_HINT_AUDIO_DEVICE_RAW_STREAM, false)) { + audioProps.s.Options = SDL_AUDCLNT_STREAMOPTIONS_RAW; + } else { + audioProps.a.cbSize = sizeof (AudioClientProperties); } - ret = IAudioClient2_SetClientProperties(client2, &audioProps); + ret = IAudioClient2_SetClientProperties(client2, (AudioClientProperties *)&audioProps); if (FAILED(ret)) { // This isn't fatal, let's log it instead of failing SDL_LogWarn(SDL_LOG_CATEGORY_AUDIO, "IAudioClient2_SetClientProperties failed: 0x%lx", ret); diff --git a/third-party/SDL/src/core/android/SDL_android.c b/third-party/SDL/src/core/android/SDL_android.c index fa559484f9..59ea5574b7 100644 --- a/third-party/SDL/src/core/android/SDL_android.c +++ b/third-party/SDL/src/core/android/SDL_android.c @@ -44,6 +44,7 @@ #include #include #include +#include #define SDL_JAVA_PREFIX org_libsdl_app #define CONCAT1(prefix, class, function) CONCAT2(prefix, class, function) @@ -297,11 +298,11 @@ JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeSetupJNI)( JNIEXPORT jboolean JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadDown)( JNIEnv *env, jclass jcls, - jint device_id, jint keycode); + jint device_id, jint keycode, jint scancode); JNIEXPORT jboolean JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadUp)( JNIEnv *env, jclass jcls, - jint device_id, jint keycode); + jint device_id, jint keycode, jint scancode); JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeJoy)( JNIEnv *env, jclass jcls, @@ -311,10 +312,15 @@ JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeHat)( JNIEnv *env, jclass jcls, jint device_id, jint hat_id, jint x, jint y); +JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeJoySensor)( + JNIEnv *env, jclass jcls, + jint device_id, jint sensor_type, jlong sensor_timestamp, jfloat x, jfloat y, jfloat z); + JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddJoystick)( JNIEnv *env, jclass jcls, jint device_id, jstring device_name, jstring device_desc, jint vendor_id, jint product_id, - jint button_mask, jint naxes, jint axis_mask, jint nhats, jboolean can_rumble, jboolean has_rgb_led); + jint button_mask, jint naxes, jint axis_mask, jint nhats, + jboolean can_rumble, jboolean has_rgb_led, jboolean has_accelerometer, jboolean has_gyroscope); JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveJoystick)( JNIEnv *env, jclass jcls, @@ -330,11 +336,12 @@ JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveHaptic)( static JNINativeMethod SDLControllerManager_tab[] = { { "nativeSetupJNI", "()V", SDL_JAVA_CONTROLLER_INTERFACE(nativeSetupJNI) }, - { "onNativePadDown", "(II)Z", SDL_JAVA_CONTROLLER_INTERFACE(onNativePadDown) }, - { "onNativePadUp", "(II)Z", SDL_JAVA_CONTROLLER_INTERFACE(onNativePadUp) }, + { "onNativePadDown", "(III)Z", SDL_JAVA_CONTROLLER_INTERFACE(onNativePadDown) }, + { "onNativePadUp", "(III)Z", SDL_JAVA_CONTROLLER_INTERFACE(onNativePadUp) }, { "onNativeJoy", "(IIF)V", SDL_JAVA_CONTROLLER_INTERFACE(onNativeJoy) }, { "onNativeHat", "(IIII)V", SDL_JAVA_CONTROLLER_INTERFACE(onNativeHat) }, - { "nativeAddJoystick", "(ILjava/lang/String;Ljava/lang/String;IIIIIIZZ)V", SDL_JAVA_CONTROLLER_INTERFACE(nativeAddJoystick) }, + { "onNativeJoySensor", "(IIJFFF)V", SDL_JAVA_CONTROLLER_INTERFACE(onNativeJoySensor) }, + { "nativeAddJoystick", "(ILjava/lang/String;Ljava/lang/String;IIIIIIZZZZ)V", SDL_JAVA_CONTROLLER_INTERFACE(nativeAddJoystick) }, { "nativeRemoveJoystick", "(I)V", SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveJoystick) }, { "nativeAddHaptic", "(ILjava/lang/String;)V", SDL_JAVA_CONTROLLER_INTERFACE(nativeAddHaptic) }, { "nativeRemoveHaptic", "(I)V", SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveHaptic) } @@ -407,6 +414,7 @@ static jclass mControllerManagerClass; // method signatures static jmethodID midPollInputDevices; static jmethodID midJoystickSetLED; +static jmethodID midJoystickSetSensorsEnabled; static jmethodID midPollHapticDevices; static jmethodID midHapticRun; static jmethodID midHapticRumble; @@ -755,6 +763,8 @@ JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeSetupJNI)(JNIEnv *env "pollInputDevices", "()V"); midJoystickSetLED = (*env)->GetStaticMethodID(env, mControllerManagerClass, "joystickSetLED", "(IIII)V"); + midJoystickSetSensorsEnabled = (*env)->GetStaticMethodID(env, mControllerManagerClass, + "joystickSetSensorsEnabled", "(IZ)V"); midPollHapticDevices = (*env)->GetStaticMethodID(env, mControllerManagerClass, "pollHapticDevices", "()V"); midHapticRun = (*env)->GetStaticMethodID(env, mControllerManagerClass, @@ -764,7 +774,7 @@ JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeSetupJNI)(JNIEnv *env midHapticStop = (*env)->GetStaticMethodID(env, mControllerManagerClass, "hapticStop", "(I)V"); - if (!midPollInputDevices || !midJoystickSetLED || !midPollHapticDevices || !midHapticRun || !midHapticRumble || !midHapticStop) { + if (!midPollInputDevices || !midJoystickSetLED || !midJoystickSetSensorsEnabled || !midPollHapticDevices || !midHapticRun || !midHapticRumble || !midHapticStop) { __android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java callbacks, do you have the latest version of SDLControllerManager.java?"); } @@ -1149,10 +1159,10 @@ SDL_JAVA_AUDIO_INTERFACE(nativeRemoveAudioDevice)(JNIEnv *env, jclass jcls, jboo // Paddown JNIEXPORT jboolean JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadDown)( JNIEnv *env, jclass jcls, - jint device_id, jint keycode) + jint device_id, jint keycode, jint scancode) { #ifdef SDL_JOYSTICK_ANDROID - return Android_OnPadDown(device_id, keycode); + return Android_OnPadDown(device_id, keycode, scancode); #else return false; #endif // SDL_JOYSTICK_ANDROID @@ -1161,10 +1171,10 @@ JNIEXPORT jboolean JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadDown)( // Padup JNIEXPORT jboolean JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadUp)( JNIEnv *env, jclass jcls, - jint device_id, jint keycode) + jint device_id, jint keycode, jint scancode) { #ifdef SDL_JOYSTICK_ANDROID - return Android_OnPadUp(device_id, keycode); + return Android_OnPadUp(device_id, keycode, scancode); #else return false; #endif // SDL_JOYSTICK_ANDROID @@ -1190,17 +1200,29 @@ JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeHat)( #endif // SDL_JOYSTICK_ANDROID } +JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeJoySensor)( + JNIEnv *env, jclass jcls, + jint device_id, jint sensor_type, jlong sensor_timestamp, jfloat x, jfloat y, jfloat z) +{ +#ifdef SDL_JOYSTICK_ANDROID + // In Java there's no Uint64 type, so pass Sint64 as if it was Uint64. + Android_OnJoySensor(device_id, sensor_type, sensor_timestamp, x, y, z); +#endif // SDL_JOYSTICK_ANDROID +} + JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddJoystick)( JNIEnv *env, jclass jcls, jint device_id, jstring device_name, jstring device_desc, jint vendor_id, jint product_id, - jint button_mask, jint naxes, jint axis_mask, jint nhats, jboolean can_rumble, jboolean has_rgb_led) + jint button_mask, jint naxes, jint axis_mask, jint nhats, jboolean can_rumble, jboolean has_rgb_led, + jboolean has_accelerometer, jboolean has_gyroscope) { #ifdef SDL_JOYSTICK_ANDROID const char *name = (*env)->GetStringUTFChars(env, device_name, NULL); const char *desc = (*env)->GetStringUTFChars(env, device_desc, NULL); - Android_AddJoystick(device_id, name, desc, vendor_id, product_id, button_mask, naxes, axis_mask, nhats, can_rumble, has_rgb_led); + Android_AddJoystick(device_id, name, desc, vendor_id, product_id, button_mask, naxes, axis_mask, nhats, + can_rumble, has_rgb_led, has_accelerometer, has_gyroscope); (*env)->ReleaseStringUTFChars(env, device_name, name); (*env)->ReleaseStringUTFChars(env, device_desc, desc); @@ -1838,9 +1860,455 @@ static bool Android_JNI_ExceptionOccurred(bool silent) return false; } + +// APK file tree discovery... + +// APK files are just .zip files, so try to find parse out the file tree from it. We'll still +// use Android's system APIs to actually access data, but those APIs aren't reliable for +// enumerating the tree for various reasons. + +// we only care about the directories; Android's AAssetManager can enumerate all files in any +// known directory, but it won't enumerate subdirectories, so we track that by ourselves. +typedef struct APKNode +{ + char *name; + SDL_PathInfo info; + struct APKNode *children; + struct APKNode *next_sibling; +} APKNode; + +static APKNode *APKRootNode = NULL; + +static void FreeAPKNode(APKNode *node) +{ + if (node) { + FreeAPKNode(node->next_sibling); + FreeAPKNode(node->children); + SDL_free(node->name); + SDL_free(node); + } +} + +static APKNode *FindAPKChildNode(APKNode *parent, const char *child) +{ + for (APKNode *node = parent->children; node != NULL; node = node->next_sibling) { + if (SDL_strcmp(child, node->name) == 0) { + return node; + } + } + return NULL; +} + +static const APKNode *FindAPKNode(const char *constpath) +{ + APKNode *parent = APKRootNode; + if (!parent) { + return NULL; + } + + const size_t pathlen = SDL_strlen(constpath); + bool isstack = false; + char *alloc_path = SDL_small_alloc(char, pathlen + 1, &isstack); + if (!alloc_path) { + return NULL; + } + char *path = alloc_path; + SDL_strlcpy(path, constpath, pathlen + 1); + + while (parent) { + while (*path == '/') { + path++; // just in case there are absolute paths or double-slashes, drop them. + } + + if (*path == '\0') { // ended with a '/'? We're done. + break; + } + + char *ptr = SDL_strchr(path, '/'); + if (ptr) { + *ptr = '\0'; // terminate on the end of this subdir's name. + } + + APKNode *node = FindAPKChildNode(parent, path); + if (!node) { + SDL_SetError("No such file or directory"); + parent = NULL; + } else if ((node->info.type == SDL_PATHTYPE_FILE) && ptr) { // file where we want a directory? + SDL_SetError("%s is not a directory", alloc_path); + parent = NULL; + } else { + parent = node; + if (!ptr) { + break; + } + *ptr = '/'; + path = ptr + 1; + } + } + + SDL_small_free(alloc_path, isstack); + return parent; +} + +static APKNode *AddAPKChildNode(APKNode *parent, const char *child) +{ + APKNode *node = FindAPKChildNode(parent, child); + if (!node) { // don't have this one yet, make a new node. + node = (APKNode *) SDL_calloc(1, sizeof (*node)); + if (!node) { + return NULL; // uhoh. + } + + node->name = SDL_strdup(child); + if (!node->name) { + SDL_free(node); + return NULL; // uhoh. + } + + SDL_copyp(&node->info, &parent->info); // you probably need to update this afterwards. + + node->next_sibling = parent->children; + parent->children = node; + } + return node; +} + +static APKNode *AddAPKDirs(char *path, APKNode *parent) +{ + // zip files specify explicit directories by just having a path that ends with a dir separator, + // which works nicely for our needs here; if the last segment of the path doesn't end with a + // '/', it's a file and we can drop it, or we filled in the final subdirectory and the '/' at + // the end will put us at an empty string to be dropped. + // + // directories do not need to be explicitly specified if something uses some deeper path, they + // may need to be inferred from those references, so we build out the tree by looking at all + // files and filling in nodes they mention. + + SDL_assert(parent->info.type == SDL_PATHTYPE_DIRECTORY); + SDL_assert(parent->info.size == 0); + + while (true) { // while still subdirectories to handle... + while (*path == '/') { + path++; // just in case there are absolute paths or double-slashes, drop them. + } + + char *ptr = SDL_strchr(path, '/'); + if (!ptr) { + break; // last thing is either an empty string (we ended with a '/'), or an actual file's name, so drop it. + } + + *ptr = '\0'; // terminate on the end of this subdir's name. + APKNode *node = AddAPKChildNode(parent, path); + *ptr = '/'; + + if (!node) { + return NULL; // uhoh. + } + + parent = node; + path = ptr + 1; // point to start of next section. + } + + return parent; +} + + +static SDL_Time ZipDosTimeToSDLTime(Uint32 dostime) +{ + Uint32 dosdate; + struct tm unixtime; + SDL_zero(unixtime); + + dosdate = (Uint32) ((dostime >> 16) & 0xFFFF); + dostime &= 0xFFFF; + + /* dissect date */ + unixtime.tm_year = ((dosdate >> 9) & 0x7F) + 80; + unixtime.tm_mon = ((dosdate >> 5) & 0x0F) - 1; + unixtime.tm_mday = ((dosdate ) & 0x1F); + + /* dissect time */ + unixtime.tm_hour = ((dostime >> 11) & 0x1F); + unixtime.tm_min = ((dostime >> 5) & 0x3F); + unixtime.tm_sec = ((dostime << 1) & 0x3E); + + /* let mktime calculate daylight savings time. */ + unixtime.tm_isdst = -1; + + return ((SDL_Time) mktime(&unixtime)); +} + + + +#define ZIP_CENTRAL_DIR_SIG 0x02014b50 +#define ZIP_END_OF_CENTRAL_DIR_SIG 0x06054b50 +#define ZIP64_END_OF_CENTRAL_DIR_SIG 0x06064b50 +#define ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR_SIG 0x07064b50 +#define ZIP64_EXTENDED_INFO_EXTRA_FIELD_SIG 0x0001 + +static bool ProcessZip(SDL_IOStream *io, APKNode *root) +{ + // There is a record at the end of a .zip file we can use to find the central directory; + // unfortunately it's before the variable-length comment field, so we might need to read + // from the end of the file until we see a magic signature. For now, we assume APKs don't + // have a comment, so we don't have to search backwards for the signature and it's always + // 22 bytes from EOF. + const Sint64 eocd = SDL_GetIOSize(io) - 22; + if (eocd < 0) { + SDL_Log("ANDROID: Couldn't find End Of Central Directory in APK (%s). Filesystem enumeration will fail.", SDL_GetError()); + return false; + } + + bool zip64 = false; + Sint64 centraldir = -1; + Uint64 num_entries = 0; + Uint16 val16 = 0; + Uint32 val32 = 0; + Uint64 val64 = 0; + + // First, check if this is actually zip64 format instead. The zip64 magic is 20 bytes back. + if (eocd < 20) { // presumably we always _are_ > 20, but let's be defensive here. + goto corrupterr; + } else if (SDL_SeekIO(io, eocd - 20, SDL_IO_SEEK_SET) < 0) { + goto ioerr; + } else if (!SDL_ReadU32LE(io, &val32)) { + goto ioerr; + } else if (val32 == ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR_SIG) { // this is a zip64 archive? + if (!SDL_ReadU32LE(io, &val32)) { // disk number with start of central directory. + goto ioerr; + } else if (val32 != 0) { + goto corrupterr; + } else if (!SDL_ReadU64LE(io, &val64)) { // file offset of zip64 end-of-central-dir record + goto ioerr; + + // note that this gets significantly more complex if there is data prepended to the .zip file + // (like a self-extracting .exe, etc), but until that happens, we're keeping this as simple + // as possible and assuming the file offset in val64 is correct. + + } else if (SDL_SeekIO(io, (Sint64) val64, SDL_IO_SEEK_SET) < 0) { + goto ioerr; + } else if (!SDL_ReadU32LE(io, &val32)) { // zip64 end-of-central-dir signature. + goto ioerr; + } else if (val32 != ZIP64_END_OF_CENTRAL_DIR_SIG) { + goto corrupterr; + } else if (SDL_SeekIO(io, 28, SDL_IO_SEEK_CUR) < 0) { // we don't care about several of the fields, skip over them. + goto ioerr; + } else if (!SDL_ReadU64LE(io, &num_entries)) { // total entries in the central dir. + goto ioerr; + } else if (!SDL_ReadU64LE(io, &val64)) { // size of the central dir. + goto ioerr; + } else if (!SDL_ReadU64LE(io, &val64)) { // offset of the central dir. + goto ioerr; + } + + zip64 = true; + centraldir = (Sint64) val64; + } else if (SDL_SeekIO(io, eocd + 4 + 6, SDL_IO_SEEK_SET) < 0) { // skip back to where we were, plus skip some fields we don't care about. + goto ioerr; + } else if (!SDL_ReadU16LE(io, &val16)) { + goto ioerr; + } else if (!SDL_ReadU32LE(io, &val32)) { // size of the central dir. + goto ioerr; + } else if (!SDL_ReadU32LE(io, &val32)) { // offset of the central dir. + goto ioerr; + } else { + num_entries = (Uint64) val16; + centraldir = (Sint64) val32; + } + + // okay, we know where the central dir is now, go there and start reading entries. + SDL_assert(centraldir > 0); // negative means we failed, zero is impossible since there should be something else there. + + for (Uint64 i = 0; i < num_entries; i++) { + Uint16 fnamelen = 0; + Uint16 extralen = 0; + Uint16 commentlen = 0; + Uint32 dosmodtime = 0; + Uint32 uncompressed32 = 0; + Uint64 uncompressed64 = 0; + + // we don't care about most of this information, just parse through it to get what we need. + if (SDL_SeekIO(io, centraldir, SDL_IO_SEEK_SET) < 0) { + goto ioerr; + } else if (!SDL_ReadU32LE(io, &val32)) { // central dir item signature. + goto ioerr; + } else if (val32 != ZIP_CENTRAL_DIR_SIG) { + goto corrupterr; + } else if (!SDL_ReadU16LE(io, &val16)) { // version made by + goto ioerr; + } else if (!SDL_ReadU16LE(io, &val16)) { // version needed + goto ioerr; + } else if (!SDL_ReadU16LE(io, &val16)) { // general bits + goto ioerr; + } else if (!SDL_ReadU16LE(io, &val16)) { // compression method + goto ioerr; + } else if (!SDL_ReadU32LE(io, &dosmodtime)) { // last mod date/time + goto ioerr; + } else if (!SDL_ReadU32LE(io, &val32)) { // CRC-32 + goto ioerr; + } else if (!SDL_ReadU32LE(io, &val32)) { // compressed size + goto ioerr; + } else if (!SDL_ReadU32LE(io, &uncompressed32)) { // uncompressed size + goto ioerr; + } else if (!SDL_ReadU16LE(io, &fnamelen)) { // filename length + goto ioerr; + } else if (!SDL_ReadU16LE(io, &extralen)) { // extra length + goto ioerr; + } else if (!SDL_ReadU16LE(io, &commentlen)) { // comment length + goto ioerr; + } else if (!SDL_ReadU16LE(io, &val16)) { // disk number start + goto ioerr; + } else if (!SDL_ReadU16LE(io, &val16)) { // internal file attributes + goto ioerr; + } else if (!SDL_ReadU32LE(io, &val32)) { // external file attributes + goto ioerr; + } else if (!SDL_ReadU32LE(io, &val32)) { // relative offset of local header + goto ioerr; + } + + char fnamebuf[0xFFFF+1]; // just eat 64k of stack like a boss until someone complains. + if (SDL_ReadIO(io, fnamebuf, (size_t) fnamelen) != ((size_t) fnamelen)) { + goto ioerr; + } + + // technically zip files might have '\\' dir separators, but these were mostly old DOS files and not Android APKs, I think. Revisit if necessary. + + fnamebuf[fnamelen] = '\0'; // make sure the string is null-terminated. + + //SDL_Log("ANDROID: Saw ZIP entry '%s'", fnamebuf); + + uncompressed64 = (Uint64) uncompressed32; + if (zip64 && (uncompressed32 == 0xFFFFFFFF)) { // file is larger than 4gig, find the zip64 extended info field in the extra section. + bool found = false; + Uint16 remaining = extralen; + while (remaining > 4) { // Two 16-bit values at a minimum, tag and len. + Uint16 tag, len; + if (!SDL_ReadU16LE(io, &tag) || !SDL_ReadU16LE(io, &len)) { + goto ioerr; + } else if (remaining < (len + 4)) { + goto corrupterr; + } else if (tag != ZIP64_EXTENDED_INFO_EXTRA_FIELD_SIG) { // not the field we need, skip over it. + if (SDL_SeekIO(io, (Sint64) len, SDL_IO_SEEK_CUR) < 0) { + goto ioerr; + } + } else if (len < 8) { + goto corrupterr; + } else if ((uncompressed32 == 0xFFFFFFFF) && !SDL_ReadU64LE(io, &uncompressed64)) { + goto ioerr; + + // there are other values in here, but we don't care about them and we're done, so don't try to skip over them. + + } else { + found = true; + break; // got what we need, drop out. + } + remaining -= len + 4; + } + + if (!found) { + goto corrupterr; + } + } + + char *ptr = fnamebuf; + while (*ptr == '/') { // drop absolute paths. + ptr++; + } + + if (SDL_strncmp(ptr, "assets/", 7) == 0) { // we only care about things under 'assets' for now. Drop everything else. + ptr += 6; // keep the '/' so strrchr never returns NULL. + APKNode *node = AddAPKDirs(ptr, root); // this builds out any missing subdirs, returns parent dir's node. + if (!node) { + goto ioerr; // (probably out of memory.) + } + + const SDL_Time modtime = ZipDosTimeToSDLTime(dosmodtime); + ptr = SDL_strrchr(ptr, '/'); + SDL_assert(ptr != NULL); + if (*(++ptr) == '\0') { // explicit directory entry paths end with '/' ...`node` is the new node. + node->info.type = SDL_PATHTYPE_DIRECTORY; + node->info.size = 0; + } else { + node = AddAPKChildNode(node, ptr); + if (!node) { + goto ioerr; // (probably out of memory.) + } + node->info.type = SDL_PATHTYPE_FILE; + node->info.size = uncompressed64; + } + node->info.create_time = node->info.modify_time = node->info.access_time = modtime; + } + + centraldir += (Sint64) (46 + fnamelen + extralen + commentlen); // will seek to next file entry. + } + + return true; + +corrupterr: + SDL_Log("ANDROID: Unexpected or corrupt data in APK. Filesystem enumeration will fail."); + return false; + +ioerr: + SDL_Log("ANDROID: i/o error in APK (%s). Filesystem enumeration will fail.", SDL_GetError()); + return false; +} + +static bool CreateAPKNodes(const char *path) +{ + SDL_Log("ANDROID: Parsing APK file '%s' ...", path); + + SDL_IOStream *io = SDL_IOFromFile(path, "rb"); + if (!io) { + SDL_Log("ANDROID: Can't open APK '%s' for reading (%s). Filesystem enumeration will fail.", path, SDL_GetError()); + } else { + ProcessZip(io, APKRootNode); + SDL_CloseIO(io); + } + return true; +} + +static bool PrepareAPK(void) +{ + // the assetmanager isn't useful for enumerating directories, so parse the APK directly for that info upfront. + bool retval = (APKRootNode != NULL); + if (!retval) { + // allocate this upfront, so if there's a failure, we'll not try again and just have an empty file tree. + APKRootNode = (APKNode *) SDL_calloc(1, sizeof (*APKRootNode)); + if (!APKRootNode) { + return false; // oh well. + } + APKRootNode->info.type = SDL_PATHTYPE_DIRECTORY; + + struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(SDL_FUNCTION); + JNIEnv *env = Android_JNI_GetEnv(); + if (LocalReferenceHolder_Init(&refs, env)) { + jobject context = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetContext); + jmethodID mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, context), "getPackageResourcePath", "()Ljava/lang/String;"); + jstring jstr = (jstring)(*env)->CallObjectMethod(env, context, mid); + jthrowable jexception = (*env)->ExceptionOccurred(env); + if (jexception != NULL) { + (*env)->ExceptionClear(env); // oh well + } else { + const char *apkpath = (*env)->GetStringUTFChars(env, jstr, NULL); + SDL_PathInfo apkinfo; + SDL_assert(apkpath[0] == '/'); // So SDL_GetPathInfo goes through the `stat` path and doesn't try to dig into the APK. + if (SDL_GetPathInfo(apkpath, &apkinfo)) { // we just want the file times here, so oh well if it fails. + APKRootNode->info.create_time = apkinfo.create_time; + APKRootNode->info.modify_time = apkinfo.modify_time; + APKRootNode->info.access_time = apkinfo.access_time; + } + CreateAPKNodes(apkpath); + (*env)->ReleaseStringUTFChars(env, jstr, apkpath); + retval = true; + } + } + LocalReferenceHolder_Cleanup(&refs); + } + return retval; // even on failure, leave an empty root node so we have zero files and don't try to load the .zip again. +} + static void Internal_Android_Create_AssetManager(void) { - struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(SDL_FUNCTION); JNIEnv *env = Android_JNI_GetEnv(); jmethodID mid; @@ -1885,6 +2353,11 @@ static void Internal_Android_Destroy_AssetManager(void) (*env)->DeleteGlobalRef(env, javaAssetManagerRef); asset_manager = NULL; } + + if (APKRootNode) { + FreeAPKNode(APKRootNode); + APKRootNode = NULL; + } } static const char *GetAssetPath(const char *path) @@ -1963,63 +2436,42 @@ bool Android_JNI_EnumerateAssetDirectory(const char *path, SDL_EnumerateDirector { SDL_assert(path != NULL); - if (!asset_manager) { - Internal_Android_Create_AssetManager(); - if (!asset_manager) { - return SDL_SetError("Couldn't create asset manager"); - } - } - - path = GetAssetPath(path); - - AAssetDir *adir = AAssetManager_openDir(asset_manager, path); - if (!adir) { - return SDL_SetError("AAssetManager_openDir failed"); + if (!PrepareAPK()) { + return false; } SDL_EnumerationResult result = SDL_ENUM_CONTINUE; - const char *ent; - while ((result == SDL_ENUM_CONTINUE) && ((ent = AAssetDir_getNextFileName(adir)) != NULL)) { - result = cb(userdata, path, ent); - } + const char *asset_path = GetAssetPath(path); - AAssetDir_close(adir); + const APKNode *apknode = FindAPKNode(asset_path); + if (!apknode) { + return SDL_SetError("No such directory"); + } else if (apknode->info.type != SDL_PATHTYPE_DIRECTORY) { + return SDL_SetError("Not a directory"); + } else { + for (const APKNode *node = apknode->children; node && (result == SDL_ENUM_CONTINUE); node = node->next_sibling) { + result = cb(userdata, path, node->name); + } + } return (result != SDL_ENUM_FAILURE); } bool Android_JNI_GetAssetPathInfo(const char *path, SDL_PathInfo *info) { - if (!asset_manager) { - Internal_Android_Create_AssetManager(); - if (!asset_manager) { - return SDL_SetError("Couldn't create asset manager"); - } + SDL_assert(path != NULL); + + if (!PrepareAPK()) { + return false; } path = GetAssetPath(path); - - // this is sort of messy, but there isn't a stat()-like interface to the Assets. - AAsset *aasset = AAssetManager_open(asset_manager, path, AASSET_MODE_UNKNOWN); - if (aasset) { // it's a file! - info->type = SDL_PATHTYPE_FILE; - info->size = (Uint64) AAsset_getLength64(aasset); - AAsset_close(aasset); - return true; + const APKNode *apknode = FindAPKNode(path); + if (!apknode) { + return SDL_SetError("No such file or directory"); } - - AAssetDir *adir = AAssetManager_openDir(asset_manager, path); - if (adir) { // This does _not_ return NULL for a missing directory! Treat empty directories as missing. Better than nothing. :/ - const bool contains_something = (AAssetDir_getNextFileName(adir) != NULL); // if not NULL, there are files in this directory, so it's _definitely_ a directory. - AAssetDir_close(adir); - if (contains_something) { - info->type = SDL_PATHTYPE_DIRECTORY; - info->size = 0; - return true; - } - } - - return SDL_SetError("Couldn't open asset '%s'", path); + SDL_copyp(info, &apknode->info); + return true; } bool Android_JNI_SetClipboardText(const char *text) @@ -2195,6 +2647,12 @@ void Android_JNI_JoystickSetLED(int device_id, int red, int green, int blue) (*env)->CallStaticVoidMethod(env, mControllerManagerClass, midJoystickSetLED, device_id, red, green, blue); } +void Android_JNI_JoystickSetSensorsEnabled(int device_id, bool enabled) +{ + JNIEnv *env = Android_JNI_GetEnv(); + (*env)->CallStaticVoidMethod(env, mControllerManagerClass, midJoystickSetSensorsEnabled, device_id, (enabled == 1)); +} + void Android_JNI_PollHapticDevices(void) { JNIEnv *env = Android_JNI_GetEnv(); @@ -2390,6 +2848,43 @@ int SDL_GetAndroidSDKVersion(void) return sdk_version; } +char *SDL_GetAndroidPackageName(void) +{ + // this doesn't currently cache this, because it's only used by SDL_GetExeName, which _does_ cache it. + struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(SDL_FUNCTION); + + JNIEnv *env = Android_JNI_GetEnv(); + if (!LocalReferenceHolder_Init(&refs, env)) { + LocalReferenceHolder_Cleanup(&refs); + return NULL; + } + + // context = SDLActivity.getContext(); + jobject context = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetContext); + if (!context) { + SDL_SetError("Couldn't get Android context!"); + LocalReferenceHolder_Cleanup(&refs); + return NULL; + } + + // fileObj = context.getFilesDir(); + jmethodID mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, context), "getPackageName", "()Ljava/lang/String;"); + jstring jstr = (jstring)(*env)->CallObjectMethod(env, context, mid); + if (Android_JNI_ExceptionOccurred(false)) { + LocalReferenceHolder_Cleanup(&refs); + return NULL; + } + + const char *cstr = (*env)->GetStringUTFChars(env, jstr, NULL); + char *retval = cstr ? SDL_strdup(cstr) : NULL; + (*env)->ReleaseStringUTFChars(env, jstr, cstr); + + LocalReferenceHolder_Cleanup(&refs); + + return retval; +} + + bool SDL_IsAndroidTablet(void) { JNIEnv *env = Android_JNI_GetEnv(); diff --git a/third-party/SDL/src/core/android/SDL_android.h b/third-party/SDL/src/core/android/SDL_android.h index 9bb44eb49d..eedb661757 100644 --- a/third-party/SDL/src/core/android/SDL_android.h +++ b/third-party/SDL/src/core/android/SDL_android.h @@ -105,6 +105,7 @@ int Android_JNI_GetPowerInfo(int *plugged, int *charged, int *battery, int *seco // Joystick support void Android_JNI_PollInputDevices(void); void Android_JNI_JoystickSetLED(int device_id, int red, int green, int blue); +void Android_JNI_JoystickSetSensorsEnabled(int device_id, bool enabled); // Haptic support void Android_JNI_PollHapticDevices(void); @@ -152,6 +153,8 @@ int SDL_GetAndroidSDKVersion(void); bool SDL_IsAndroidTablet(void); bool SDL_IsAndroidTV(void); +char *SDL_GetAndroidPackageName(void); // this is a SDL_malloc'd string the caller will own. + // File Dialogs bool Android_JNI_OpenFileDialog(SDL_DialogFileCallback callback, void *userdata, const SDL_DialogFileFilter *filters, int nfilters, bool forwrite, diff --git a/third-party/SDL/src/core/linux/SDL_dbus.c b/third-party/SDL/src/core/linux/SDL_dbus.c index 2d083e8f29..cd7f77a100 100644 --- a/third-party/SDL/src/core/linux/SDL_dbus.c +++ b/third-party/SDL/src/core/linux/SDL_dbus.c @@ -22,6 +22,9 @@ #include "SDL_dbus.h" #include "../../stdlib/SDL_vacopy.h" +#include +#include + #ifdef SDL_USE_LIBDBUS // we never link directly to libdbus. #define SDL_DRIVER_DBUS_DYNAMIC "libdbus-1.so.3" @@ -471,10 +474,119 @@ static bool SDL_DBus_AppendDictWithKeyValue(DBusMessageIter *iterInit, const cha return SDL_DBus_AppendDictWithKeysAndValues(iterInit, keys, values, 1); } +bool SDL_DBus_OpenURI(const char *uri, const char *window_id, const char *activation_token) +{ + static const char *bus_name = "org.freedesktop.portal.Desktop"; + static const char *path = "/org/freedesktop/portal/desktop"; + static const char *interface = "org.freedesktop.portal.OpenURI"; + + if (!dbus.session_conn) { + /* We either lost connection to the session bus or were not able to + * load the D-Bus library at all. + */ + return false; + } + + DBusMessageIter iterInit; + DBusMessage *msg = NULL; + int fd = -1; + bool ret = false; + const bool has_file_scheme = SDL_strncasecmp(uri, "file:/", 6) == 0; + + // The OpenURI method can't open 'file://' URIs or local paths, so OpenFile must be used instead. + if (has_file_scheme || !SDL_IsURI(uri)) { + char *decoded_path = NULL; + + // Decode the path if it is a URI. + if (has_file_scheme) { + const size_t len = SDL_strlen(uri) + 1; + decoded_path = SDL_malloc(len); + if (!decoded_path) { + goto done; + } + if (SDL_URIToLocal(uri, decoded_path) < 0) { + SDL_free(decoded_path); + goto done; + } + uri = decoded_path; + } + fd = open(uri, O_RDWR | O_CLOEXEC); + SDL_free(decoded_path); + if (fd >= 0) { + msg = dbus.message_new_method_call(bus_name, path, interface, "OpenFile"); + } + } else { + msg = dbus.message_new_method_call(bus_name, path, interface, "OpenURI"); + } + if (!msg) { + goto done; + } + + dbus.message_iter_init_append(msg, &iterInit); + + if (!window_id) { + window_id = ""; + } + if (!dbus.message_iter_append_basic(&iterInit, DBUS_TYPE_STRING, &window_id)) { + goto done; + } + + if (fd >= 0) { + if (!dbus.message_iter_append_basic(&iterInit, DBUS_TYPE_UNIX_FD, &fd)) { + goto done; + } + } else { + if (!dbus.message_iter_append_basic(&iterInit, DBUS_TYPE_STRING, &uri)) { + goto done; + } + } + + if (activation_token) { + if (!SDL_DBus_AppendDictWithKeyValue(&iterInit, "activation_token", activation_token)) { + goto done; + } + } else { + // The array must be in the parameter list, even if empty. + DBusMessageIter iterArray; + if (!dbus.message_iter_open_container(&iterInit, DBUS_TYPE_ARRAY, "{sv}", &iterArray)) { + goto done; + } + if (!dbus.message_iter_close_container(&iterInit, &iterArray)) { + goto done; + } + } + + { + DBusMessage *reply = dbus.connection_send_with_reply_and_block(dbus.session_conn, msg, -1, NULL); + if (reply) { + ret = true; + dbus.message_unref(reply); + } + } + +done: + if (msg) { + dbus.message_unref(msg); + } + + // The file descriptor is duplicated by D-Bus, so it can be closed on this end. + if (fd >= 0) { + close(fd); + } + + return ret; +} + bool SDL_DBus_ScreensaverInhibit(bool inhibit) { + static bool interface_unavailable = false; const char *default_inhibit_reason = "Playing a game"; + // If the interface was previously queried and is unavailable, return false. + if (interface_unavailable) { + return false; + } + if ((inhibit && (screensaver_cookie != 0 || inhibit_handle)) || (!inhibit && (screensaver_cookie == 0 && !inhibit_handle))) { return true; } @@ -525,6 +637,8 @@ bool SDL_DBus_ScreensaverInhibit(bool inhibit) if (SDL_DBus_CallWithBasicReply(dbus.session_conn, &reply, msg, DBUS_TYPE_OBJECT_PATH, &reply_path)) { inhibit_handle = SDL_strdup(reply_path); result = true; + } else { + interface_unavailable = true; } SDL_DBus_FreeReply(&reply); dbus.message_unref(msg); @@ -551,6 +665,7 @@ bool SDL_DBus_ScreensaverInhibit(bool inhibit) if (!SDL_DBus_CallMethod(NULL, bus_name, path, interface, "Inhibit", DBUS_TYPE_STRING, &app, DBUS_TYPE_STRING, &reason, DBUS_TYPE_INVALID, DBUS_TYPE_UINT32, &screensaver_cookie, DBUS_TYPE_INVALID)) { + interface_unavailable = true; return false; } return (screensaver_cookie != 0); diff --git a/third-party/SDL/src/core/linux/SDL_dbus.h b/third-party/SDL/src/core/linux/SDL_dbus.h index e6f81b48ac..568dd74bb2 100644 --- a/third-party/SDL/src/core/linux/SDL_dbus.h +++ b/third-party/SDL/src/core/linux/SDL_dbus.h @@ -119,6 +119,8 @@ extern void SDL_DBus_FreeReply(DBusMessage **saved_reply); extern void SDL_DBus_ScreensaverTickle(void); extern bool SDL_DBus_ScreensaverInhibit(bool inhibit); +extern bool SDL_DBus_OpenURI(const char *uri, const char *window_id, const char *activation_token); + extern void SDL_DBus_PumpEvents(void); extern char *SDL_DBus_GetLocalMachineId(void); diff --git a/third-party/SDL/src/core/linux/SDL_fcitx.c b/third-party/SDL/src/core/linux/SDL_fcitx.c index d47b44e86f..a13b640db5 100644 --- a/third-party/SDL/src/core/linux/SDL_fcitx.c +++ b/third-party/SDL/src/core/linux/SDL_fcitx.c @@ -54,6 +54,7 @@ typedef struct FcitxClient static FcitxClient fcitx_client; +// !!! FIXME: should this just be dumped for src/core/unix's SDL_GetAppID()? static const char *GetAppName(void) { const char *exe_name = SDL_GetExeName(); diff --git a/third-party/SDL/src/core/unix/SDL_appid.c b/third-party/SDL/src/core/unix/SDL_appid.c index 3b85cbc876..95c5dc1a6b 100644 --- a/third-party/SDL/src/core/unix/SDL_appid.c +++ b/third-party/SDL/src/core/unix/SDL_appid.c @@ -24,39 +24,6 @@ #include "SDL_appid.h" #include -const char *SDL_GetExeName(void) -{ - static const char *proc_name = NULL; - - // TODO: Use a fallback if BSD has no mounted procfs (OpenBSD has no procfs at all) - if (!proc_name) { -#if defined(SDL_PLATFORM_LINUX) || defined(SDL_PLATFORM_FREEBSD) || defined (SDL_PLATFORM_NETBSD) || defined(SDL_PLATFORM_HURD) - static char linkfile[1024]; - int linksize; - -#if defined(SDL_PLATFORM_LINUX) || defined(SDL_PLATFORM_HURD) - const char *proc_path = "/proc/self/exe"; -#elif defined(SDL_PLATFORM_FREEBSD) - const char *proc_path = "/proc/curproc/file"; -#elif defined(SDL_PLATFORM_NETBSD) - const char *proc_path = "/proc/curproc/exe"; -#endif - linksize = readlink(proc_path, linkfile, sizeof(linkfile) - 1); - if (linksize > 0) { - linkfile[linksize] = '\0'; - proc_name = SDL_strrchr(linkfile, '/'); - if (proc_name) { - ++proc_name; - } else { - proc_name = linkfile; - } - } -#endif - } - - return proc_name; -} - const char *SDL_GetAppID(void) { const char *id_str = SDL_GetAppMetadataProperty(SDL_PROP_APP_METADATA_IDENTIFIER_STRING); @@ -73,3 +40,4 @@ const char *SDL_GetAppID(void) return id_str; } + diff --git a/third-party/SDL/src/core/unix/SDL_appid.h b/third-party/SDL/src/core/unix/SDL_appid.h index 9ed45c31bc..5f8eb932e6 100644 --- a/third-party/SDL/src/core/unix/SDL_appid.h +++ b/third-party/SDL/src/core/unix/SDL_appid.h @@ -24,7 +24,6 @@ freely, subject to the following restrictions: #ifndef SDL_appid_h_ #define SDL_appid_h_ -extern const char *SDL_GetExeName(void); extern const char *SDL_GetAppID(void); #endif // SDL_appid_h_ diff --git a/third-party/SDL/src/core/unix/SDL_fribidi.c b/third-party/SDL/src/core/unix/SDL_fribidi.c index 4a55133023..4599c974dd 100644 --- a/third-party/SDL/src/core/unix/SDL_fribidi.c +++ b/third-party/SDL/src/core/unix/SDL_fribidi.c @@ -84,8 +84,8 @@ char *SDL_FriBidi_Process(SDL_FriBidi *fribidi, char *utf8, ssize_t utf8_len, bo char *result; FriBidiStrIndex len; FriBidiLevel max_level; - FriBidiLevel start; - FriBidiLevel end; + FriBidiStrIndex start; + FriBidiStrIndex end; FriBidiParType direction; FriBidiParType str_direction; unsigned int i; diff --git a/third-party/SDL/src/core/unix/SDL_gtk.c b/third-party/SDL/src/core/unix/SDL_gtk.c index 6cf26fd47a..92e8a16ff9 100644 --- a/third-party/SDL/src/core/unix/SDL_gtk.c +++ b/third-party/SDL/src/core/unix/SDL_gtk.c @@ -78,7 +78,7 @@ static void QuitGtk(void) libgtk = NULL; } -static bool IsGtkInit() +static bool IsGtkInit(void) { return libgdk != NULL && libgtk != NULL; } diff --git a/third-party/SDL/src/core/windows/SDL_windows.c b/third-party/SDL/src/core/windows/SDL_windows.c index 562a3f3f77..d011db38ea 100644 --- a/third-party/SDL/src/core/windows/SDL_windows.c +++ b/third-party/SDL/src/core/windows/SDL_windows.c @@ -374,6 +374,11 @@ BOOL WIN_IsWindows8OrGreater(void) CHECKWINVER(TRUE, IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN8), LOBYTE(_WIN32_WINNT_WIN8), 0)); } +BOOL WIN_IsWindows81OrGreater(void) +{ + CHECKWINVER(TRUE, IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINBLUE), LOBYTE(_WIN32_WINNT_WINBLUE), 0)); +} + BOOL WIN_IsWindows11OrGreater(void) { return IsWindowsBuildVersionAtLeast(22000); @@ -725,4 +730,42 @@ const char *WIN_CheckDefaultArgcArgv(int *pargc, char ***pargv, void **pallocate return NULL; // no error string. } +char *WIN_GetModulePath(HMODULE handle) +{ + DWORD buflen = 128; + WCHAR *path = NULL; + DWORD len = 0; + + while (true) { + void *ptr = SDL_realloc(path, buflen * sizeof(WCHAR)); + if (!ptr) { + SDL_free(path); + return NULL; + } + + path = (WCHAR *)ptr; + + len = GetModuleFileNameW(handle, path, buflen); + // if it truncated, then len >= buflen - 1 + // if there was enough room (or failure), len < buflen - 1 + if (len < (buflen - 1)) { + break; + } + + // buffer too small? Try again. + buflen *= 2; + } + + char *retval = NULL; + if (len == 0) { + WIN_SetError("Couldn't locate module"); + } else { + retval = WIN_StringToUTF8W(path); + } + + SDL_free(path); + + return retval; +} + #endif // defined(SDL_PLATFORM_WINDOWS) diff --git a/third-party/SDL/src/core/windows/SDL_windows.h b/third-party/SDL/src/core/windows/SDL_windows.h index 5317e30d08..54f3f2c96c 100644 --- a/third-party/SDL/src/core/windows/SDL_windows.h +++ b/third-party/SDL/src/core/windows/SDL_windows.h @@ -183,6 +183,9 @@ extern BOOL WIN_IsWindows7OrGreater(void); // Returns true if we're running on Windows 8 and newer extern BOOL WIN_IsWindows8OrGreater(void); +// Returns true if we're running on Windows 8.1 and newer +extern BOOL WIN_IsWindows81OrGreater(void); + // Returns true if we're running on Windows 11 and newer extern BOOL WIN_IsWindows11OrGreater(void); @@ -210,7 +213,10 @@ extern SDL_AudioFormat SDL_WaveFormatExToSDLFormat(WAVEFORMATEX *waveformat); extern int WIN_WideCharToMultiByte(UINT CodePage, DWORD dwFlags, LPCWCH lpWideCharStr, int cchWideChar, LPSTR lpMultiByteStr, int cbMultiByte, LPCCH lpDefaultChar, LPBOOL lpUsedDefaultChar); // parse out command lines from OS if argv is NULL, otherwise pass through unchanged. `*pallocated` must be HeapFree'd by caller if not NULL on successful return. Returns NULL on success, error string on problems. -const char *WIN_CheckDefaultArgcArgv(int *pargc, char ***pargv, void **pallocated); +extern const char *WIN_CheckDefaultArgcArgv(int *pargc, char ***pargv, void **pallocated); + +// Does all the win32 tapdancing to make GetModuleFileName work. Returns a SDL_malloc'd UTF-8 string, or NULL on failure. +extern char *WIN_GetModulePath(HMODULE handle); // Ends C function definitions when using C++ #ifdef __cplusplus diff --git a/third-party/SDL/src/core/windows/version.rc b/third-party/SDL/src/core/windows/version.rc index 2726807423..d5b7ec7438 100644 --- a/third-party/SDL/src/core/windows/version.rc +++ b/third-party/SDL/src/core/windows/version.rc @@ -9,8 +9,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // VS_VERSION_INFO VERSIONINFO - FILEVERSION 3,4,4,0 - PRODUCTVERSION 3,4,4,0 + FILEVERSION 3,4,10,0 + PRODUCTVERSION 3,4,10,0 FILEFLAGSMASK 0x3fL FILEFLAGS 0x0L FILEOS 0x40004L @@ -23,12 +23,12 @@ BEGIN BEGIN VALUE "CompanyName", "\0" VALUE "FileDescription", "SDL\0" - VALUE "FileVersion", "3, 4, 4, 0\0" + VALUE "FileVersion", "3, 4, 10, 0\0" VALUE "InternalName", "SDL\0" VALUE "LegalCopyright", "Copyright (C) 2026 Sam Lantinga\0" VALUE "OriginalFilename", "SDL3.dll\0" VALUE "ProductName", "Simple DirectMedia Layer\0" - VALUE "ProductVersion", "3, 4, 4, 0\0" + VALUE "ProductVersion", "3, 4, 10, 0\0" END END BLOCK "VarFileInfo" diff --git a/third-party/SDL/src/dialog/SDL_dialog_utils.c b/third-party/SDL/src/dialog/SDL_dialog_utils.c index 826cc601c4..5af3b47689 100644 --- a/third-party/SDL/src/dialog/SDL_dialog_utils.c +++ b/third-party/SDL/src/dialog/SDL_dialog_utils.c @@ -27,7 +27,8 @@ char *convert_filters(const SDL_DialogFileFilter *filters, int nfilters, const char *separator, const char *suffix, const char *filt_prefix, const char *filt_separator, const char *filt_suffix, const char *ext_prefix, - const char *ext_separator, const char *ext_suffix) + const char *ext_separator, const char *ext_suffix, + bool anycase) { char *combined; char *new_combined; @@ -52,7 +53,7 @@ char *convert_filters(const SDL_DialogFileFilter *filters, int nfilters, converted = convert_filter(*f, ntf, filt_prefix, filt_separator, filt_suffix, ext_prefix, ext_separator, - ext_suffix); + ext_suffix, anycase); if (!converted) { SDL_free(combined); @@ -97,7 +98,8 @@ char *convert_filters(const SDL_DialogFileFilter *filters, int nfilters, char *convert_filter(SDL_DialogFileFilter filter, NameTransform ntf, const char *prefix, const char *separator, const char *suffix, const char *ext_prefix, - const char *ext_separator, const char *ext_suffix) + const char *ext_separator, const char *ext_suffix, + bool anycase) { char *converted; char *name_filtered; @@ -105,7 +107,7 @@ char *convert_filter(SDL_DialogFileFilter filter, NameTransform ntf, char *list; list = convert_ext_list(filter.pattern, ext_prefix, ext_separator, - ext_suffix); + ext_suffix, anycase); if (!list) { return NULL; @@ -145,7 +147,7 @@ char *convert_filter(SDL_DialogFileFilter filter, NameTransform ntf, } char *convert_ext_list(const char *list, const char *prefix, - const char *separator, const char *suffix) + const char *separator, const char *suffix, bool anycase) { char *converted; int semicolons; @@ -158,7 +160,7 @@ char *convert_ext_list(const char *list, const char *prefix, } total_length = - SDL_strlen(list) - semicolons // length of list contents + (SDL_strlen(list) - semicolons) * 4 // length of list contents (including "a" -> "[aA]") + semicolons * SDL_strlen(separator) // length of separators + SDL_strlen(prefix) + SDL_strlen(suffix) // length of prefix/suffix + 1; // terminating null byte @@ -179,9 +181,17 @@ char *convert_ext_list(const char *list, const char *prefix, SDL_strlcat(converted, "*", total_length); } else { for (const char *c = list; *c; c++) { - if ((*c >= 'a' && *c <= 'z') || (*c >= 'A' && *c <= 'Z') - || (*c >= '0' && *c <= '9') || *c == '-' || *c == '_' - || *c == '.') { + if (anycase && ((*c >= 'a' && *c <= 'z') || (*c >= 'A' && *c <= 'Z'))) { + char str[5]; + str[0] = '['; + str[1] = *c; + str[2] = *c ^ 0x20; // ASCII case toggle + str[3] = ']'; + str[4] = '\0'; + SDL_strlcat(converted, str, total_length); + } else if ((*c >= 'a' && *c <= 'z') || (*c >= 'A' && *c <= 'Z') + || (*c >= '0' && *c <= '9') || *c == '-' || *c == '_' + || *c == '.') { char str[2]; str[0] = *c; str[1] = '\0'; diff --git a/third-party/SDL/src/dialog/SDL_dialog_utils.h b/third-party/SDL/src/dialog/SDL_dialog_utils.h index 773fb967bc..c1ad9237d6 100644 --- a/third-party/SDL/src/dialog/SDL_dialog_utils.h +++ b/third-party/SDL/src/dialog/SDL_dialog_utils.h @@ -37,19 +37,21 @@ char *convert_filters(const SDL_DialogFileFilter *filters, int nfilters, const char *separator, const char *suffix, const char *filt_prefix, const char *filt_separator, const char *filt_suffix, const char *ext_prefix, - const char *ext_separator, const char *ext_suffix); + const char *ext_separator, const char *ext_suffix, + bool anycase); // Converts one filter into a single string. // [filter name][filter extension list] char *convert_filter(SDL_DialogFileFilter filter, NameTransform ntf, const char *prefix, const char *separator, const char *suffix, const char *ext_prefix, - const char *ext_separator, const char *ext_suffix); + const char *ext_separator, const char *ext_suffix, + bool anycase); // Converts the extension list of a filter into a single string. // [extension]{[extension]...} char *convert_ext_list(const char *list, const char *prefix, - const char *separator, const char *suffix); + const char *separator, const char *suffix, bool anycase); /* Must be used if convert_* functions aren't used */ // Returns an error message if there's a problem, NULL otherwise diff --git a/third-party/SDL/src/dialog/haiku/SDL_haikudialog.cc b/third-party/SDL/src/dialog/haiku/SDL_haikudialog.cc index 33bf87cef1..78881e47be 100644 --- a/third-party/SDL/src/dialog/haiku/SDL_haikudialog.cc +++ b/third-party/SDL/src/dialog/haiku/SDL_haikudialog.cc @@ -222,9 +222,7 @@ void SDL_SYS_ShowFileDialogWithProperties(SDL_FileDialogType type, SDL_DialogFil }; if (!SDL_InitBeApp()) { - char *err = SDL_strdup(SDL_GetError()); - SDL_SetError("Couldn't init Be app: %s", err); - SDL_free(err); + SDL_SetError("Couldn't init Be app: %s", SDL_GetError()); callback(userdata, NULL, -1); return; } diff --git a/third-party/SDL/src/dialog/unix/SDL_portaldialog.c b/third-party/SDL/src/dialog/unix/SDL_portaldialog.c index 7949a330d1..c0f957c8e2 100644 --- a/third-party/SDL/src/dialog/unix/SDL_portaldialog.c +++ b/third-party/SDL/src/dialog/unix/SDL_portaldialog.c @@ -86,10 +86,25 @@ static void DBus_AppendFilter(SDL_DBusContext *dbus, DBusMessageIter *parent, co dbus->message_iter_append_basic(&filter_entry, DBUS_TYPE_STRING, &filter.name); dbus->message_iter_open_container(&filter_entry, DBUS_TYPE_ARRAY, "(us)", &filter_array); - patterns = SDL_strdup(filter.pattern); + /* Copy the filter string, converting to a case-insensitive version. + * For example, for case-insensitive matching of '*.png', the pattern '*.[pP][nN][gG]' is used. + */ + const size_t len = SDL_strlen(filter.pattern) + 1; + patterns = SDL_malloc(len * 4); // Single characters may be expanded to 4 characters. if (!patterns) { goto cleanup; } + for (size_t i = 0, p = 0; i < len; ++i) { + if ((filter.pattern[i] >= 'a' && filter.pattern[i] <= 'z') || + (filter.pattern[i] >= 'A' && filter.pattern[i] <= 'Z')) { + patterns[p++] = '['; + patterns[p++] = SDL_tolower(filter.pattern[i]); + patterns[p++] = SDL_toupper(filter.pattern[i]); + patterns[p++] = ']'; + } else { + patterns[p++] = filter.pattern[i]; + } + } pattern = SDL_strtok_r(patterns, ";", &state); while (pattern) { diff --git a/third-party/SDL/src/dialog/unix/SDL_zenitydialog.c b/third-party/SDL/src/dialog/unix/SDL_zenitydialog.c index e34434feb5..91fb3cd9d8 100644 --- a/third-party/SDL/src/dialog/unix/SDL_zenitydialog.c +++ b/third-party/SDL/src/dialog/unix/SDL_zenitydialog.c @@ -192,7 +192,7 @@ static zenityArgs *create_zenity_args(SDL_FileDialogType type, SDL_DialogFileCal char *filter_str = convert_filter(filters[i], zenity_clean_name, "--file-filter=", " | ", "", - "*.", " *.", ""); + "*.", " *.", "", true); if (!filter_str) { while (i--) { diff --git a/third-party/SDL/src/dialog/unix/SDL_zenitymessagebox.h b/third-party/SDL/src/dialog/unix/SDL_zenitymessagebox.h index a06c46208c..1e08ae99ff 100644 --- a/third-party/SDL/src/dialog/unix/SDL_zenitymessagebox.h +++ b/third-party/SDL/src/dialog/unix/SDL_zenitymessagebox.h @@ -25,4 +25,4 @@ extern bool SDL_Zenity_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonID); extern bool SDL_get_zenity_version(int *major, int *minor); -#endif // SDL_waylandmessagebox_h_ +#endif // SDL_zenitymessagebox_h_ diff --git a/third-party/SDL/src/dialog/windows/SDL_windowsdialog.c b/third-party/SDL/src/dialog/windows/SDL_windowsdialog.c index 24a1ecaef8..660665c91a 100644 --- a/third-party/SDL/src/dialog/windows/SDL_windowsdialog.c +++ b/third-party/SDL/src/dialog/windows/SDL_windowsdialog.c @@ -1143,7 +1143,7 @@ wchar_t *win_get_filters(const SDL_DialogFileFilter *filters, int nfilters) // suffix needs two null bytes in case the filter list is empty char *filterlist = convert_filters(filters, nfilters, clear_filt_names, "", "", "\x01\x01", "", "\x01", - "\x01", "*.", ";*.", ""); + "\x01", "*.", ";*.", "", false); if (!filterlist) { return NULL; diff --git a/third-party/SDL/src/dynapi/SDL_dynapi_procs.h b/third-party/SDL/src/dynapi/SDL_dynapi_procs.h index 9f28b68a50..78a896a544 100644 --- a/third-party/SDL/src/dynapi/SDL_dynapi_procs.h +++ b/third-party/SDL/src/dynapi/SDL_dynapi_procs.h @@ -830,7 +830,7 @@ SDL_DYNAPI_PROC(bool,SDL_SetAudioStreamInputChannelMap,(SDL_AudioStream *a, cons SDL_DYNAPI_PROC(bool,SDL_SetAudioStreamOutputChannelMap,(SDL_AudioStream *a, const int *b, int c),(a,b,c),return) SDL_DYNAPI_PROC(bool,SDL_SetAudioStreamPutCallback,(SDL_AudioStream *a, SDL_AudioStreamCallback b, void *c),(a,b,c),return) SDL_DYNAPI_PROC(bool,SDL_SetBooleanProperty,(SDL_PropertiesID a, const char *b, bool c),(a,b,c),return) -SDL_DYNAPI_PROC(bool,SDL_SetClipboardData,(SDL_ClipboardDataCallback a, SDL_ClipboardCleanupCallback b, void *c, const char **d, size_t e),(a,b,c,d,e),return) +SDL_DYNAPI_PROC(bool,SDL_SetClipboardData,(SDL_ClipboardDataCallback a, SDL_ClipboardCleanupCallback b, void *c, const char *const *d, size_t e),(a,b,c,d,e),return) SDL_DYNAPI_PROC(bool,SDL_SetClipboardText,(const char *a),(a),return) SDL_DYNAPI_PROC(bool,SDL_SetCurrentThreadPriority,(SDL_ThreadPriority a),(a),return) SDL_DYNAPI_PROC(bool,SDL_SetCursor,(SDL_Cursor *a),(a),return) diff --git a/third-party/SDL/src/dynapi/gendynapi.py b/third-party/SDL/src/dynapi/gendynapi.py old mode 100644 new mode 100755 diff --git a/third-party/SDL/src/events/SDL_keyboard.c b/third-party/SDL/src/events/SDL_keyboard.c index 0945339e44..f6969e37ed 100644 --- a/third-party/SDL/src/events/SDL_keyboard.c +++ b/third-party/SDL/src/events/SDL_keyboard.c @@ -201,14 +201,22 @@ SDL_KeyboardID *SDL_GetKeyboards(int *count) const char *SDL_GetKeyboardNameForID(SDL_KeyboardID instance_id) { const char *name = NULL; - if (!SDL_FindInHashTable(SDL_keyboard_names, (const void *)(uintptr_t)instance_id, (const void **)&name)) { - SDL_SetError("Keyboard %" SDL_PRIu32 " not found", instance_id); - return NULL; - } - if (!name) { - // SDL_strdup() failed during insert - SDL_OutOfMemory(); - return NULL; + + switch (instance_id) { + case SDL_GLOBAL_KEYBOARD_ID: + name = "Keyboard"; + break; + default: + if (!SDL_FindInHashTable(SDL_keyboard_names, (const void *)(uintptr_t)instance_id, (const void **)&name)) { + SDL_SetError("Keyboard %" SDL_PRIu32 " not found", instance_id); + return NULL; + } + if (!name) { + // SDL_strdup() failed during insert + SDL_OutOfMemory(); + return NULL; + } + break; } return name; } diff --git a/third-party/SDL/src/events/SDL_mouse.c b/third-party/SDL/src/events/SDL_mouse.c index 80bd6495f2..eaf1f2cdfd 100644 --- a/third-party/SDL/src/events/SDL_mouse.c +++ b/third-party/SDL/src/events/SDL_mouse.c @@ -188,25 +188,6 @@ static void SDLCALL SDL_PenMouseEventsChanged(void *userdata, const char *name, mouse->pen_mouse_events = SDL_GetStringBoolean(hint, true); } -static void SDLCALL SDL_PenTouchEventsChanged(void *userdata, const char *name, const char *oldValue, const char *hint) -{ - SDL_Mouse *mouse = (SDL_Mouse *)userdata; - - mouse->pen_touch_events = SDL_GetStringBoolean(hint, true); - - if (mouse->pen_touch_events) { - if (!mouse->added_pen_touch_device) { - SDL_AddTouch(SDL_PEN_TOUCHID, SDL_TOUCH_DEVICE_DIRECT, "pen_input"); - mouse->added_pen_touch_device = true; - } - } else { - if (mouse->added_pen_touch_device) { - SDL_DelTouch(SDL_PEN_TOUCHID); - mouse->added_pen_touch_device = false; - } - } -} - static void SDLCALL SDL_MouseAutoCaptureChanged(void *userdata, const char *name, const char *oldValue, const char *hint) { SDL_Mouse *mouse = (SDL_Mouse *)userdata; @@ -289,9 +270,6 @@ bool SDL_PreInitMouse(void) SDL_AddHintCallback(SDL_HINT_PEN_MOUSE_EVENTS, SDL_PenMouseEventsChanged, mouse); - SDL_AddHintCallback(SDL_HINT_PEN_TOUCH_EVENTS, - SDL_PenTouchEventsChanged, mouse); - SDL_AddHintCallback(SDL_HINT_MOUSE_AUTO_CAPTURE, SDL_MouseAutoCaptureChanged, mouse); @@ -444,14 +422,38 @@ SDL_MouseID *SDL_GetMice(int *count) const char *SDL_GetMouseNameForID(SDL_MouseID instance_id) { const char *name = NULL; - if (!SDL_FindInHashTable(SDL_mouse_names, (const void *)(uintptr_t)instance_id, (const void **)&name)) { - SDL_SetError("Mouse %" SDL_PRIu32 " not found", instance_id); - return NULL; - } - if (!name) { - // SDL_strdup() failed during insert - SDL_OutOfMemory(); - return NULL; + + switch (instance_id) { + case SDL_GLOBAL_MOUSE_ID: + name = "Mouse"; + break; + case SDL_TOUCH_MOUSEID: + // We can't tell which touch device it was, just use the first one + { + SDL_TouchID *devices = SDL_GetTouchDevices(NULL); + if (devices) { + name = SDL_GetTouchDeviceName(devices[0]); + SDL_free(devices); + } + } + if (!name) { + name = "Touch"; + } + break; + case SDL_PEN_MOUSEID: + name = "Pen"; + break; + default: + if (!SDL_FindInHashTable(SDL_mouse_names, (const void *)(uintptr_t)instance_id, (const void **)&name)) { + SDL_SetError("Mouse %" SDL_PRIu32 " not found", instance_id); + return NULL; + } + if (!name) { + // SDL_strdup() failed during insert + SDL_OutOfMemory(); + return NULL; + } + break; } return name; } @@ -1156,9 +1158,6 @@ void SDL_QuitMouse(void) SDL_RemoveHintCallback(SDL_HINT_PEN_MOUSE_EVENTS, SDL_PenMouseEventsChanged, mouse); - SDL_RemoveHintCallback(SDL_HINT_PEN_TOUCH_EVENTS, - SDL_PenTouchEventsChanged, mouse); - SDL_RemoveHintCallback(SDL_HINT_MOUSE_AUTO_CAPTURE, SDL_MouseAutoCaptureChanged, mouse); diff --git a/third-party/SDL/src/events/SDL_pen.c b/third-party/SDL/src/events/SDL_pen.c index 61d41e1fea..d70542b9f9 100644 --- a/third-party/SDL/src/events/SDL_pen.c +++ b/third-party/SDL/src/events/SDL_pen.c @@ -93,6 +93,33 @@ SDL_PenID SDL_FindPenByCallback(bool (*callback)(void *handle, void *userdata), return result; } +static void UpdateTouchEmulationDevicePresence(void) +{ + SDL_Mouse *mouse = SDL_GetMouse(); + + SDL_LockRWLockForReading(pen_device_rwlock); + bool has_pen = (pen_device_count != 0); + SDL_UnlockRWLock(pen_device_rwlock); + + if (!mouse->pen_touch_events || !has_pen) { + if (mouse->added_pen_touch_device) { + SDL_DelTouch(SDL_PEN_TOUCHID); + mouse->added_pen_touch_device = false; + } + } else if (!mouse->added_pen_touch_device) { + SDL_AddTouch(SDL_PEN_TOUCHID, SDL_TOUCH_DEVICE_DIRECT, "pen_input"); + mouse->added_pen_touch_device = true; + } +} + +static void SDLCALL SDL_PenTouchEventsChanged(void *userdata, const char *name, const char *oldValue, const char *hint) +{ + SDL_Mouse *mouse = SDL_GetMouse(); + + mouse->pen_touch_events = SDL_GetStringBoolean(hint, true); + + UpdateTouchEmulationDevicePresence(); +} // public API ... @@ -106,11 +133,18 @@ bool SDL_InitPen(void) if (!pen_device_rwlock) { return false; } + + SDL_AddHintCallback(SDL_HINT_PEN_TOUCH_EVENTS, + SDL_PenTouchEventsChanged, NULL); + return true; } void SDL_QuitPen(void) { + SDL_RemoveHintCallback(SDL_HINT_PEN_TOUCH_EVENTS, + SDL_PenTouchEventsChanged, NULL); + SDL_RemoveAllPenDevices(NULL, NULL); SDL_DestroyRWLock(pen_device_rwlock); pen_device_rwlock = NULL; @@ -251,6 +285,8 @@ SDL_PenID SDL_AddPenDevice(Uint64 timestamp, const char *name, SDL_Window *windo SDL_free(namecpy); } + UpdateTouchEmulationDevicePresence(); + if (result && in_proximity) { SDL_SendPenProximity(timestamp, result, window, true, true); } @@ -291,6 +327,8 @@ void SDL_RemovePenDevice(Uint64 timestamp, SDL_Window *window, SDL_PenID instanc } } SDL_UnlockRWLock(pen_device_rwlock); + + UpdateTouchEmulationDevicePresence(); } // This presumably is happening during video quit, so we don't send PROXIMITY_OUT events here. diff --git a/third-party/SDL/src/filesystem/SDL_filesystem.c b/third-party/SDL/src/filesystem/SDL_filesystem.c index dca0da5c99..81c0b41e34 100644 --- a/third-party/SDL/src/filesystem/SDL_filesystem.c +++ b/third-party/SDL/src/filesystem/SDL_filesystem.c @@ -510,6 +510,14 @@ const char *SDL_GetUserFolder(SDL_Folder folder) return CachedUserFolders[idx]; } +static char *CachedExeName = NULL; +const char *SDL_GetExeName(void) +{ + if (!CachedExeName) { + CachedExeName = SDL_SYS_GetExeName(); + } + return CachedExeName; +} char *SDL_GetPrefPath(const char *org, const char *app) { @@ -537,10 +545,12 @@ void SDL_InitFilesystem(void) void SDL_QuitFilesystem(void) { - if (CachedBasePath) { - SDL_free(CachedBasePath); - CachedBasePath = NULL; - } + SDL_free(CachedBasePath); + CachedBasePath = NULL; + + SDL_free(CachedExeName); + CachedExeName = NULL; + for (int i = 0; i < SDL_arraysize(CachedUserFolders); i++) { if (CachedUserFolders[i]) { SDL_free(CachedUserFolders[i]); diff --git a/third-party/SDL/src/filesystem/SDL_sysfilesystem.h b/third-party/SDL/src/filesystem/SDL_sysfilesystem.h index 660c712480..4f1cf1d5cc 100644 --- a/third-party/SDL/src/filesystem/SDL_sysfilesystem.h +++ b/third-party/SDL/src/filesystem/SDL_sysfilesystem.h @@ -24,6 +24,7 @@ // return a string that we can SDL_free(). It will be cached at the higher level. extern char *SDL_SYS_GetBasePath(void); +extern char *SDL_SYS_GetExeName(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); diff --git a/third-party/SDL/src/filesystem/android/SDL_sysfilesystem.c b/third-party/SDL/src/filesystem/android/SDL_sysfilesystem.c index 7eddd28774..89cd0ce1ed 100644 --- a/third-party/SDL/src/filesystem/android/SDL_sysfilesystem.c +++ b/third-party/SDL/src/filesystem/android/SDL_sysfilesystem.c @@ -26,14 +26,18 @@ // System dependent filesystem routines #include "../SDL_sysfilesystem.h" - -#include +#include "../../core/android/SDL_android.h" char *SDL_SYS_GetBasePath(void) { return SDL_strdup("./"); } +char *SDL_SYS_GetExeName(void) +{ + return SDL_GetAndroidPackageName(); +} + char *SDL_SYS_GetPrefPath(const char *org, const char *app) { const char *path = SDL_GetAndroidInternalStoragePath(); diff --git a/third-party/SDL/src/filesystem/cocoa/SDL_sysfilesystem.m b/third-party/SDL/src/filesystem/cocoa/SDL_sysfilesystem.m index 26ee7cc102..4cd9a563c7 100644 --- a/third-party/SDL/src/filesystem/cocoa/SDL_sysfilesystem.m +++ b/third-party/SDL/src/filesystem/cocoa/SDL_sysfilesystem.m @@ -47,7 +47,7 @@ char *SDL_SYS_GetBasePath(void) } else if (SDL_strcasecmp(baseType, "parent") == 0) { base = [[[bundle bundlePath] stringByDeletingLastPathComponent] fileSystemRepresentation]; } else { - // this returns the exedir for non-bundled and the resourceDir for bundled apps + // this returns the exedir for non-bundled and the resourceDir for bundled apps base = [[bundle resourcePath] fileSystemRepresentation]; } @@ -63,6 +63,28 @@ char *SDL_SYS_GetBasePath(void) } } +char *SDL_SYS_GetExeName(void) +{ + @autoreleasepool { + NSBundle *bundle = [NSBundle mainBundle]; + const char *name = [[[bundle infoDictionary] objectForKey:@"CFBundleIdentifier"] UTF8String]; + if (!name) { + name = [[[bundle infoDictionary] objectForKey:@"CFBundleDisplayName"] UTF8String]; + if (!name) { + name = [[[bundle infoDictionary] objectForKey:@"CFBundleName"] UTF8String]; + if (!name) { + name = [[[bundle infoDictionary] objectForKey:@"CFBundleExecutable"] UTF8String]; + if (!name) { + name = [[[NSProcessInfo processInfo] processName] UTF8String]; // oh well. + } + } + } + } + + return name ? SDL_strdup(name) : NULL; + } +} + char *SDL_SYS_GetPrefPath(const char *org, const char *app) { @autoreleasepool { diff --git a/third-party/SDL/src/filesystem/dummy/SDL_sysfilesystem.c b/third-party/SDL/src/filesystem/dummy/SDL_sysfilesystem.c index 53bc50f7f9..2df8ec4f2c 100644 --- a/third-party/SDL/src/filesystem/dummy/SDL_sysfilesystem.c +++ b/third-party/SDL/src/filesystem/dummy/SDL_sysfilesystem.c @@ -33,6 +33,12 @@ char *SDL_SYS_GetBasePath(void) return NULL; } +char *SDL_SYS_GetExeName(void) +{ + SDL_Unsupported(); + return NULL; +} + char *SDL_SYS_GetPrefPath(const char *org, const char *app) { SDL_Unsupported(); diff --git a/third-party/SDL/src/filesystem/emscripten/SDL_sysfilesystem.c b/third-party/SDL/src/filesystem/emscripten/SDL_sysfilesystem.c index 0d21cd2698..54dcf08256 100644 --- a/third-party/SDL/src/filesystem/emscripten/SDL_sysfilesystem.c +++ b/third-party/SDL/src/filesystem/emscripten/SDL_sysfilesystem.c @@ -37,6 +37,11 @@ char *SDL_SYS_GetBasePath(void) return SDL_strdup("/"); } +char *SDL_SYS_GetExeName(void) +{ + return NULL; // no EXE name on this system. +} + char *SDL_SYS_GetPrefPath(const char *org, const char *app) { #ifdef SDL_EMSCRIPTEN_PERSISTENT_PATH_STRING diff --git a/third-party/SDL/src/filesystem/gdk/SDL_sysfilesystem.cpp b/third-party/SDL/src/filesystem/gdk/SDL_sysfilesystem.cpp index 83abfada54..3c66550e1f 100644 --- a/third-party/SDL/src/filesystem/gdk/SDL_sysfilesystem.cpp +++ b/third-party/SDL/src/filesystem/gdk/SDL_sysfilesystem.cpp @@ -33,53 +33,34 @@ extern "C" { #include #include -char * -SDL_SYS_GetBasePath(void) +char *SDL_SYS_GetBasePath(void) { - /* NOTE: This function is a UTF8 version of the Win32 SDL_GetBasePath()! - * The GDK actually _recommends_ the 'A' functions over the 'W' functions :o - */ - DWORD buflen = 128; - CHAR *path = NULL; - DWORD len = 0; - int i; - - while (true) { - void *ptr = SDL_realloc(path, buflen * sizeof(CHAR)); - if (!ptr) { - SDL_free(path); - return NULL; - } - - path = (CHAR *)ptr; - - len = GetModuleFileNameA(NULL, path, buflen); - // if it truncated, then len >= buflen - 1 - // if there was enough room (or failure), len < buflen - 1 - if (len < buflen - 1) { - break; - } - - // buffer too small? Try again. - buflen *= 2; + char *path = WIN_GetModulePath(NULL); // look up full path of the current process's EXE file. + if (!path) { + return NULL; // error message was already set. } - if (len == 0) { - SDL_free(path); - WIN_SetError("Couldn't locate our .exe"); - return NULL; + char *ptr = SDL_strrchr(path, '\\'); + SDL_assert(ptr != NULL); // Should have been an absolute path. + + ptr[1] = '\0'; // chop off filename, leave '\\'. + + ptr = (char *) SDL_realloc(path, ((size_t) (ptr - path)) + 2); // try to shrink this allocation down a little. + return ptr ? ptr : path; // return shrunk buffer if shrink worked out, unchanged original buffer if not. +} + +char *SDL_SYS_GetExeName(void) +{ + char *path = WIN_GetModulePath(NULL); // look up full path of the current process's EXE file. + if (!path) { + return NULL; // error message was already set. } - for (i = len - 1; i > 0; i--) { - if (path[i] == '\\') { - break; - } - } - - SDL_assert(i > 0); // Should have been an absolute path. - path[i + 1] = '\0'; // chop off filename. - - return path; + char *ptr = SDL_strrchr(path, '\\'); + const size_t slen = SDL_strlen(ptr); // counts null terminator because we're still sitting on path separator. + SDL_memmove(path, ptr + 1, slen); // move filename string to start of SDL_realloc'd region. + ptr = (char *) SDL_realloc(path, slen); // try to shrink this allocation down a little. + return ptr ? ptr : path; // return shrunk buffer if shrink worked out, unchanged original buffer if not. } char *SDL_SYS_GetPrefPath(const char *org, const char *app) diff --git a/third-party/SDL/src/filesystem/haiku/SDL_sysfilesystem.cc b/third-party/SDL/src/filesystem/haiku/SDL_sysfilesystem.cc index 380e05ce1e..7a051135ed 100644 --- a/third-party/SDL/src/filesystem/haiku/SDL_sysfilesystem.cc +++ b/third-party/SDL/src/filesystem/haiku/SDL_sysfilesystem.cc @@ -44,6 +44,7 @@ char *SDL_SYS_GetBasePath(void) return NULL; } + // !!! FIXME: if find_path promises an absolute path, can we dump this and just do SDL_strrchr(name, '/')? BEntry entry(name, true); BPath path; status_t rc = entry.GetPath(&path); // (path) now has binary's path. @@ -64,6 +65,15 @@ char *SDL_SYS_GetBasePath(void) return result; } +char *SDL_SYS_GetExeName(void) +{ + char name[MAXPATHLEN]; + if (find_path(B_APP_IMAGE_SYMBOL, B_FIND_PATH_IMAGE_PATH, NULL, name, sizeof(name)) != B_OK) { + return NULL; + } + char *ptr = SDL_strrchr(name, '/'); + return SDL_strdup(ptr ? ptr + 1 : name); +} char *SDL_SYS_GetPrefPath(const char *org, const char *app) { diff --git a/third-party/SDL/src/filesystem/n3ds/SDL_sysfilesystem.c b/third-party/SDL/src/filesystem/n3ds/SDL_sysfilesystem.c index 66dba9ddaf..8a129c7194 100644 --- a/third-party/SDL/src/filesystem/n3ds/SDL_sysfilesystem.c +++ b/third-party/SDL/src/filesystem/n3ds/SDL_sysfilesystem.c @@ -40,6 +40,11 @@ char *SDL_SYS_GetBasePath(void) return base_path; } +char *SDL_SYS_GetExeName(void) +{ + return NULL; // there isn't an "exe name" on this platform. +} + char *SDL_SYS_GetPrefPath(const char *org, const char *app) { char *pref_path = NULL; diff --git a/third-party/SDL/src/filesystem/ngage/SDL_sysfilesystem.c b/third-party/SDL/src/filesystem/ngage/SDL_sysfilesystem.c index a7d5ea8959..8ce3ee1512 100644 --- a/third-party/SDL/src/filesystem/ngage/SDL_sysfilesystem.c +++ b/third-party/SDL/src/filesystem/ngage/SDL_sysfilesystem.c @@ -21,6 +21,7 @@ #include "SDL_internal.h" extern void NGAGE_GetAppPath(char *path); +extern void NGAGE_GetExeName(char *path); char *SDL_SYS_GetBasePath(void) { @@ -30,6 +31,13 @@ char *SDL_SYS_GetBasePath(void) return base_path; } +char *SDL_SYS_GetExeName(void) +{ + char exe_name[512]; + NGAGE_GetExeName(exe_name); + return SDL_strdup(exe_name); +} + char *SDL_SYS_GetPrefPath(const char *org, const char *app) { char *pref_path = NULL; diff --git a/third-party/SDL/src/filesystem/ngage/SDL_sysfilesystem.cpp b/third-party/SDL/src/filesystem/ngage/SDL_sysfilesystem.cpp index 622c82e465..415fe55c4d 100644 --- a/third-party/SDL/src/filesystem/ngage/SDL_sysfilesystem.cpp +++ b/third-party/SDL/src/filesystem/ngage/SDL_sysfilesystem.cpp @@ -63,6 +63,23 @@ void NGAGE_GetAppPath(char *path) } } +void NGAGE_GetExeName(char *path) +{ + TBuf<512> aPath; + + TFileName fullExePath = RProcess().FileName(); + + TParsePtrC parser(fullExePath); + aPath.Copy(parser.NameAndExt()); + + TBuf8<512> utf8Path; // Temporary buffer for UTF-8 data. + CnvUtfConverter::ConvertFromUnicodeToUtf8(utf8Path, aPath); + + // Copy UTF-8 data to the provided char* buffer. + strncpy(path, (const char *)utf8Path.Ptr(), utf8Path.Length()); + path[utf8Path.Length()] = '\0'; +} + #ifdef __cplusplus } #endif diff --git a/third-party/SDL/src/filesystem/posix/SDL_sysfsops.c b/third-party/SDL/src/filesystem/posix/SDL_sysfsops.c index 1ab9f80301..fd42876215 100644 --- a/third-party/SDL/src/filesystem/posix/SDL_sysfsops.c +++ b/third-party/SDL/src/filesystem/posix/SDL_sysfsops.c @@ -74,7 +74,7 @@ bool SDL_SYS_EnumerateDirectory(const char *path, SDL_EnumerateDirectoryCallback #endif char *pathwithsep = NULL; - int pathwithseplen = SDL_asprintf(&pathwithsep, "%s/", apath ? apath : path); + int pathwithseplen = SDL_asprintf(&pathwithsep, "%s%s", apath ? apath : path, (apath ? *apath : *path) ? "/" : ""); const size_t extralen = apath ? (SDL_strlen(apath) - SDL_strlen(path)) : 0; SDL_free(apath); if ((pathwithseplen == -1) || (!pathwithsep)) { diff --git a/third-party/SDL/src/filesystem/ps2/SDL_sysfilesystem.c b/third-party/SDL/src/filesystem/ps2/SDL_sysfilesystem.c index 87b44c9bf0..ddc3c68399 100644 --- a/third-party/SDL/src/filesystem/ps2/SDL_sysfilesystem.c +++ b/third-party/SDL/src/filesystem/ps2/SDL_sysfilesystem.c @@ -46,6 +46,11 @@ char *SDL_SYS_GetBasePath(void) return result; } +char *SDL_SYS_GetExeName(void) +{ + return NULL; // no EXE name on this system. +} + // Do a recursive mkdir of parents folders static void recursive_mkdir(const char *dir) { diff --git a/third-party/SDL/src/filesystem/psp/SDL_sysfilesystem.c b/third-party/SDL/src/filesystem/psp/SDL_sysfilesystem.c index 18cd9d95ed..dd828cf523 100644 --- a/third-party/SDL/src/filesystem/psp/SDL_sysfilesystem.c +++ b/third-party/SDL/src/filesystem/psp/SDL_sysfilesystem.c @@ -46,6 +46,11 @@ char *SDL_SYS_GetBasePath(void) return result; } +char *SDL_SYS_GetExeName(void) +{ + return NULL; // no EXE name on this system. +} + char *SDL_SYS_GetPrefPath(const char *org, const char *app) { char *result = NULL; diff --git a/third-party/SDL/src/filesystem/riscos/SDL_sysfilesystem.c b/third-party/SDL/src/filesystem/riscos/SDL_sysfilesystem.c index d52b22ad43..b616864122 100644 --- a/third-party/SDL/src/filesystem/riscos/SDL_sysfilesystem.c +++ b/third-party/SDL/src/filesystem/riscos/SDL_sysfilesystem.c @@ -152,6 +152,29 @@ char *SDL_SYS_GetBasePath(void) return result; } +char *SDL_SYS_GetExeName(void) +{ + _kernel_swi_regs regs; + _kernel_oserror *error; + char *canon, *ptr, *retval; + + error = _kernel_swi(OS_GetEnv, ®s, ®s); + if (error) { + return NULL; + } + + canon = canonicalisePath((const char *)regs.r[0], "Run$Path"); + if (!canon) { + return NULL; + } + + // find filename. + ptr = SDL_strrchr(canon, '.'); + retval = SDL_strdup(ptr ? ptr + 1 : canon); + SDL_free(canon); + return retval; +} + char *SDL_SYS_GetPrefPath(const char *org, const char *app) { char *canon, *dir, *result; diff --git a/third-party/SDL/src/filesystem/unix/SDL_sysfilesystem.c b/third-party/SDL/src/filesystem/unix/SDL_sysfilesystem.c index 9edbf6dc0b..a23d67b362 100644 --- a/third-party/SDL/src/filesystem/unix/SDL_sysfilesystem.c +++ b/third-party/SDL/src/filesystem/unix/SDL_sysfilesystem.c @@ -122,7 +122,7 @@ static char *search_path_for_binary(const char *bin) } #endif -char *SDL_SYS_GetBasePath(void) +static char *GetExePath(void) { char *result = NULL; @@ -235,27 +235,42 @@ char *SDL_SYS_GetBasePath(void) /* If we had access to argv[0] here, we could check it for a path, or troll through $PATH looking for it, too. */ - if (result) { // chop off filename. - char *ptr = SDL_strrchr(result, '/'); - if (ptr) { - *(ptr + 1) = '\0'; - } else { // shouldn't happen, but just in case... - SDL_free(result); - result = NULL; - } - } - - if (result) { - // try to shrink buffer... - char *ptr = (char *)SDL_realloc(result, SDL_strlen(result) + 1); - if (ptr) { - result = ptr; // oh well if it failed. - } - } - return result; } +char *SDL_SYS_GetBasePath(void) +{ + char *path = GetExePath(); + if (!path) { + return NULL; + } + + char *ptr = SDL_strrchr(path, '/'); + SDL_assert(ptr != NULL); // Should have been an absolute path. + + ptr[1] = '\0'; // chop off filename, leave '/'. + + ptr = (char *) SDL_realloc(path, ((size_t) (ptr - path)) + 2); // try to shrink this allocation down a little. + return ptr ? ptr : path; // return shrunk buffer if shrink worked out, unchanged original buffer if not. +} + + +char *SDL_SYS_GetExeName(void) +{ + char *path = GetExePath(); + if (!path) { + return NULL; + } + + char *ptr = SDL_strrchr(path, '/'); + SDL_assert(ptr != NULL); // Should have been an absolute path. + const size_t slen = SDL_strlen(ptr); // counts null terminator because we're still sitting on path separator. + SDL_memmove(path, ptr + 1, slen); // move filename string to start of SDL_realloc'd region. + ptr = (char *) SDL_realloc(path, slen); // try to shrink this allocation down a little. + return ptr ? ptr : path; // return shrunk buffer if shrink worked out, unchanged original buffer if not. +} + + char *SDL_SYS_GetPrefPath(const char *org, const char *app) { /* diff --git a/third-party/SDL/src/filesystem/vita/SDL_sysfilesystem.c b/third-party/SDL/src/filesystem/vita/SDL_sysfilesystem.c index a23fcdcad6..4910000acb 100644 --- a/third-party/SDL/src/filesystem/vita/SDL_sysfilesystem.c +++ b/third-party/SDL/src/filesystem/vita/SDL_sysfilesystem.c @@ -41,6 +41,11 @@ char *SDL_SYS_GetBasePath(void) return SDL_strdup("app0:/"); } +char *SDL_SYS_GetExeName(void) +{ + return NULL; // no EXE name on this system. +} + char *SDL_SYS_GetPrefPath(const char *org, const char *app) { const char *envr = "ux0:/data/"; diff --git a/third-party/SDL/src/filesystem/windows/SDL_sysfilesystem.c b/third-party/SDL/src/filesystem/windows/SDL_sysfilesystem.c index f2c59f4f2f..dd836e25bc 100644 --- a/third-party/SDL/src/filesystem/windows/SDL_sysfilesystem.c +++ b/third-party/SDL/src/filesystem/windows/SDL_sysfilesystem.c @@ -45,51 +45,32 @@ DEFINE_GUID(SDL_FOLDERID_Videos, 0x18989B1D, 0x99B5, 0x455B, 0x84, 0x1C, 0xAB, 0 char *SDL_SYS_GetBasePath(void) { - DWORD buflen = 128; - WCHAR *path = NULL; - char *result = NULL; - DWORD len = 0; - int i; - - while (true) { - void *ptr = SDL_realloc(path, buflen * sizeof(WCHAR)); - if (!ptr) { - SDL_free(path); - return NULL; - } - - path = (WCHAR *)ptr; - - len = GetModuleFileNameW(NULL, path, buflen); - // if it truncated, then len >= buflen - 1 - // if there was enough room (or failure), len < buflen - 1 - if (len < buflen - 1) { - break; - } - - // buffer too small? Try again. - buflen *= 2; + char *path = WIN_GetModulePath(NULL); // look up full path of the current process's EXE file. + if (!path) { + return NULL; // error message was already set. } - if (len == 0) { - SDL_free(path); - WIN_SetError("Couldn't locate our .exe"); - return NULL; + char *ptr = SDL_strrchr(path, '\\'); + SDL_assert(ptr != NULL); // Should have been an absolute path. + + ptr[1] = '\0'; // chop off filename, leave '\\'. + + ptr = (char *) SDL_realloc(path, ((size_t) (ptr - path)) + 2); // try to shrink this allocation down a little. + return ptr ? ptr : path; // return shrunk buffer if shrink worked out, unchanged original buffer if not. +} + +char *SDL_SYS_GetExeName(void) +{ + char *path = WIN_GetModulePath(NULL); // look up full path of the current process's EXE file. + if (!path) { + return NULL; // error message was already set. } - for (i = len - 1; i > 0; i--) { - if (path[i] == '\\') { - break; - } - } - - SDL_assert(i > 0); // Should have been an absolute path. - path[i + 1] = '\0'; // chop off filename. - - result = WIN_StringToUTF8W(path); - SDL_free(path); - - return result; + char *ptr = SDL_strrchr(path, '\\'); + const size_t slen = SDL_strlen(ptr); // counts null terminator because we're still sitting on path separator. + SDL_memmove(path, ptr + 1, slen); // move filename string to start of SDL_realloc'd region. + ptr = (char *) SDL_realloc(path, slen); // try to shrink this allocation down a little. + return ptr ? ptr : path; // return shrunk buffer if shrink worked out, unchanged original buffer if not. } char *SDL_SYS_GetPrefPath(const char *org, const char *app) diff --git a/third-party/SDL/src/gpu/SDL_gpu.c b/third-party/SDL/src/gpu/SDL_gpu.c index 9a68d9c4fd..5e6d2aa959 100644 --- a/third-party/SDL/src/gpu/SDL_gpu.c +++ b/third-party/SDL/src/gpu/SDL_gpu.c @@ -1258,6 +1258,11 @@ SDL_GPUTexture *SDL_CreateGPUTexture( SDL_assert_release(!"For any texture: num_levels must be >= 1"); failed = true; } + if (createinfo->type == SDL_GPU_TEXTURETYPE_2D && createinfo->layer_count_or_depth != 1) + { + SDL_assert_release(!"2D textures must have a layer count of 1"); + failed = true; + } if ((createinfo->usage & SDL_GPU_TEXTUREUSAGE_GRAPHICS_STORAGE_READ) && (createinfo->usage & SDL_GPU_TEXTUREUSAGE_SAMPLER)) { SDL_assert_release(!"For any texture: usage cannot contain both GRAPHICS_STORAGE_READ and SAMPLER"); failed = true; @@ -1344,10 +1349,6 @@ SDL_GPUTexture *SDL_CreateGPUTexture( } else { if (createinfo->type == SDL_GPU_TEXTURETYPE_2D_ARRAY) { // Array Texture Validation - if (createinfo->usage & SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET) { - SDL_assert_release(!"For array textures: usage must not contain DEPTH_STENCIL_TARGET"); - failed = true; - } if (createinfo->sample_count > SDL_GPU_SAMPLECOUNT_1) { SDL_assert_release(!"For array textures: sample_count must be SDL_GPU_SAMPLECOUNT_1"); failed = true; @@ -3448,7 +3449,11 @@ bool SDL_WaitForGPUFences( { CHECK_DEVICE_MAGIC(device, false); - CHECK_PARAM(fences == NULL && num_fences > 0) { + if (!num_fences) { + return true; + } + + CHECK_PARAM(fences == NULL) { SDL_InvalidParamError("fences"); return false; } diff --git a/third-party/SDL/src/gpu/d3d12/SDL_gpu_d3d12.c b/third-party/SDL/src/gpu/d3d12/SDL_gpu_d3d12.c index d21a998ac3..b5a6e50f1e 100644 --- a/third-party/SDL/src/gpu/d3d12/SDL_gpu_d3d12.c +++ b/third-party/SDL/src/gpu/d3d12/SDL_gpu_d3d12.c @@ -1792,6 +1792,16 @@ static inline Uint32 D3D12_INTERNAL_CalcSubresource( return mipLevel + (layer * numLevels); } +static inline Uint32 D3D12_INTERNAL_CalcSubresourceWithPlane( + Uint32 mipLevel, + Uint32 layer, + Uint32 planeSlice, + Uint32 numLevels, + Uint32 arraySize) +{ + return mipLevel + (layer * numLevels) + (planeSlice * numLevels * arraySize); +} + static void D3D12_INTERNAL_ResourceBarrier( D3D12CommandBuffer *commandBuffer, D3D12_RESOURCE_STATES sourceState, @@ -1848,6 +1858,27 @@ static void D3D12_INTERNAL_TextureSubresourceBarrier( textureSubresource->parent->resource, textureSubresource->index, needsUAVBarrier); + + // D3D12 stores planar values on a separate subresource. + // Since depth-stencil is our only supported planar format, + // just force an extra transition if we're using a stencil format. + if (IsStencilFormat(textureSubresource->parent->container->header.info.format)) { + Uint32 planeSubresourceIndex = D3D12_INTERNAL_CalcSubresourceWithPlane( + textureSubresource->level, + textureSubresource->layer, + 1, + textureSubresource->parent->container->header.info.num_levels, + textureSubresource->parent->container->header.info.layer_count_or_depth + ); + + D3D12_INTERNAL_ResourceBarrier( + commandBuffer, + sourceState, + destinationState, + textureSubresource->parent->resource, + planeSubresourceIndex, + needsUAVBarrier); + } } static D3D12_RESOURCE_STATES D3D12_INTERNAL_DefaultTextureResourceState( @@ -9100,17 +9131,14 @@ static SDL_GPUDevice *D3D12_CreateDevice(bool debugMode, bool preferLowPower, SD if (SDL_HasProperty(props, SDL_PROP_GPU_DEVICE_CREATE_D3D12_AGILITY_SDK_PATH_STRING) && SDL_HasProperty(props, SDL_PROP_GPU_DEVICE_CREATE_D3D12_AGILITY_SDK_VERSION_NUMBER)) { int d3d12SDKVersion = SDL_GetNumberProperty(props, SDL_PROP_GPU_DEVICE_CREATE_D3D12_AGILITY_SDK_VERSION_NUMBER, 0); const char *d3d12SDKPath = SDL_GetStringProperty(props, SDL_PROP_GPU_DEVICE_CREATE_D3D12_AGILITY_SDK_PATH_STRING, ".\\D3D12\\"); + ID3D12SDKConfiguration *sdk_config = NULL; pD3D12GetInterface = (PFN_D3D12_GET_INTERFACE)SDL_LoadFunction( renderer->d3d12_dll, D3D12_GET_INTERFACE_FUNC); if (pD3D12GetInterface == NULL) { SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "Could not load D3D12GetInterface, custom D3D12 SDK will not load."); - } - - ID3D12SDKConfiguration *sdk_config = NULL; - - if (SUCCEEDED(pD3D12GetInterface(D3D_GUID(D3D_CLSID_ID3D12SDKConfiguration), D3D_GUID(D3D_IID_ID3D12SDKConfiguration), (void**) &sdk_config))) { + } else if (SUCCEEDED(pD3D12GetInterface(D3D_GUID(D3D_CLSID_ID3D12SDKConfiguration), D3D_GUID(D3D_IID_ID3D12SDKConfiguration), (void**) &sdk_config))) { ID3D12SDKConfiguration1 *sdk_config1 = NULL; if (SUCCEEDED(IUnknown_QueryInterface(sdk_config, &D3D_IID_ID3D12SDKConfiguration1, (void**) &sdk_config1))) { if (SUCCEEDED(ID3D12SDKConfiguration1_CreateDeviceFactory(sdk_config1, d3d12SDKVersion, d3d12SDKPath, &D3D_IID_ID3D12DeviceFactory, (void**) &factory))) { diff --git a/third-party/SDL/src/gpu/metal/SDL_gpu_metal.m b/third-party/SDL/src/gpu/metal/SDL_gpu_metal.m index c8713885d0..a057eab7bb 100644 --- a/third-party/SDL/src/gpu/metal/SDL_gpu_metal.m +++ b/third-party/SDL/src/gpu/metal/SDL_gpu_metal.m @@ -3545,6 +3545,8 @@ static void METAL_INTERNAL_PerformPendingDestroys( Sint32 i; Uint32 j; + SDL_LockMutex(renderer->disposeLock); + for (i = renderer->bufferContainersToDestroyCount - 1; i >= 0; i -= 1) { referenceCount = 0; for (j = 0; j < renderer->bufferContainersToDestroy[i]->bufferCount; j += 1) { @@ -3574,6 +3576,8 @@ static void METAL_INTERNAL_PerformPendingDestroys( renderer->textureContainersToDestroyCount -= 1; } } + + SDL_UnlockMutex(renderer->disposeLock); } // Fences diff --git a/third-party/SDL/src/gpu/metal/compile_shaders.sh b/third-party/SDL/src/gpu/metal/compile_shaders.sh old mode 100644 new mode 100755 diff --git a/third-party/SDL/src/gpu/vulkan/SDL_gpu_vulkan.c b/third-party/SDL/src/gpu/vulkan/SDL_gpu_vulkan.c index f05c03973c..ae19a13893 100644 --- a/third-party/SDL/src/gpu/vulkan/SDL_gpu_vulkan.c +++ b/third-party/SDL/src/gpu/vulkan/SDL_gpu_vulkan.c @@ -603,6 +603,11 @@ struct VulkanTexture VkImageAspectFlags aspectFlags; Uint32 depth; // used for cleanup only + // used to avoid indirection on barriers + Uint32 levelCount; + Uint32 layerCount; + SDL_GPUTextureType type; + // FIXME: It'd be nice if we didn't have to have this on the texture... SDL_GPUTextureUsageFlags usage; // used for defrag transitions only. @@ -2452,6 +2457,11 @@ static void VULKAN_INTERNAL_TrackTextureTransfer( VulkanCommandBuffer *commandBuffer, VulkanTexture *texture) { + // Textures not managed by our allocator (i.e. the swapchain) don't need to be refcounted. + if (texture->usedRegion == NULL) { + return; + } + TRACK_RESOURCE( texture, VulkanTexture *, @@ -2670,12 +2680,16 @@ static void VULKAN_INTERNAL_BufferMemoryBarrier( buffer->transitioned = true; } -static void VULKAN_INTERNAL_TextureSubresourceMemoryBarrier( +static void VULKAN_INTERNAL_TextureMemoryBarrier( VulkanRenderer *renderer, VulkanCommandBuffer *commandBuffer, VulkanTextureUsageMode sourceUsageMode, VulkanTextureUsageMode destinationUsageMode, - VulkanTextureSubresource *textureSubresource) + Uint32 baseLevel, + Uint32 levelCount, + Uint32 baseLayer, + Uint32 layerCount, + VulkanTexture *texture) { VkPipelineStageFlags srcStages = 0; VkPipelineStageFlags dstStages = 0; @@ -2689,12 +2703,12 @@ static void VULKAN_INTERNAL_TextureSubresourceMemoryBarrier( memoryBarrier.newLayout = VK_IMAGE_LAYOUT_UNDEFINED; memoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; memoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - memoryBarrier.image = textureSubresource->parent->image; - memoryBarrier.subresourceRange.aspectMask = textureSubresource->parent->aspectFlags; - memoryBarrier.subresourceRange.baseArrayLayer = textureSubresource->layer; - memoryBarrier.subresourceRange.layerCount = 1; - memoryBarrier.subresourceRange.baseMipLevel = textureSubresource->level; - memoryBarrier.subresourceRange.levelCount = 1; + memoryBarrier.image = texture->image; + memoryBarrier.subresourceRange.aspectMask = texture->aspectFlags; + memoryBarrier.subresourceRange.baseMipLevel = baseLevel; + memoryBarrier.subresourceRange.levelCount = levelCount; + memoryBarrier.subresourceRange.baseArrayLayer = baseLayer; + memoryBarrier.subresourceRange.layerCount = layerCount; if (sourceUsageMode == VULKAN_TEXTURE_USAGE_MODE_UNINITIALIZED) { srcStages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; @@ -2791,6 +2805,56 @@ static void VULKAN_INTERNAL_TextureSubresourceMemoryBarrier( &memoryBarrier); } +// Transitions the entire texture with a single barrier call. +static void VULKAN_INTERNAL_FullTextureMemoryBarrier( + VulkanRenderer *renderer, + VulkanCommandBuffer *commandBuffer, + VulkanTextureUsageMode sourceUsageMode, + VulkanTextureUsageMode destinationUsageMode, + VulkanTexture *texture) +{ + VULKAN_INTERNAL_TextureMemoryBarrier( + renderer, + commandBuffer, + sourceUsageMode, + destinationUsageMode, + 0, + texture->levelCount, + 0, + texture->layerCount, + texture); +} + +static void VULKAN_INTERNAL_TextureSubresourceMemoryBarrier( + VulkanRenderer *renderer, + VulkanCommandBuffer *commandBuffer, + VulkanTextureUsageMode sourceUsageMode, + VulkanTextureUsageMode destinationUsageMode, + VulkanTextureSubresource *textureSubresource) +{ + Uint32 layerCount = 1; + + // VK_KHR_maintenance9 adds the ability to independently transition arbitrary subsets of slices in a 3D texture + // but otherwise it is not necessarily supported by the driver. + // As a workaround we have to transition the whole texture instead of just the subresource. + // If VK_KHR_maintenance9 becomes widely supported, this can be removed. + // See https://docs.vulkan.org/features/latest/features/proposals/VK_KHR_maintenance9.html#_barriers_with_2d_array_compatible_3d_images + if (textureSubresource->parent->type == SDL_GPU_TEXTURETYPE_3D) { + layerCount = VK_REMAINING_ARRAY_LAYERS; + } + + VULKAN_INTERNAL_TextureMemoryBarrier( + renderer, + commandBuffer, + sourceUsageMode, + destinationUsageMode, + textureSubresource->level, + 1, + textureSubresource->layer, + layerCount, + textureSubresource->parent); +} + static VulkanBufferUsageMode VULKAN_INTERNAL_DefaultBufferUsageMode( VulkanBuffer *buffer) { @@ -2888,13 +2952,12 @@ static void VULKAN_INTERNAL_TextureTransitionFromDefaultUsage( VulkanTextureUsageMode destinationUsageMode, VulkanTexture *texture) { - for (Uint32 i = 0; i < texture->subresourceCount; i += 1) { - VULKAN_INTERNAL_TextureSubresourceTransitionFromDefaultUsage( - renderer, - commandBuffer, - destinationUsageMode, - &texture->subresources[i]); - } + VULKAN_INTERNAL_FullTextureMemoryBarrier( + renderer, + commandBuffer, + VULKAN_INTERNAL_DefaultTextureUsageMode(texture), + destinationUsageMode, + texture); } static void VULKAN_INTERNAL_TextureSubresourceTransitionToDefaultUsage( @@ -2917,14 +2980,12 @@ static void VULKAN_INTERNAL_TextureTransitionToDefaultUsage( VulkanTextureUsageMode sourceUsageMode, VulkanTexture *texture) { - // FIXME: could optimize this barrier - for (Uint32 i = 0; i < texture->subresourceCount; i += 1) { - VULKAN_INTERNAL_TextureSubresourceTransitionToDefaultUsage( - renderer, - commandBuffer, - sourceUsageMode, - &texture->subresources[i]); - } + VULKAN_INTERNAL_FullTextureMemoryBarrier( + renderer, + commandBuffer, + sourceUsageMode, + VULKAN_INTERNAL_DefaultTextureUsageMode(texture), + texture); } // Resource Disposal @@ -3130,7 +3191,9 @@ static void VULKAN_INTERNAL_DestroyCommandPool( SDL_free(commandBuffer->waitSemaphores); SDL_free(commandBuffer->signalSemaphores); SDL_free(commandBuffer->usedBuffers); + SDL_free(commandBuffer->buffersUsedInPendingTransfers); SDL_free(commandBuffer->usedTextures); + SDL_free(commandBuffer->texturesUsedInPendingTransfers); SDL_free(commandBuffer->usedSamplers); SDL_free(commandBuffer->usedGraphicsPipelines); SDL_free(commandBuffer->usedComputePipelines); @@ -4788,6 +4851,9 @@ static Uint32 VULKAN_INTERNAL_CreateSwapchain( windowData->textureContainers[i].activeTexture->aspectFlags = VK_IMAGE_ASPECT_COLOR_BIT; windowData->textureContainers[i].activeTexture->depth = 1; windowData->textureContainers[i].activeTexture->usage = SDL_GPU_TEXTUREUSAGE_COLOR_TARGET; + windowData->textureContainers[i].activeTexture->levelCount = 1; + windowData->textureContainers[i].activeTexture->layerCount = 1; + windowData->textureContainers[i].activeTexture->type = SDL_GPU_TEXTURETYPE_2D; windowData->textureContainers[i].activeTexture->container = &windowData->textureContainers[i]; SDL_SetAtomicInt(&windowData->textureContainers[i].activeTexture->referenceCount, 0); @@ -5673,6 +5739,9 @@ static VulkanTexture *VULKAN_INTERNAL_CreateTexture( texture->swizzle = SwizzleForSDLFormat(createinfo->format); texture->depth = depth; texture->usage = createinfo->usage; + texture->levelCount = createinfo->num_levels; + texture->layerCount = (createinfo->type == SDL_GPU_TEXTURETYPE_3D) ? 1 : createinfo->layer_count_or_depth; + texture->type = createinfo->type; SDL_SetAtomicInt(&texture->referenceCount, 0); if (IsDepthFormat(createinfo->format)) { @@ -5953,12 +6022,6 @@ static void VULKAN_INTERNAL_CycleActiveTexture( renderer, &container->header.info); - VULKAN_INTERNAL_TextureTransitionToDefaultUsage( - renderer, - commandBuffer, - VULKAN_TEXTURE_USAGE_MODE_UNINITIALIZED, - texture); - if (!texture) { return; } @@ -5976,6 +6039,13 @@ static void VULKAN_INTERNAL_CycleActiveTexture( container->textureCount += 1; container->activeTexture = texture; + + // Transition texture after storing it as the memory barrier might need to read the texture's container info + VULKAN_INTERNAL_TextureTransitionToDefaultUsage( + renderer, + commandBuffer, + VULKAN_TEXTURE_USAGE_MODE_UNINITIALIZED, + texture); } static VulkanBuffer *VULKAN_INTERNAL_PrepareBufferForWrite( @@ -6893,7 +6963,9 @@ static SDL_GPUTexture *VULKAN_CreateTexture( barrierCommandBuffer, VULKAN_TEXTURE_USAGE_MODE_UNINITIALIZED, texture); + VULKAN_INTERNAL_TrackTexture(barrierCommandBuffer, texture); + if (!VULKAN_Submit((SDL_GPUCommandBuffer *)barrierCommandBuffer)) { VULKAN_ReleaseTexture((SDL_GPURenderer *)renderer, (SDL_GPUTexture *)container); return NULL; @@ -8336,7 +8408,6 @@ static void VULKAN_BindComputeStorageTextures( VULKAN_TEXTURE_USAGE_MODE_COMPUTE_STORAGE_READ, textureContainer->activeTexture); - VULKAN_INTERNAL_TrackTexture( vulkanCommandBuffer, textureContainer->activeTexture); @@ -10165,6 +10236,13 @@ static bool VULKAN_INTERNAL_AcquireSwapchainTexture( break; // we got the next image! } + // Surface lost — flag for surface + swapchain recreation on next call + if (acquireResult == VK_ERROR_SURFACE_LOST_KHR) { + windowData->needsSurfaceRecreate = true; + windowData->needsSwapchainRecreate = true; + return true; + } + // If acquisition is invalid, let's try to recreate Uint32 recreateSwapchainResult = VULKAN_INTERNAL_RecreateSwapchain(renderer, windowData); if (!recreateSwapchainResult) { @@ -10531,7 +10609,7 @@ static void VULKAN_INTERNAL_CleanCommandBuffer( commandBuffer->usedBufferCount = 0; for (Sint32 i = 0; i < commandBuffer->buffersUsedInPendingTransfersCount; i += 1) { - (void)SDL_AtomicDecRef(&commandBuffer->usedBuffers[i]->usedRegion->allocation->referenceCount); + (void)SDL_AtomicDecRef(&commandBuffer->buffersUsedInPendingTransfers[i]->usedRegion->allocation->referenceCount); } commandBuffer->buffersUsedInPendingTransfersCount = 0; @@ -10541,7 +10619,7 @@ static void VULKAN_INTERNAL_CleanCommandBuffer( commandBuffer->usedTextureCount = 0; for (Sint32 i = 0; i < commandBuffer->texturesUsedInPendingTransfersCount; i += 1){ - (void)SDL_AtomicDecRef(&commandBuffer->usedTextures[i]->usedRegion->allocation->referenceCount); + (void)SDL_AtomicDecRef(&commandBuffer->texturesUsedInPendingTransfers[i]->usedRegion->allocation->referenceCount); } commandBuffer->texturesUsedInPendingTransfersCount = 0; @@ -11046,24 +11124,26 @@ static bool VULKAN_INTERNAL_DefragmentMemory( } SDL_GPUTextureCreateInfo info = currentRegion->vulkanTexture->container->header.info; + + VULKAN_INTERNAL_TextureTransitionFromDefaultUsage( + renderer, + commandBuffer, + VULKAN_TEXTURE_USAGE_MODE_COPY_SOURCE, + currentRegion->vulkanTexture); + + VULKAN_INTERNAL_FullTextureMemoryBarrier( + renderer, + commandBuffer, + VULKAN_TEXTURE_USAGE_MODE_UNINITIALIZED, + VULKAN_TEXTURE_USAGE_MODE_COPY_DESTINATION, + newTexture + ); + + // Can only copy one mip level at a time for (Uint32 subresourceIndex = 0; subresourceIndex < currentRegion->vulkanTexture->subresourceCount; subresourceIndex += 1) { - // copy subresource if necessary VulkanTextureSubresource *srcSubresource = ¤tRegion->vulkanTexture->subresources[subresourceIndex]; VulkanTextureSubresource *dstSubresource = &newTexture->subresources[subresourceIndex]; - VULKAN_INTERNAL_TextureSubresourceTransitionFromDefaultUsage( - renderer, - commandBuffer, - VULKAN_TEXTURE_USAGE_MODE_COPY_SOURCE, - srcSubresource); - - VULKAN_INTERNAL_TextureSubresourceMemoryBarrier( - renderer, - commandBuffer, - VULKAN_TEXTURE_USAGE_MODE_UNINITIALIZED, - VULKAN_TEXTURE_USAGE_MODE_COPY_DESTINATION, - dstSubresource); - VkImageCopy imageCopy; imageCopy.srcOffset.x = 0; imageCopy.srcOffset.y = 0; @@ -11092,16 +11172,16 @@ static bool VULKAN_INTERNAL_DefragmentMemory( 1, &imageCopy); - VULKAN_INTERNAL_TextureSubresourceTransitionToDefaultUsage( - renderer, - commandBuffer, - VULKAN_TEXTURE_USAGE_MODE_COPY_DESTINATION, - dstSubresource); - VULKAN_INTERNAL_TrackTexture(commandBuffer, srcSubresource->parent); VULKAN_INTERNAL_TrackTexture(commandBuffer, dstSubresource->parent); } + VULKAN_INTERNAL_TextureTransitionToDefaultUsage( + renderer, + commandBuffer, + VULKAN_TEXTURE_USAGE_MODE_COPY_DESTINATION, + newTexture); + // re-point original container to new texture newTexture->container = currentRegion->vulkanTexture->container; newTexture->containerIndex = currentRegion->vulkanTexture->containerIndex; diff --git a/third-party/SDL/src/haptic/SDL_haptic.c b/third-party/SDL/src/haptic/SDL_haptic.c index 5eaf095b4b..577502c922 100644 --- a/third-party/SDL/src/haptic/SDL_haptic.c +++ b/third-party/SDL/src/haptic/SDL_haptic.c @@ -306,9 +306,9 @@ bool SDL_IsJoystickHaptic(SDL_Joystick *joystick) SDL_LockJoysticks(); { - // Must be a valid joystick + // Must be a valid joystick, but not a gamepad unless running under sdl2-compat if (SDL_IsJoystickValid(joystick) && - !SDL_IsGamepad(SDL_GetJoystickID(joystick))) { + (SDL_GetHintBoolean("SDL2_COMPAT", false) || !SDL_IsGamepad(SDL_GetJoystickID(joystick)))) { #ifdef SDL_JOYSTICK_HIDAPI result = SDL_SYS_JoystickIsHaptic(joystick) || SDL_HIDAPI_JoystickIsHaptic(joystick); #else diff --git a/third-party/SDL/src/haptic/hidapi/SDL_hidapihaptic_c.h b/third-party/SDL/src/haptic/hidapi/SDL_hidapihaptic_c.h index 7d678811d5..3ae4f29baa 100644 --- a/third-party/SDL/src/haptic/hidapi/SDL_hidapihaptic_c.h +++ b/third-party/SDL/src/haptic/hidapi/SDL_hidapihaptic_c.h @@ -67,4 +67,4 @@ struct SDL_HIDAPI_HapticDriver extern SDL_HIDAPI_HapticDriver SDL_HIDAPI_HapticDriverLg4ff; -#endif //SDL_joystick_c_h_ +#endif //SDL_hidapihaptic_c_h_ diff --git a/third-party/SDL/src/hidapi/android/hid.cpp b/third-party/SDL/src/hidapi/android/hid.cpp index e9a1945b5d..8b6abd97ae 100644 --- a/third-party/SDL/src/hidapi/android/hid.cpp +++ b/third-party/SDL/src/hidapi/android/hid.cpp @@ -91,6 +91,9 @@ extern "C" { typedef uint32_t uint32; typedef uint64_t uint64; +#define D0G_BLE2_PID 0x1106 +#define TRITON_BLE_PID 0x1303 + struct hid_device_ { @@ -431,15 +434,15 @@ static void ExceptionCheck( JNIEnv *env, const char *pszClassName, const char *p class CHIDDevice { public: - CHIDDevice( int nDeviceID, hid_device_info *pInfo ) + CHIDDevice( int nDeviceID, hid_device_info *pInfo, int nReportID ) { m_nId = nDeviceID; m_pInfo = pInfo; + m_nReportID = nReportID; // The Bluetooth Steam Controller needs special handling const int VALVE_USB_VID = 0x28DE; - const int D0G_BLE2_PID = 0x1106; - if ( pInfo->vendor_id == VALVE_USB_VID && pInfo->product_id == D0G_BLE2_PID ) + if ( pInfo->vendor_id == VALVE_USB_VID && ( pInfo->product_id == D0G_BLE2_PID || pInfo->product_id == TRITON_BLE_PID ) ) { m_bIsBLESteamController = true; } @@ -604,7 +607,7 @@ public: size_t nDataLen = buffer.size() > length ? length : buffer.size(); if ( m_bIsBLESteamController ) { - data[0] = 0x03; + data[0] = m_nReportID; SDL_memcpy( data + 1, buffer.data(), nDataLen ); ++nDataLen; } @@ -716,9 +719,27 @@ public: } } - size_t uBytesToCopy = m_reportResponse.size() > nDataLen ? nDataLen : m_reportResponse.size(); - SDL_memcpy( pData, m_reportResponse.data(), uBytesToCopy ); - m_reportResponse.clear(); + size_t uBytesToCopy = 0; + + if ( bFeature && m_reportResponse.size() > 0 ) + { + // Make sure we preserve the report value if it isn't already in the report. + bool bHasReportAlready = ( *pData == *m_reportResponse.data() ); + + // Make sure we only copy as much as will fit, deducting one byte for the report if we need to leave it intact. + size_t nSafeDataLen = nDataLen - ( bHasReportAlready ? 0 : 1 ); + uBytesToCopy = m_reportResponse.size() < nSafeDataLen ? m_reportResponse.size() : nSafeDataLen; + + SDL_memcpy( pData + ( bHasReportAlready ? 0 : 1 ), m_reportResponse.data(), uBytesToCopy ); + m_reportResponse.clear(); + + if ( !bHasReportAlready ) + { + // Add the report byte back on to return the real length. + uBytesToCopy++; + } + } + LOGV( "=== Got %zu bytes", uBytesToCopy ); return (int)uBytesToCopy; @@ -761,6 +782,7 @@ private: pthread_mutex_t m_refCountLock = PTHREAD_MUTEX_INITIALIZER; int m_nRefCount = 0; int m_nId = 0; + int m_nReportID = 0; hid_device_info *m_pInfo = nullptr; hid_device *m_pDevice = nullptr; bool m_bIsBLESteamController = false; @@ -810,7 +832,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, jint nDeviceID, jstring sIdentifier, jint nVendorId, jint nProductId, jstring sSerialNumber, jint nReleaseNumber, jstring sManufacturer, jstring sProduct, jint nInterface, jint nInterfaceClass, jint nInterfaceSubclass, jint nInterfaceProtocol, jboolean bBluetooth ); +JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceConnected)(JNIEnv *env, jobject thiz, jint nDeviceID, jstring sIdentifier, jint nVendorId, jint nProductId, jstring sSerialNumber, jint nReleaseNumber, jstring sManufacturer, jstring sProduct, jint nInterface, jint nInterfaceClass, jint nInterfaceSubclass, jint nInterfaceProtocol, jboolean bBluetooth, jint nReportID ); extern "C" JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceOpenPending)(JNIEnv *env, jobject thiz, jint nDeviceID); @@ -890,7 +912,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, jint nDeviceID, jstring sIdentifier, jint nVendorId, jint nProductId, jstring sSerialNumber, jint nReleaseNumber, jstring sManufacturer, jstring sProduct, jint nInterface, jint nInterfaceClass, jint nInterfaceSubclass, jint nInterfaceProtocol, jboolean bBluetooth ) +JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceConnected)(JNIEnv *env, jobject thiz, jint nDeviceID, jstring sIdentifier, jint nVendorId, jint nProductId, jstring sSerialNumber, jint nReleaseNumber, jstring sManufacturer, jstring sProduct, jint nInterface, jint nInterfaceClass, jint nInterfaceSubclass, jint nInterfaceProtocol, jboolean bBluetooth, jint nReportID ) { LOGV( "HIDDeviceConnected() id=%d VID/PID = %.4x/%.4x, interface %d\n", nDeviceID, nVendorId, nProductId, nInterface ); @@ -916,7 +938,7 @@ JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceConnected)(JNI pInfo->bus_type = HID_API_BUS_USB; } - hid_device_ref pDevice( new CHIDDevice( nDeviceID, pInfo ) ); + hid_device_ref pDevice( new CHIDDevice( nDeviceID, pInfo, nReportID ) ); hid_mutex_guard l( &g_DevicesMutex ); hid_device_ref pLast, pCurr; @@ -1396,7 +1418,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, jint nDeviceID, jstring sIdentifier, jint nVendorId, jint nProductId, jstring sSerialNumber, jint nReleaseNumber, jstring sManufacturer, jstring sProduct, jint nInterface, jint nInterfaceClass, jint nInterfaceSubclass, jint nInterfaceProtocol, jboolean bBluetooth ); +JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceConnected)(JNIEnv *env, jobject thiz, jint nDeviceID, jstring sIdentifier, jint nVendorId, jint nProductId, jstring sSerialNumber, jint nReleaseNumber, jstring sManufacturer, jstring sProduct, jint nInterface, jint nInterfaceClass, jint nInterfaceSubclass, jint nInterfaceProtocol, jboolean bBluetooth, jint nReportID ); extern "C" JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceOpenPending)(JNIEnv *env, jobject thiz, jint nDeviceID); @@ -1427,7 +1449,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, jint nDeviceID, jstring sIdentifier, jint nVendorId, jint nProductId, jstring sSerialNumber, jint nReleaseNumber, jstring sManufacturer, jstring sProduct, jint nInterface, jint nInterfaceClass, jint nInterfaceSubclass, jint nInterfaceProtocol, jboolean bBluetooth ) +JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceConnected)(JNIEnv *env, jobject thiz, jint nDeviceID, jstring sIdentifier, jint nVendorId, jint nProductId, jstring sSerialNumber, jint nReleaseNumber, jstring sManufacturer, jstring sProduct, jint nInterface, jint nInterfaceClass, jint nInterfaceSubclass, jint nInterfaceProtocol, jboolean bBluetooth, jint nReportID ) { LOGV("Stub HIDDeviceConnected() id=%d VID/PID = %.4x/%.4x, interface %d\n", nDeviceID, nVendorId, nProductId, nInterface); } @@ -1468,7 +1490,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;IIIIZ)V", (void*)HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceConnected) }, + { "HIDDeviceConnected", "(ILjava/lang/String;IILjava/lang/String;ILjava/lang/String;Ljava/lang/String;IIIIZI)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/third-party/SDL/src/hidapi/bootstrap b/third-party/SDL/src/hidapi/bootstrap old mode 100644 new mode 100755 diff --git a/third-party/SDL/src/hidapi/ios/hid.m b/third-party/SDL/src/hidapi/ios/hid.m index e1171ff92f..fe7b2e38db 100644 --- a/third-party/SDL/src/hidapi/ios/hid.m +++ b/third-party/SDL/src/hidapi/ios/hid.m @@ -78,7 +78,8 @@ typedef uint64_t uint64; // (READ/NOTIFICATIONS) #define VALVE_INPUT_CHAR_0x1106 @"100F6C33-1735-4313-B402-38567131E5F3" -#define VALVE_INPUT_CHAR_0x1303 @"100F6C7A-1735-4313-B402-38567131E5F3" +#define VALVE_INPUT_CHAR_0x1303_0x45 @"100F6C7A-1735-4313-B402-38567131E5F3" +#define VALVE_INPUT_CHAR_0x1303_0x47 @"100F6C7C-1735-4313-B402-38567131E5F3" //  (READ/WRITE) #define VALVE_REPORT_CHAR @"100F6C34-1735-4313-B402-38567131E5F3" @@ -220,6 +221,8 @@ typedef enum } @property (nonatomic, readwrite) uint16_t pid; +@property (nonatomic, readwrite) uint8_t report_id; +@property (nonatomic, readwrite) int report_length; @property (nonatomic, readwrite) bool connected; @property (nonatomic, readwrite) bool ready; @@ -523,7 +526,9 @@ static void process_pending_events(void) if ( self = [super init] ) { self.pid = 0; - _inputReports = NULL; + self.report_id = 0; + self.report_length = 0; + _inputReports = NULL; _outputReports = [[NSMutableDictionary alloc] init]; _connected = NO; _ready = NO; @@ -539,7 +544,9 @@ static void process_pending_events(void) if ( self = [super init] ) { self.pid = 0; - _inputReports = NULL; + self.report_id = 0; + self.report_length = 0; + _inputReports = NULL; _outputReports = [[NSMutableDictionary alloc] init]; _connected = NO; _ready = NO; @@ -583,17 +590,7 @@ static void process_pending_events(void) { if ( RingBuffer_read( _inputReports, dst+1 ) ) { - switch ( self.pid ) - { - case D0G_BLE2_PID: - *dst = 0x03; - break; - case TRITON_BLE_PID: - *dst = 0x42; - break; - default: - abort(); - } + *dst = self.report_id; return _inputReports->_cbElem + 1; } return 0; @@ -603,9 +600,9 @@ static void process_pending_events(void) { if ( self.pid == D0G_BLE2_PID ) { - [_bleSteamController writeValue:[NSData dataWithBytes:data length:length] forCharacteristic:_bleCharacteristicReport type:CBCharacteristicWriteWithResponse]; - return (int)length; -} + [_bleSteamController writeValue:[NSData dataWithBytes:data length:length] forCharacteristic:_bleCharacteristicReport type:CBCharacteristicWriteWithResponse]; + return (int)length; + } // We need to look up the correct characteristic for this output report if ( length > 0 ) @@ -748,11 +745,22 @@ static void process_pending_events(void) if ( [aChar.UUID isEqual:[CBUUID UUIDWithString:VALVE_INPUT_CHAR_0x1106]] ) { self.pid = D0G_BLE2_PID; + self.report_id = 0x03; + self.report_length = 19; self.bleCharacteristicInput = aChar; } - else if ( [aChar.UUID isEqual:[CBUUID UUIDWithString:VALVE_INPUT_CHAR_0x1303]] ) + else if ( [aChar.UUID isEqual:[CBUUID UUIDWithString:VALVE_INPUT_CHAR_0x1303_0x45]] ) { self.pid = TRITON_BLE_PID; + self.report_id = 0x45; + self.report_length = 45; + self.bleCharacteristicInput = aChar; + } + else if ( [aChar.UUID isEqual:[CBUUID UUIDWithString:VALVE_INPUT_CHAR_0x1303_0x47]] ) + { + self.pid = TRITON_BLE_PID; + self.report_id = 0x47; + self.report_length = 45; self.bleCharacteristicInput = aChar; } else if ( [aChar.UUID isEqual:[CBUUID UUIDWithString:VALVE_REPORT_CHAR]] ) @@ -789,22 +797,10 @@ static void process_pending_events(void) if ( self.ready == NO ) { self.ready = YES; - if ( _inputReports == NULL ) - { - int cbElem = 0; - switch ( self.pid ) - { - case D0G_BLE2_PID: - cbElem = 19; - break; - case TRITON_BLE_PID: - cbElem = 53; - break; - default: - abort(); - } - _inputReports = RingBuffer_alloc( cbElem ); - } + if ( _inputReports == NULL ) + { + _inputReports = RingBuffer_alloc( self.report_length ); + } HIDBLEManager.sharedInstance.nPendingPairs -= 1; } @@ -858,7 +854,7 @@ static void process_pending_events(void) - (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error { - NSLog( @"didUpdateNotificationStateForCharacteristic %@ (%@)", characteristic, error ); + //NSLog( @"didUpdateNotificationStateForCharacteristic %@ (%@)", characteristic, error ); } @end diff --git a/third-party/SDL/src/hidapi/libusb/hid.c b/third-party/SDL/src/hidapi/libusb/hid.c index 956e982b1a..c5e68d7e19 100644 --- a/third-party/SDL/src/hidapi/libusb/hid.c +++ b/third-party/SDL/src/hidapi/libusb/hid.c @@ -853,6 +853,7 @@ static int is_xbox360(unsigned short vendor_id, const struct libusb_interface_de 0x24c6, /* PowerA */ 0x2c22, /* Qanba */ 0x2dc8, /* 8BitDo */ + 0x3537, /* GameSir */ 0x37d7, /* Flydigi */ 0x9886, /* ASTRO Gaming */ }; diff --git a/third-party/SDL/src/hidapi/mac/hid.c b/third-party/SDL/src/hidapi/mac/hid.c index 4936fe1262..79d86bee19 100644 --- a/third-party/SDL/src/hidapi/mac/hid.c +++ b/third-party/SDL/src/hidapi/mac/hid.c @@ -573,6 +573,7 @@ static struct hid_device_info *create_device_info_with_usage(IOHIDDeviceRef dev, { unsigned short dev_vid; unsigned short dev_pid; + unsigned short dev_version; int BUF_LEN = 256; wchar_t buf[BUF_LEN]; CFTypeRef transport_prop; @@ -593,6 +594,7 @@ static struct hid_device_info *create_device_info_with_usage(IOHIDDeviceRef dev, dev_vid = get_vendor_id(dev); dev_pid = get_product_id(dev); + dev_version = get_int_property(dev, CFSTR(kIOHIDVersionNumberKey)); #ifdef HIDAPI_IGNORE_DEVICE /* See if there are any devices we should skip in enumeration */ @@ -602,6 +604,15 @@ static struct hid_device_info *create_device_info_with_usage(IOHIDDeviceRef dev, } #endif +#ifdef HIDAPI_USING_SDL_RUNTIME + if (SDL_IsJoystickSteamVirtualGamepad(dev_vid, dev_pid, dev_version)) { + if (IOHIDDeviceGetProperty(dev, CFSTR(kIOHIDVirtualHIDevice)) != kCFBooleanTrue) { + /* This is a real Xbox 360 controller, adjust the version so it's not detected as a Steam virtual gamepad */ + dev_version = 1; + } + } +#endif + cur_dev->usage_page = usage_page; cur_dev->usage = usage; @@ -649,7 +660,7 @@ static struct hid_device_info *create_device_info_with_usage(IOHIDDeviceRef dev, cur_dev->product_id = dev_pid; /* Release Number */ - cur_dev->release_number = get_int_property(dev, CFSTR(kIOHIDVersionNumberKey)); + cur_dev->release_number = dev_version; /* Interface Number. * We can only retrieve the interface number for USB HID devices. @@ -1138,6 +1149,8 @@ return_error: static int set_report(hid_device *dev, IOHIDReportType type, const unsigned char *data, size_t length) { + const char *pass_through_magic = "MAGIC0"; + size_t pass_through_magic_length = strlen(pass_through_magic); const unsigned char *data_to_send = data; CFIndex length_to_send = length; IOReturn res; @@ -1158,6 +1171,11 @@ static int set_report(hid_device *dev, IOHIDReportType type, const unsigned char data_to_send = data+1; length_to_send = length-1; } + else if (length > 6 && memcmp(data, pass_through_magic, pass_through_magic_length) == 0) { + report_id = data[pass_through_magic_length]; + data_to_send = data+pass_through_magic_length; + length_to_send = length-pass_through_magic_length; + } /* Avoid crash if the device has been unplugged. */ if (dev->disconnected) { diff --git a/third-party/SDL/src/hidapi/netbsd/hid.c b/third-party/SDL/src/hidapi/netbsd/hid.c index d88630db87..5f5858504d 100644 --- a/third-party/SDL/src/hidapi/netbsd/hid.c +++ b/third-party/SDL/src/hidapi/netbsd/hid.c @@ -711,6 +711,7 @@ struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned shor for (size_t i = 0; i < len; i++) { char devpath[USB_MAX_DEVNAMELEN]; int bus; + struct hid_device_info *prev_end; strlcpy(devpath, "/dev/", sizeof(devpath)); strlcat(devpath, arr[i], sizeof(devpath)); @@ -719,7 +720,17 @@ struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned shor if (bus == -1) continue; + /* + * ehci/ohci/uhci/dwctwo etc. use 'addr 1' for root hubs + * but xhci uses 'addr 0' on NetBSD. + * Check addr 0 (that would be unused on other than xhci) + * and then check addr 1 if there is no device at addr 0. + */ + prev_end = hed.end; enumerate_usb_devices(bus, 0, hid_enumerate_callback, &hed); + if (hed.end == prev_end) + enumerate_usb_devices(bus, 1, + hid_enumerate_callback, &hed); close(bus); } diff --git a/third-party/SDL/src/hidapi/testgui/copy_to_bundle.sh b/third-party/SDL/src/hidapi/testgui/copy_to_bundle.sh old mode 100644 new mode 100755 diff --git a/third-party/SDL/src/hidapi/windows/hid.c b/third-party/SDL/src/hidapi/windows/hid.c index aa67a466d3..7579978232 100644 --- a/third-party/SDL/src/hidapi/windows/hid.c +++ b/third-party/SDL/src/hidapi/windows/hid.c @@ -1027,6 +1027,12 @@ struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned shor continue; } +#ifdef HIDAPI_IGNORE_DEVICE + hid_bus_type bus_type = HID_API_BUS_UNKNOWN; + PHIDP_PREPARSED_DATA pp_data = NULL; + HIDP_CAPS caps = { 0 }; +#endif + /* Get the Vendor ID and Product ID for this device. */ attrib.Size = sizeof(HIDD_ATTRIBUTES); if (!HidD_GetAttributes(device_handle, &attrib)) { @@ -1035,9 +1041,7 @@ struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned shor #ifdef HIDAPI_IGNORE_DEVICE /* See if there are any devices we should skip in enumeration */ - hid_bus_type bus_type = get_bus_type(device_interface); - PHIDP_PREPARSED_DATA pp_data = NULL; - HIDP_CAPS caps = { 0 }; + bus_type = get_bus_type(device_interface); if (HidD_GetPreparsedData(device_handle, &pp_data)) { HidP_GetCaps(pp_data, &caps); HidD_FreePreparsedData(pp_data); diff --git a/third-party/SDL/src/joystick/SDL_gamepad.c b/third-party/SDL/src/joystick/SDL_gamepad.c index e05269088b..dd5b4166a9 100644 --- a/third-party/SDL/src/joystick/SDL_gamepad.c +++ b/third-party/SDL/src/joystick/SDL_gamepad.c @@ -206,7 +206,7 @@ static const struct SDL_GamepadBlacklistWords SDL_gamepad_blacklist_words[] = { {"Synaptics ", GAMEPAD_BLACKLIST_ANYWHERE}, // "Synaptics TM2768-001", "SynPS/2 Synaptics TouchPad" {"Trackpad", GAMEPAD_BLACKLIST_ANYWHERE}, {"Clickpad", GAMEPAD_BLACKLIST_ANYWHERE}, - // "PG-90215 Keyboard", "Usb Keyboard Usb Keyboard Consumer Control", "Framework Laptop 16 Keyboard Module - ISO System Control" + // "Usb Keyboard Usb Keyboard Consumer Control", "Framework Laptop 16 Keyboard Module - ISO System Control" {" Keyboard", GAMEPAD_BLACKLIST_ANYWHERE}, {" Laptop ", GAMEPAD_BLACKLIST_ANYWHERE}, // "Framework Laptop 16 Numpad Module System Control" {"Mouse ", GAMEPAD_BLACKLIST_BEGIN}, // "Mouse passthrough" @@ -712,91 +712,116 @@ static GamepadMapping_t *SDL_CreateMappingForAndroidGamepad(SDL_GUID guid) char mapping_string[1024]; int button_mask; int axis_mask; + Uint16 vendor, product; + + SDL_strlcpy(mapping_string, "none,", sizeof(mapping_string)); - button_mask = SDL_Swap16LE(*(Uint16 *)(&guid.data[sizeof(guid.data) - 4])); - axis_mask = SDL_Swap16LE(*(Uint16 *)(&guid.data[sizeof(guid.data) - 2])); - if (!button_mask && !axis_mask) { - // Accelerometer, shouldn't have a gamepad mapping - return NULL; - } - if (!(button_mask & face_button_mask)) { - // We don't know what buttons or axes are supported, don't make up a mapping - return NULL; - } + SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL, NULL); + if (vendor == USB_VENDOR_NINTENDO) { + if (product == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_LEFT || product == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_RIGHT) { + // FIXME: Should we have a separate hint for non-HIDAPI JoyCon handling? + // Android doesn't report JoyCon SL/SR presses for some reason, so no horizontal triggers/vertical paddles + if (SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_VERTICAL_JOY_CONS, false)) { + // Vertical mode + if (product == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_LEFT) { + SDL_strlcat(mapping_string, "Nintendo Switch Joy-Con (L),back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b15,leftx:a0,lefty:a1,misc1:b18,", sizeof(mapping_string)); + } else { + SDL_strlcat(mapping_string, "Nintendo Switch Joy-Con (R),a:b0,b:b1,guide:b5,rightshoulder:b10,rightstick:b8,righttrigger:b16,rightx:a0,righty:a1,start:b6,x:b3,y:b2,", sizeof(mapping_string)); + } + } else { + // Mini gamepad mode + if (product == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_LEFT) { + SDL_strlcat(mapping_string, "Nintendo Switch Joy-Con (L),a:b13,b:b12,guide:b18,leftstick:b7,leftx:a1,lefty:a0~,start:b4,x:b11,y:b14,paddle2:b9,paddle4:b15,", sizeof(mapping_string)); + } else { + SDL_strlcat(mapping_string, "Nintendo Switch Joy-Con (R),a:b1,b:b2,guide:b5,leftstick:b8,leftx:a1~,lefty:a0,start:b6,x:b0,y:b3,paddle1:b10,paddle3:b16,", sizeof(mapping_string)); + } + } + } + } else { + button_mask = SDL_Swap16LE(*(Uint16 *)(&guid.data[sizeof(guid.data) - 4])); + axis_mask = SDL_Swap16LE(*(Uint16 *)(&guid.data[sizeof(guid.data) - 2])); + if (!button_mask && !axis_mask) { + // Accelerometer, shouldn't have a gamepad mapping + return NULL; + } + if (!(button_mask & face_button_mask)) { + // We don't know what buttons or axes are supported, don't make up a mapping + return NULL; + } - SDL_strlcpy(mapping_string, "none,*,", sizeof(mapping_string)); + SDL_strlcat(mapping_string, "*,", sizeof(mapping_string)); - if (button_mask & (1 << SDL_GAMEPAD_BUTTON_SOUTH)) { - SDL_strlcat(mapping_string, "a:b0,", sizeof(mapping_string)); - } - if (button_mask & (1 << SDL_GAMEPAD_BUTTON_EAST)) { - SDL_strlcat(mapping_string, "b:b1,", sizeof(mapping_string)); - } else if (button_mask & (1 << SDL_GAMEPAD_BUTTON_BACK)) { - // Use the back button as "B" for easy UI navigation with TV remotes - SDL_strlcat(mapping_string, "b:b4,", sizeof(mapping_string)); - button_mask &= ~(1 << SDL_GAMEPAD_BUTTON_BACK); - } - if (button_mask & (1 << SDL_GAMEPAD_BUTTON_WEST)) { - SDL_strlcat(mapping_string, "x:b2,", sizeof(mapping_string)); - } - if (button_mask & (1 << SDL_GAMEPAD_BUTTON_NORTH)) { - SDL_strlcat(mapping_string, "y:b3,", sizeof(mapping_string)); - } - if (button_mask & (1 << SDL_GAMEPAD_BUTTON_BACK)) { - SDL_strlcat(mapping_string, "back:b4,", sizeof(mapping_string)); - } - if (button_mask & (1 << SDL_GAMEPAD_BUTTON_GUIDE)) { - // The guide button generally isn't functional (or acts as a home button) on most Android gamepads before Android 11 - if (SDL_GetAndroidSDKVersion() >= 30 /* Android 11 */) { - SDL_strlcat(mapping_string, "guide:b5,", sizeof(mapping_string)); + if (button_mask & (1 << SDL_GAMEPAD_BUTTON_SOUTH)) { + SDL_strlcat(mapping_string, "a:b0,", sizeof(mapping_string)); + } + if (button_mask & (1 << SDL_GAMEPAD_BUTTON_EAST)) { + SDL_strlcat(mapping_string, "b:b1,", sizeof(mapping_string)); + } else if (button_mask & (1 << SDL_GAMEPAD_BUTTON_BACK)) { + // Use the back button as "B" for easy UI navigation with TV remotes + SDL_strlcat(mapping_string, "b:b4,", sizeof(mapping_string)); + button_mask &= ~(1 << SDL_GAMEPAD_BUTTON_BACK); + } + if (button_mask & (1 << SDL_GAMEPAD_BUTTON_WEST)) { + SDL_strlcat(mapping_string, "x:b2,", sizeof(mapping_string)); + } + if (button_mask & (1 << SDL_GAMEPAD_BUTTON_NORTH)) { + SDL_strlcat(mapping_string, "y:b3,", sizeof(mapping_string)); + } + if (button_mask & (1 << SDL_GAMEPAD_BUTTON_BACK)) { + SDL_strlcat(mapping_string, "back:b4,", sizeof(mapping_string)); + } + if (button_mask & (1 << SDL_GAMEPAD_BUTTON_GUIDE)) { + // The guide button generally isn't functional (or acts as a home button) on most Android gamepads before Android 11 + if (SDL_GetAndroidSDKVersion() >= 30 /* Android 11 */) { + SDL_strlcat(mapping_string, "guide:b5,", sizeof(mapping_string)); + } + } + if (button_mask & (1 << SDL_GAMEPAD_BUTTON_START)) { + SDL_strlcat(mapping_string, "start:b6,", sizeof(mapping_string)); + } + if (button_mask & (1 << SDL_GAMEPAD_BUTTON_LEFT_STICK)) { + SDL_strlcat(mapping_string, "leftstick:b7,", sizeof(mapping_string)); + } + if (button_mask & (1 << SDL_GAMEPAD_BUTTON_RIGHT_STICK)) { + SDL_strlcat(mapping_string, "rightstick:b8,", sizeof(mapping_string)); + } + if (button_mask & (1 << SDL_GAMEPAD_BUTTON_LEFT_SHOULDER)) { + SDL_strlcat(mapping_string, "leftshoulder:b9,", sizeof(mapping_string)); + } + if (button_mask & (1 << SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER)) { + SDL_strlcat(mapping_string, "rightshoulder:b10,", sizeof(mapping_string)); + } + if (button_mask & (1 << SDL_GAMEPAD_BUTTON_DPAD_UP)) { + SDL_strlcat(mapping_string, "dpup:b11,", sizeof(mapping_string)); + } + if (button_mask & (1 << SDL_GAMEPAD_BUTTON_DPAD_DOWN)) { + SDL_strlcat(mapping_string, "dpdown:b12,", sizeof(mapping_string)); + } + if (button_mask & (1 << SDL_GAMEPAD_BUTTON_DPAD_LEFT)) { + SDL_strlcat(mapping_string, "dpleft:b13,", sizeof(mapping_string)); + } + if (button_mask & (1 << SDL_GAMEPAD_BUTTON_DPAD_RIGHT)) { + SDL_strlcat(mapping_string, "dpright:b14,", sizeof(mapping_string)); + } + if (axis_mask & (1 << SDL_GAMEPAD_AXIS_LEFTX)) { + SDL_strlcat(mapping_string, "leftx:a0,", sizeof(mapping_string)); + } + if (axis_mask & (1 << SDL_GAMEPAD_AXIS_LEFTY)) { + SDL_strlcat(mapping_string, "lefty:a1,", sizeof(mapping_string)); + } + if (axis_mask & (1 << SDL_GAMEPAD_AXIS_RIGHTX)) { + SDL_strlcat(mapping_string, "rightx:a2,", sizeof(mapping_string)); + } + if (axis_mask & (1 << SDL_GAMEPAD_AXIS_RIGHTY)) { + SDL_strlcat(mapping_string, "righty:a3,", sizeof(mapping_string)); + } + if (axis_mask & (1 << SDL_GAMEPAD_AXIS_LEFT_TRIGGER)) { + SDL_strlcat(mapping_string, "lefttrigger:a4,", sizeof(mapping_string)); + } + if (axis_mask & (1 << SDL_GAMEPAD_AXIS_RIGHT_TRIGGER)) { + SDL_strlcat(mapping_string, "righttrigger:a5,", sizeof(mapping_string)); } } - if (button_mask & (1 << SDL_GAMEPAD_BUTTON_START)) { - SDL_strlcat(mapping_string, "start:b6,", sizeof(mapping_string)); - } - if (button_mask & (1 << SDL_GAMEPAD_BUTTON_LEFT_STICK)) { - SDL_strlcat(mapping_string, "leftstick:b7,", sizeof(mapping_string)); - } - if (button_mask & (1 << SDL_GAMEPAD_BUTTON_RIGHT_STICK)) { - SDL_strlcat(mapping_string, "rightstick:b8,", sizeof(mapping_string)); - } - if (button_mask & (1 << SDL_GAMEPAD_BUTTON_LEFT_SHOULDER)) { - SDL_strlcat(mapping_string, "leftshoulder:b9,", sizeof(mapping_string)); - } - if (button_mask & (1 << SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER)) { - SDL_strlcat(mapping_string, "rightshoulder:b10,", sizeof(mapping_string)); - } - if (button_mask & (1 << SDL_GAMEPAD_BUTTON_DPAD_UP)) { - SDL_strlcat(mapping_string, "dpup:b11,", sizeof(mapping_string)); - } - if (button_mask & (1 << SDL_GAMEPAD_BUTTON_DPAD_DOWN)) { - SDL_strlcat(mapping_string, "dpdown:b12,", sizeof(mapping_string)); - } - if (button_mask & (1 << SDL_GAMEPAD_BUTTON_DPAD_LEFT)) { - SDL_strlcat(mapping_string, "dpleft:b13,", sizeof(mapping_string)); - } - if (button_mask & (1 << SDL_GAMEPAD_BUTTON_DPAD_RIGHT)) { - SDL_strlcat(mapping_string, "dpright:b14,", sizeof(mapping_string)); - } - if (axis_mask & (1 << SDL_GAMEPAD_AXIS_LEFTX)) { - SDL_strlcat(mapping_string, "leftx:a0,", sizeof(mapping_string)); - } - if (axis_mask & (1 << SDL_GAMEPAD_AXIS_LEFTY)) { - SDL_strlcat(mapping_string, "lefty:a1,", sizeof(mapping_string)); - } - if (axis_mask & (1 << SDL_GAMEPAD_AXIS_RIGHTX)) { - SDL_strlcat(mapping_string, "rightx:a2,", sizeof(mapping_string)); - } - if (axis_mask & (1 << SDL_GAMEPAD_AXIS_RIGHTY)) { - SDL_strlcat(mapping_string, "righty:a3,", sizeof(mapping_string)); - } - if (axis_mask & (1 << SDL_GAMEPAD_AXIS_LEFT_TRIGGER)) { - SDL_strlcat(mapping_string, "lefttrigger:a4,", sizeof(mapping_string)); - } - if (axis_mask & (1 << SDL_GAMEPAD_AXIS_RIGHT_TRIGGER)) { - SDL_strlcat(mapping_string, "righttrigger:a5,", sizeof(mapping_string)); - } - return SDL_PrivateAddMappingForGUID(guid, mapping_string, &existing, SDL_GAMEPAD_MAPPING_PRIORITY_DEFAULT); } #endif // SDL_PLATFORM_ANDROID @@ -1226,9 +1251,12 @@ static GamepadMapping_t *SDL_CreateMappingForHIDAPIGamepad(SDL_GUID guid) if (SDL_IsJoystickSteamController(vendor, product)) { // Steam controllers have 2 back paddle buttons SDL_strlcat(mapping_string, "paddle1:b11,paddle2:b12,", sizeof(mapping_string)); + } else if (SDL_IsJoystickSteamDeck(vendor, product)) { + // The Steam Deck's built-in controller has QAM, 4 back buttons, L/R trackpads, and L/R capacitive touch sticks + SDL_strlcat(mapping_string, "misc1:b11,paddle1:b12,paddle2:b13,paddle3:b14,paddle4:b15,touchpad:b17,misc2:b16,misc3:b19,misc4:b18", sizeof(mapping_string)); } else if (SDL_IsJoystickSteamTriton(vendor, product)) { // Second generation Steam controllers have 4 back paddle buttons - SDL_strlcat(mapping_string, "misc1:b11,paddle1:b12,paddle2:b13,paddle3:b14,paddle4:b15", sizeof(mapping_string)); + SDL_strlcat(mapping_string, "misc1:b11,paddle1:b12,paddle2:b13,paddle3:b14,paddle4:b15,touchpad:b17,misc2:b16,misc3:b19,misc4:b18,misc5:b21,misc6:b20", sizeof(mapping_string)); } else if (SDL_IsJoystickNintendoSwitchPro(vendor, product) || SDL_IsJoystickNintendoSwitchProInputOnly(vendor, product)) { // Nintendo Switch Pro controllers have a screenshot button @@ -1252,7 +1280,7 @@ static GamepadMapping_t *SDL_CreateMappingForHIDAPIGamepad(SDL_GUID guid) } } else if (SDL_IsJoystickHoriSteamController(vendor, product)) { /* The Wireless HORIPad for Steam has QAM, Steam, Capsense L/R Sticks, 2 rear buttons, and 2 misc buttons */ - SDL_strlcat(mapping_string, "paddle1:b13,paddle2:b12,paddle3:b15,paddle4:b14,misc2:b11,misc3:b16,misc4:b17", sizeof(mapping_string)); + SDL_strlcat(mapping_string, "paddle1:b13,paddle2:b12,paddle3:b15,paddle4:b14,misc1:b11,misc3:b16,misc4:b17", sizeof(mapping_string)); } else if (SDL_IsJoystickFlydigiController(vendor, product)) { SDL_strlcat(mapping_string, "paddle1:b11,paddle2:b12,paddle3:b13,paddle4:b14,", sizeof(mapping_string)); if (guid.data[15] >= SDL_FLYDIGI_VADER2) { @@ -3228,28 +3256,33 @@ bool SDL_IsGamepad(SDL_JoystickID instance_id) */ bool SDL_ShouldIgnoreGamepad(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name) { - int i; - for (i = 0; i < SDL_arraysize(SDL_gamepad_blacklist_words); i++) { - const struct SDL_GamepadBlacklistWords *blacklist_word = &SDL_gamepad_blacklist_words[i]; + if (name) { + for (int i = 0; i < SDL_arraysize(SDL_gamepad_blacklist_words); i++) { + const struct SDL_GamepadBlacklistWords *blacklist_word = &SDL_gamepad_blacklist_words[i]; - switch (blacklist_word->pos) { - case GAMEPAD_BLACKLIST_BEGIN: - if (SDL_startswith(name, blacklist_word->str)) { - return true; - } - break; + switch (blacklist_word->pos) { + case GAMEPAD_BLACKLIST_BEGIN: + if (SDL_startswith(name, blacklist_word->str)) { + return true; + } + break; - case GAMEPAD_BLACKLIST_END: - if (SDL_endswith(name, blacklist_word->str)) { - return true; - } - break; + case GAMEPAD_BLACKLIST_END: + if (SDL_endswith(name, blacklist_word->str)) { + return true; + } + break; - case GAMEPAD_BLACKLIST_ANYWHERE: - if (SDL_strstr(name, blacklist_word->str) != NULL) { - return true; - } - break; + case GAMEPAD_BLACKLIST_ANYWHERE: + if (SDL_strstr(name, blacklist_word->str) != NULL) { + if (SDL_startswith(name, "PG-")) { + // Ipega gamepads have modes with keyboard keys in addition to gamepad controls + break; + } + return true; + } + break; + } } } diff --git a/third-party/SDL/src/joystick/SDL_gamepad_db.h b/third-party/SDL/src/joystick/SDL_gamepad_db.h index 476ead6f44..2067460550 100644 --- a/third-party/SDL/src/joystick/SDL_gamepad_db.h +++ b/third-party/SDL/src/joystick/SDL_gamepad_db.h @@ -721,7 +721,7 @@ static const char *s_GamepadMappings[] = { "05000000de2800000212000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,", "05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,", "05000000de2800000611000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,", - "03000000de2800000512000000016800,Steam Deck Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,misc1:b11,paddle1:b12,paddle2:b13,paddle3:b14,paddle4:b15,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,", + "03000000de2800000512000000016800,Steam Deck Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,misc1:b11,paddle1:b12,paddle2:b13,paddle3:b14,paddle4:b15,touchpad:b17,misc2:b16,misc3:b19,misc4:b18,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,", "03000000de2800000512000011010000,Steam Deck,a:b3,b:b4,back:b11,dpdown:b17,dpleft:b18,dpright:b19,dpup:b16,guide:b13,leftshoulder:b7,leftstick:b14,lefttrigger:a9,leftx:a0,lefty:a1,misc1:b2,paddle1:b21,paddle2:b20,paddle3:b23,paddle4:b22,rightshoulder:b8,rightstick:b15,righttrigger:a8,rightx:a2,righty:a3,start:b12,x:b5,y:b6,", "03000000de280000ff11000001000000,Steam Virtual Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,", "0500000011010000311400001b010000,SteelSeries Stratus Duo,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b32,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,", diff --git a/third-party/SDL/src/joystick/SDL_joystick.c b/third-party/SDL/src/joystick/SDL_joystick.c index 918c2baee3..02cc012f14 100644 --- a/third-party/SDL/src/joystick/SDL_joystick.c +++ b/third-party/SDL/src/joystick/SDL_joystick.c @@ -444,11 +444,14 @@ static Uint32 initial_blacklist_devices[] = { MAKE_VIDPID(0x0e6f, 0x018a), // PDP REALMz Wireless Controller for Switch, USB charging MAKE_VIDPID(0x1532, 0x0266), // Razer Huntsman V2 Analog, non-functional DInput device MAKE_VIDPID(0x1532, 0x0282), // Razer Huntsman Mini Analog, non-functional DInput device - MAKE_VIDPID(0x26ce, 0x01a2), // ASRock LED Controller MAKE_VIDPID(0x20d6, 0x0002), // PowerA Enhanced Wireless Controller for Nintendo Switch (charging port only) - MAKE_VIDPID(0x31e3, 0x1310), // Wooting 60HE (ARM) + MAKE_VIDPID(0x256c, 0x006d), // Huion Tablet_GS1331, Huion Tablet_GS1331 Touch Strip + MAKE_VIDPID(0x256c, 0x006e), // Huion Tablet Kamvas Pro 22, Huion Tablet Kamvas Pro 22 Touch Strip + MAKE_VIDPID(0x26ce, 0x01a2), // ASRock LED Controller MAKE_VIDPID(0x3297, 0x1969), // Moonlander MK1 Keyboard + MAKE_VIDPID(0x3434, 0x0121), // Keychron Q3 System Control MAKE_VIDPID(0x3434, 0x0211), // Keychron K1 Pro System Control + MAKE_VIDPID(0x3434, 0x02a0), // Keychron K10 Pro System Control MAKE_VIDPID(0x3434, 0x0353), // Keychron V5 System Control MAKE_VIDPID(0x3434, 0xd030), // Keychron Link }; @@ -476,6 +479,8 @@ static Uint32 initial_flightstick_devices[] = { MAKE_VIDPID(0x10f5, 0x7084), // Turtle Beach VelocityOne MAKE_VIDPID(0x231d, 0x0126), // Gunfighter Mk.III 'Space Combat Edition' (right) MAKE_VIDPID(0x231d, 0x0127), // Gunfighter Mk.III 'Space Combat Edition' (left) + MAKE_VIDPID(0x3344, 0x4391), // VIRPIL Controls R-VPC Stick MT-50CM3 + MAKE_VIDPID(0x3344, 0x8390), // VIRPIL Controls L-VPC Stick MT-50CM3 MAKE_VIDPID(0x362c, 0x0001), // Yawman Arrow }; static SDL_vidpid_list flightstick_devices = { @@ -523,6 +528,7 @@ static Uint32 initial_throttle_devices[] = { MAKE_VIDPID(0x044f, 0x0404), // HOTAS Warthog Throttle MAKE_VIDPID(0x0738, 0xa221), // Saitek Pro Flight X-56 Rhino Throttle MAKE_VIDPID(0x10f5, 0x7085), // Turtle Beach VelocityOne Throttle + MAKE_VIDPID(0x3344, 0x0196), // VIRPIL Controls VPC VMAX Prime Throttle }; static SDL_vidpid_list throttle_devices = { SDL_HINT_JOYSTICK_THROTTLE_DEVICES, 0, 0, NULL, diff --git a/third-party/SDL/src/joystick/android/SDL_sysjoystick.c b/third-party/SDL/src/joystick/android/SDL_sysjoystick.c index feba3cdd32..265c32e391 100644 --- a/third-party/SDL/src/joystick/android/SDL_sysjoystick.c +++ b/third-party/SDL/src/joystick/android/SDL_sysjoystick.c @@ -29,8 +29,10 @@ #include "../../events/SDL_keyboard_c.h" #include "../../core/android/SDL_android.h" #include "../hidapi/SDL_hidapijoystick_c.h" +#include "../usb_ids.h" #include "android/keycodes.h" +#include // As of platform android-14, android/keycodes.h is missing these defines #ifndef AKEYCODE_BUTTON_1 @@ -170,6 +172,31 @@ static int keycode_to_SDL(int keycode) return button; } +static int scancode_to_SDL(int scancode) +{ + int button = 0; + switch (scancode) { + // D-Pad buttons on the left JoyCon + case BTN_DPAD_UP: + button = SDL_GAMEPAD_BUTTON_DPAD_UP; + break; + case BTN_DPAD_DOWN: + button = SDL_GAMEPAD_BUTTON_DPAD_DOWN; + break; + case BTN_DPAD_LEFT: + button = SDL_GAMEPAD_BUTTON_DPAD_LEFT; + break; + case BTN_DPAD_RIGHT: + button = SDL_GAMEPAD_BUTTON_DPAD_RIGHT; + break; + + default: + return -1; + } + SDL_assert(button < ANDROID_MAX_NBUTTONS); + return button; +} + static SDL_Scancode button_to_scancode(int button) { switch (button) { @@ -195,11 +222,14 @@ static SDL_Scancode button_to_scancode(int button) return SDL_SCANCODE_UNKNOWN; } -bool Android_OnPadDown(int device_id, int keycode) +bool Android_OnPadDown(int device_id, int keycode, int scancode) { Uint64 timestamp = SDL_GetTicksNS(); SDL_joylist_item *item; int button = keycode_to_SDL(keycode); + if (button < 0) { + button = scancode_to_SDL(scancode); + } if (button >= 0) { SDL_LockJoysticks(); item = JoystickByDeviceId(device_id); @@ -215,11 +245,14 @@ bool Android_OnPadDown(int device_id, int keycode) return false; } -bool Android_OnPadUp(int device_id, int keycode) +bool Android_OnPadUp(int device_id, int keycode, int scancode) { Uint64 timestamp = SDL_GetTicksNS(); SDL_joylist_item *item; int button = keycode_to_SDL(keycode); + if (button < 0) { + button = scancode_to_SDL(scancode); + } if (button >= 0) { SDL_LockJoysticks(); item = JoystickByDeviceId(device_id); @@ -306,7 +339,37 @@ bool Android_OnHat(int device_id, int hat_id, int x, int y) return false; } -void Android_AddJoystick(int device_id, const char *name, const char *desc, int vendor_id, int product_id, int button_mask, int naxes, int axis_mask, int nhats, bool can_rumble, bool has_rgb_led) +void Android_OnJoySensor(int device_id, int sensor_type, Uint64 sensor_timestamp, float x, float y, float z) +{ + Uint64 timestamp = SDL_GetTicksNS(); + SDL_joylist_item *item; + SDL_SensorType sensor; + float data[3]; + + if (sensor_type == 1) { // Sensor.TYPE_ACCELEROMETER + sensor = SDL_SENSOR_ACCEL; + } else if (sensor_type == 4) { // Sensor.TYPE_GYROSCOPE + sensor = SDL_SENSOR_GYRO; + } else { + // Unsupported sensor + return; + } + + // The axes of sensor events and their signs are the same as SDL's, so no conversion required + data[0] = x; + data[1] = y; + data[2] = z; + + SDL_LockJoysticks(); + item = JoystickByDeviceId(device_id); + if (item && item->joystick) { + SDL_SendJoystickSensor(timestamp, item->joystick, sensor, sensor_timestamp, data, 3); + } + SDL_UnlockJoysticks(); +} + +void Android_AddJoystick(int device_id, const char *name, const char *desc, int vendor_id, int product_id, int button_mask, int naxes, int axis_mask, int nhats, + bool can_rumble, bool has_rgb_led, bool has_accelerometer, bool has_gyroscope) { SDL_joylist_item *item; SDL_GUID guid; @@ -337,8 +400,9 @@ void Android_AddJoystick(int device_id, const char *name, const char *desc, int SDL_Log("Joystick: %s, descriptor %s, vendor = 0x%.4x, product = 0x%.4x, %d axes, %d hats", name, desc, vendor_id, product_id, naxes, nhats); #endif - if (nhats > 0) { + if (nhats > 0 || (vendor_id == USB_VENDOR_NINTENDO && product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_LEFT)) { // Hat is translated into DPAD buttons + // D-Pad on the left JoyCon is a special case, it's not recognized via keycodes or hats, only scancodes button_mask |= ((1 << SDL_GAMEPAD_BUTTON_DPAD_UP) | (1 << SDL_GAMEPAD_BUTTON_DPAD_DOWN) | (1 << SDL_GAMEPAD_BUTTON_DPAD_LEFT) | @@ -382,6 +446,8 @@ void Android_AddJoystick(int device_id, const char *name, const char *desc, int item->nhats = nhats; item->can_rumble = can_rumble; item->has_rgb_led = has_rgb_led; + item->has_accelerometer = has_accelerometer; + item->has_gyroscope = has_accelerometer; item->device_instance = SDL_GetNextObjectID(); if (!SDL_joylist_tail) { SDL_joylist = SDL_joylist_tail = item; @@ -587,6 +653,13 @@ static bool ANDROID_JoystickOpen(SDL_Joystick *joystick, int device_index) SDL_SetBooleanProperty(SDL_GetJoystickProperties(joystick), SDL_PROP_JOYSTICK_CAP_RGB_LED_BOOLEAN, true); } + if (item->has_accelerometer) { + SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL, 0.0f); + } + if (item->has_gyroscope) { + SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_GYRO, 0.0f); + } + return true; } @@ -631,7 +704,15 @@ static bool ANDROID_JoystickSendEffect(SDL_Joystick *joystick, const void *data, static bool ANDROID_JoystickSetSensorsEnabled(SDL_Joystick *joystick, bool enabled) { - return SDL_Unsupported(); + SDL_joylist_item *item = (SDL_joylist_item *)joystick->hwdata; + if (!item) { + return SDL_SetError("SetSensorsEnabled failed, device disconnected"); + } + if (!item->has_accelerometer && !item->has_gyroscope) { + return SDL_Unsupported(); + } + Android_JNI_JoystickSetSensorsEnabled(item->device_id, enabled); + return true; } static void ANDROID_JoystickUpdate(SDL_Joystick *joystick) diff --git a/third-party/SDL/src/joystick/android/SDL_sysjoystick_c.h b/third-party/SDL/src/joystick/android/SDL_sysjoystick_c.h index 48f7ae2256..f99abb5e61 100644 --- a/third-party/SDL/src/joystick/android/SDL_sysjoystick_c.h +++ b/third-party/SDL/src/joystick/android/SDL_sysjoystick_c.h @@ -28,11 +28,13 @@ #include "../SDL_sysjoystick.h" -extern bool Android_OnPadDown(int device_id, int keycode); -extern bool Android_OnPadUp(int device_id, int keycode); +extern bool Android_OnPadDown(int device_id, int keycode, int scancode); +extern bool Android_OnPadUp(int device_id, int keycode, int scancode); extern bool Android_OnJoy(int device_id, int axisnum, float value); extern bool Android_OnHat(int device_id, int hat_id, int x, int y); -extern void Android_AddJoystick(int device_id, const char *name, const char *desc, int vendor_id, int product_id, int button_mask, int naxes, int axis_mask, int nhats, bool can_rumble, bool has_rgb_led); +extern void Android_OnJoySensor(int device_id, int sensor_type, Uint64 sensor_timestamp, float x, float y, float z); +extern void Android_AddJoystick(int device_id, const char *name, const char *desc, int vendor_id, int product_id, int button_mask, int naxes, int axis_mask, int nhats, + bool can_rumble, bool has_rgb_led, bool has_accelerometer, bool has_gyroscope); extern void Android_RemoveJoystick(int device_id); // A linked list of available joysticks @@ -47,6 +49,8 @@ typedef struct SDL_joylist_item int dpad_state; bool can_rumble; bool has_rgb_led; + bool has_accelerometer; + bool has_gyroscope; struct SDL_joylist_item *next; } SDL_joylist_item; diff --git a/third-party/SDL/src/joystick/apple/SDL_mfijoystick.m b/third-party/SDL/src/joystick/apple/SDL_mfijoystick.m index c7ed381d66..98756f2eab 100644 --- a/third-party/SDL/src/joystick/apple/SDL_mfijoystick.m +++ b/third-party/SDL/src/joystick/apple/SDL_mfijoystick.m @@ -340,8 +340,6 @@ static bool IOS_AddMFIJoystickDevice(SDL_JoystickDeviceItem *device, GCControlle name = "MFi Gamepad"; } - device->name = SDL_CreateJoystickName(0, 0, NULL, name); - #ifdef DEBUG_CONTROLLER_PROFILE NSLog(@"Product name: %@\n", controller.vendorName); NSLog(@"Product category: %@\n", controller.productCategory); @@ -428,12 +426,19 @@ static bool IOS_AddMFIJoystickDevice(SDL_JoystickDeviceItem *device, GCControlle if (device->has_xbox_paddles) { // Assume Xbox One Elite Series 2 Controller unless/until GCController flows VID/PID product = USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2_BLUETOOTH; + + // controller.vendorName returns a generic name for Xbox controllers ("Controller"), + // and controller.productCategory only returns "Xbox One" for those controllers, + // so we give them proper names based on the ones from SDL_gamepad_db.h + name = "Xbox One Elite 2 Controller"; } else if (device->has_xbox_share_button) { // Assume Xbox Series X Controller unless/until GCController flows VID/PID product = USB_PRODUCT_XBOX_SERIES_X_BLE; + name = "Xbox Series X Controller"; } else { // Assume Xbox One S Bluetooth Controller unless/until GCController flows VID/PID product = USB_PRODUCT_XBOX_ONE_S_REV1_BLUETOOTH; + name = "Xbox One Wireless Controller"; } } else if (device->is_ps4) { // Assume DS4 Slim unless/until GCController flows VID/PID @@ -611,6 +616,8 @@ static bool IOS_AddMFIJoystickDevice(SDL_JoystickDeviceItem *device, GCControlle // We don't know how to get input events from this device return false; } + + device->name = SDL_CreateJoystickName(0, 0, NULL, name); Uint16 signature; if (@available(macOS 11.0, iOS 14.0, tvOS 14.0, *)) { @@ -1059,13 +1066,7 @@ static void IOS_MFIJoystickUpdate(SDL_Joystick *joystick) int button = 0; for (id key in device->buttons) { - bool down; - if (button == device->pause_button_index) { - down = (device->pause_button_pressed > 0); - } else { - down = buttons[key].isPressed; - } - SDL_SendJoystickButton(timestamp, joystick, button++, down); + SDL_SendJoystickButton(timestamp, joystick, button++, buttons[key].isPressed); } } else if (controller.extendedGamepad) { bool isstack; @@ -1772,6 +1773,11 @@ bool IOS_SupportedHIDDevice(IOHIDDeviceRef device) return false; } + if (IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVirtualHIDevice)) == kCFBooleanTrue) { + // Steam virtual gamepads always have kIOHIDVirtualHIDevice property unlike real devices, and are also not supported as GCController + return false; + } + if (@available(macOS 11.0, *)) { const int MAX_ATTEMPTS = 3; for (int attempt = 0; attempt < MAX_ATTEMPTS; ++attempt) { diff --git a/third-party/SDL/src/joystick/check_8bitdo.sh b/third-party/SDL/src/joystick/check_8bitdo.sh old mode 100644 new mode 100755 diff --git a/third-party/SDL/src/joystick/controller_list.h b/third-party/SDL/src/joystick/controller_list.h index ca7b85646e..e71dc78248 100644 --- a/third-party/SDL/src/joystick/controller_list.h +++ b/third-party/SDL/src/joystick/controller_list.h @@ -329,6 +329,7 @@ static const ControllerDescription_t arrControllers[] = { { MAKE_CONTROLLER_ID( 0x24c6, 0xfafb ), k_eControllerType_XBox360Controller, NULL }, // Aplay Controller { MAKE_CONTROLLER_ID( 0x24c6, 0xfafc ), k_eControllerType_XBox360Controller, NULL }, // Afterglow Gamepad 1 { MAKE_CONTROLLER_ID( 0x24c6, 0xfafd ), k_eControllerType_XBox360Controller, NULL }, // Afterglow Gamepad 3 + { MAKE_CONTROLLER_ID( 0x3537, 0x103e ), k_eControllerType_XBox360Controller, "Gamesir Xbox Controller" }, // Gamesir dongle for Supernova (and maybe others) in Xinput mode { MAKE_CONTROLLER_ID( 0x24c6, 0xfafe ), k_eControllerType_XBox360Controller, NULL }, // Rock Candy Gamepad for Xbox 360 { MAKE_CONTROLLER_ID( 0x03f0, 0x0495 ), k_eControllerType_XBoxOneController, NULL }, // HP HyperX Clutch Gladiate @@ -604,6 +605,7 @@ static const ControllerDescription_t arrControllers[] = { { MAKE_CONTROLLER_ID( 0x0e6f, 0x0186 ), k_eControllerType_SwitchProController, NULL }, // PDP Afterglow Wireless Switch Controller - working gyro. USB is for charging only. Many later "Wireless" line devices w/ gyro also use this vid/pid { MAKE_CONTROLLER_ID( 0x0e6f, 0x0187 ), k_eControllerType_SwitchInputOnlyController, NULL }, // PDP Rockcandy Wired Controller { MAKE_CONTROLLER_ID( 0x0e6f, 0x0188 ), k_eControllerType_SwitchInputOnlyController, NULL }, // PDP Afterglow Wired Deluxe+ Audio Controller + { MAKE_CONTROLLER_ID( 0x0e6f, 0x018b ), k_eControllerType_SwitchProController, NULL }, // PDP Afterglow Wave Wireless Controller for Switch { MAKE_CONTROLLER_ID( 0x0e6f, 0x018c ), k_eControllerType_SwitchProController, "PDP REALMz Wireless Controller" }, // PDP REALMz Wireless Controller for Switch { MAKE_CONTROLLER_ID( 0x0f0d, 0x00aa ), k_eControllerType_SwitchInputOnlyController, NULL }, // HORI Real Arcade Pro V Hayabusa in Switch Mode { MAKE_CONTROLLER_ID( 0x20d6, 0xa711 ), k_eControllerType_SwitchInputOnlyController, NULL }, // PowerA Wired Controller Plus/PowerA Wired Controller Nintendo GameCube Style diff --git a/third-party/SDL/src/joystick/darwin/SDL_iokitjoystick.c b/third-party/SDL/src/joystick/darwin/SDL_iokitjoystick.c index 209f7d8205..b88258f18d 100644 --- a/third-party/SDL/src/joystick/darwin/SDL_iokitjoystick.c +++ b/third-party/SDL/src/joystick/darwin/SDL_iokitjoystick.c @@ -28,6 +28,8 @@ #include "../hidapi/SDL_hidapijoystick_c.h" #include "../../haptic/darwin/SDL_syshaptic_c.h" // For haptic hot plugging #include "../usb_ids.h" +#include "../../SDL_hints_c.h" +#include #define SDL_JOYSTICK_RUNLOOP_MODE CFSTR("SDLJoystick") @@ -442,6 +444,16 @@ static int GetSteamVirtualGamepadSlot(Uint16 vendor_id, Uint16 product_id, const return slot; } +static bool IsControlledBy360ControllerDriver(IOHIDDeviceRef hidDevice) +{ + bool controlled_by_360controller = false; + io_service_t service = IOHIDDeviceGetService(hidDevice); + if (service != MACH_PORT_NULL) { + controlled_by_360controller = IOObjectConformsTo(service, "Xbox360ControllerClass"); + } + return controlled_by_360controller; +} + static bool GetDeviceInfo(IOHIDDeviceRef hidDevice, recDevice *pDevice) { Sint32 vendor = 0; @@ -499,11 +511,18 @@ static bool GetDeviceInfo(IOHIDDeviceRef hidDevice, recDevice *pDevice) CFNumberGetValue(refCF, kCFNumberSInt32Type, &version); } - if (SDL_IsJoystickXboxOne(vendor, product)) { - // We can't actually use this API for Xbox controllers + if (!IsControlledBy360ControllerDriver(hidDevice) && SDL_IsJoystickXboxOne(vendor, product)) { + // We can't actually use this API for Xbox controllers without the 360Controller driver return false; } + if (SDL_IsJoystickSteamVirtualGamepad(vendor, product, version)) { + if (IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDVirtualHIDevice)) != kCFBooleanTrue) { + // This is a real Xbox 360 controller, adjust the version so it's not detected as a Steam virtual gamepad + version = 1; + } + } + // get device name refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDManufacturerKey)); if ((!refCF) || (!CFStringGetCString(refCF, manufacturer_string, sizeof(manufacturer_string), kCFStringEncodingUTF8))) { @@ -550,7 +569,7 @@ static bool JoystickAlreadyKnown(IOHIDDeviceRef ioHIDDeviceObject) #ifdef SDL_JOYSTICK_MFI extern bool IOS_SupportedHIDDevice(IOHIDDeviceRef device); - if (IOS_SupportedHIDDevice(ioHIDDeviceObject)) { + if (!IsControlledBy360ControllerDriver(ioHIDDeviceObject) && IOS_SupportedHIDDevice(ioHIDDeviceObject)) { return true; } #endif diff --git a/third-party/SDL/src/joystick/emscripten/SDL_sysjoystick.c b/third-party/SDL/src/joystick/emscripten/SDL_sysjoystick.c index 5d831bddbb..627a329594 100644 --- a/third-party/SDL/src/joystick/emscripten/SDL_sysjoystick.c +++ b/third-party/SDL/src/joystick/emscripten/SDL_sysjoystick.c @@ -100,11 +100,33 @@ static int SDL_IsEmscriptenJoystickXInput(int device_index) }, device_index); } +static int SDL_GetEmscriptenOSID() +{ + return MAIN_THREAD_EM_ASM_INT({ + const os = ([ + 'Android', + 'Linux', + 'iPhone', + 'Macintosh', + 'Windows', + ]); + const ua = navigator['userAgent']; + for (let i = 0; i < os.length; i++) { + if (ua['indexOf'](os[i]) >= 0) { + return i + 1; + } + } + return 0; + }); +} + static EM_BOOL Emscripten_JoyStickConnected(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData) { SDL_joylist_item *item; int i; + Uint16 bus; Uint16 vendor, product; + Uint8 os_id; bool is_xinput; SDL_LockJoysticks(); @@ -121,6 +143,7 @@ static EM_BOOL Emscripten_JoyStickConnected(int eventType, const EmscriptenGamep SDL_zerop(item); item->index = gamepadEvent->index; + bus = SDL_HARDWARE_BUS_UNKNOWN; vendor = SDL_GetEmscriptenJoystickVendor(gamepadEvent->index); product = SDL_GetEmscriptenJoystickProduct(gamepadEvent->index); is_xinput = SDL_IsEmscriptenJoystickXInput(gamepadEvent->index); @@ -130,6 +153,21 @@ static EM_BOOL Emscripten_JoyStickConnected(int eventType, const EmscriptenGamep vendor = USB_VENDOR_MICROSOFT; product = USB_PRODUCT_XBOX360_XUSB_CONTROLLER; } + + os_id = SDL_GetEmscriptenOSID(); + + if (os_id != 0) { + if (os_id == 1 || os_id == 3) { // Android or iOS (mobile) + bus = SDL_HARDWARE_BUS_BLUETOOTH; + } else { // Desktop + bus = SDL_HARDWARE_BUS_USB; + } + } + + if (SDL_strcmp(gamepadEvent->mapping, "standard") == 0) { + // We should differentiate between devices that are mapped or unmapped by the browser. + os_id += 0x80; + } item->name = SDL_CreateJoystickName(vendor, product, NULL, gamepadEvent->id); if (!item->name) { @@ -138,9 +176,10 @@ static EM_BOOL Emscripten_JoyStickConnected(int eventType, const EmscriptenGamep } if (vendor && product) { - item->guid = SDL_CreateJoystickGUID(SDL_HARDWARE_BUS_UNKNOWN, vendor, product, 0, NULL, item->name, 0, 0); + item->guid = SDL_CreateJoystickGUID(bus, vendor, product, 0, NULL, gamepadEvent->id, 0, os_id); } else { item->guid = SDL_CreateJoystickGUIDForName(item->name); + item->guid.data[15] = os_id; } if (is_xinput) { @@ -448,7 +487,6 @@ static SDL_JoystickID EMSCRIPTEN_JoystickGetDeviceInstanceID(int device_index) static bool EMSCRIPTEN_JoystickOpen(SDL_Joystick *joystick, int device_index) { SDL_joylist_item *item = JoystickByDeviceIndex(device_index); - bool rumble_available = false; if (!item) { return SDL_SetError("No such device"); @@ -466,22 +504,24 @@ static bool EMSCRIPTEN_JoystickOpen(SDL_Joystick *joystick, int device_index) joystick->nbuttons = item->nbuttons; joystick->naxes = item->naxes; - rumble_available = MAIN_THREAD_EM_ASM_INT({ - let gamepads = navigator['getGamepads'](); - if (!gamepads) { - return 0; - } - let gamepad = gamepads[$0]; - if (!gamepad || !gamepad['vibrationActuator']) { - return 0; - } - return 1; + item->rumble_available = MAIN_THREAD_EM_ASM_INT({ + let gamepad = navigator['getGamepads']()[$0]; + return gamepad && gamepad['vibrationActuator'] && gamepad['vibrationActuator']['effects'] && gamepad['vibrationActuator']['effects']['includes']('dual-rumble'); }, item->index); - if (rumble_available) { + if (item->rumble_available) { SDL_SetBooleanProperty(SDL_GetJoystickProperties(joystick), SDL_PROP_JOYSTICK_CAP_RUMBLE_BOOLEAN, true); } + item->trigger_rumble_available = MAIN_THREAD_EM_ASM_INT({ + let gamepad = navigator['getGamepads']()[$0]; + return gamepad && gamepad['vibrationActuator'] && gamepad['vibrationActuator']['effects'] && gamepad['vibrationActuator']['effects']['includes']('trigger-rumble'); + }, item->index); + + if (item->trigger_rumble_available) { + SDL_SetBooleanProperty(SDL_GetJoystickProperties(joystick), SDL_PROP_JOYSTICK_CAP_TRIGGER_RUMBLE_BOOLEAN, true); + } + return true; } @@ -582,36 +622,49 @@ static SDL_GUID EMSCRIPTEN_JoystickGetDeviceGUID(int device_index) return JoystickByDeviceIndex(device_index)->guid; } -static bool EMSCRIPTEN_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble) +static bool Emscripten_UpdateRumble(SDL_joylist_item *item) { - SDL_joylist_item *item = (SDL_joylist_item *)joystick->hwdata; - - // clang-format off bool result = MAIN_THREAD_EM_ASM_INT({ - let gamepads = navigator['getGamepads'](); - if (!gamepads) { - return 0; + let gamepad = navigator['getGamepads']()[$0]; + if (!gamepad) { + return false; } - let gamepad = gamepads[$0]; - if (!gamepad || !gamepad['vibrationActuator']) { - return 0; - } - + // We check if rumble is available in EMSCRIPTEN_JoystickRumble() and EMSCRIPTEN_JoystickRumbleTriggers(). + // From my testing using "dual-rumble" here covers both main rumble and trigger rumble. gamepad['vibrationActuator']['playEffect']('dual-rumble', { 'startDelay': 0, 'duration': 3000, - 'weakMagnitude': $2 / 0xFFFF, - 'strongMagnitude': $1 / 0xFFFF, + 'weakMagnitude': $1 / 0xFFFF, + 'strongMagnitude': $2 / 0xFFFF, + 'leftTrigger': $3 / 0xFFFF, + 'rightTrigger': $4 / 0xFFFF, }); - return 1; - }, item->index, low_frequency_rumble, high_frequency_rumble); + return true; + }, item->index, item->weak_magnitude_rumble, item->strong_magnitude_rumble, item->left_trigger_rumble, item->right_trigger_rumble); return result; } +static bool EMSCRIPTEN_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble) +{ + SDL_joylist_item *item = (SDL_joylist_item *)joystick->hwdata; + if (!item || !item->rumble_available) { + return SDL_Unsupported(); + } + item->strong_magnitude_rumble = low_frequency_rumble; + item->weak_magnitude_rumble = high_frequency_rumble; + return Emscripten_UpdateRumble(item); +} + static bool EMSCRIPTEN_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble) { - return SDL_Unsupported(); + SDL_joylist_item *item = (SDL_joylist_item *)joystick->hwdata; + if (!item || !item->trigger_rumble_available) { + return SDL_Unsupported(); + } + item->left_trigger_rumble = left_rumble; + item->right_trigger_rumble = right_rumble; + return Emscripten_UpdateRumble(item); } static bool EMSCRIPTEN_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out) diff --git a/third-party/SDL/src/joystick/emscripten/SDL_sysjoystick_c.h b/third-party/SDL/src/joystick/emscripten/SDL_sysjoystick_c.h index df1fda85ae..c23d544748 100644 --- a/third-party/SDL/src/joystick/emscripten/SDL_sysjoystick_c.h +++ b/third-party/SDL/src/joystick/emscripten/SDL_sysjoystick_c.h @@ -46,6 +46,12 @@ typedef struct SDL_joylist_item double analogButton[64]; EM_BOOL digitalButton[64]; Uint8 hat; // there is (currently) only ever one of these, faked from the d-pad buttons. + bool rumble_available; + bool trigger_rumble_available; + Uint16 weak_magnitude_rumble; + Uint16 strong_magnitude_rumble; + Uint16 left_trigger_rumble; + Uint16 right_trigger_rumble; struct SDL_joylist_item *next; } SDL_joylist_item; diff --git a/third-party/SDL/src/joystick/gdk/SDL_gameinputjoystick.cpp b/third-party/SDL/src/joystick/gdk/SDL_gameinputjoystick.cpp index 5d373a2550..c6c2b8ed06 100644 --- a/third-party/SDL/src/joystick/gdk/SDL_gameinputjoystick.cpp +++ b/third-party/SDL/src/joystick/gdk/SDL_gameinputjoystick.cpp @@ -34,11 +34,6 @@ #define SDL_GAMEINPUT_DEFAULT false #endif -// Enable sensor support in GameInput 2.0, once we have a device that can be used for testing -#if GAMEINPUT_API_VERSION >= 2 -//#define GAMEINPUT_SENSOR_SUPPORT -#endif - enum { SDL_GAMEPAD_BUTTON_GAMEINPUT_SHARE = 11 @@ -419,9 +414,11 @@ static bool GAMEINPUT_JoystickInit(void) if (SDL_GetHintBoolean(SDL_HINT_JOYSTICK_GAMEINPUT, SDL_GAMEINPUT_DEFAULT)) { kind |= GameInputKindController; } +#if GAMEINPUT_API_VERSION >= 3 if (GAMEINPUT_IsRawGameInputEnabled()) { kind |= GameInputKindRawDeviceReport; } +#endif hr = g_pGameInput->RegisterDeviceCallback(NULL, kind, @@ -647,17 +644,17 @@ static bool GAMEINPUT_JoystickOpen(SDL_Joystick *joystick, int device_index) SDL_SetBooleanProperty(SDL_GetJoystickProperties(joystick), SDL_PROP_JOYSTICK_CAP_TRIGGER_RUMBLE_BOOLEAN, true); } -#ifdef GAMEINPUT_SENSOR_SUPPORT +#if GAMEINPUT_API_VERSION >= 3 if (info->supportedInput & GameInputKindSensors) { - // FIXME: What's the sensor update rate? if (info->sensorsInfo->supportedSensors & GameInputSensorsGyrometer) { - SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_GYRO, 250.0f); + SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_GYRO, 60.0f); } if (info->sensorsInfo->supportedSensors & GameInputSensorsAccelerometer) { - SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL, 250.0f); + SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL, 60.0f); } } -#endif +#endif // GAMEINPUT_API_VERSION >= 3 + return true; } @@ -701,7 +698,8 @@ static bool GAMEINPUT_JoystickSetSensorsEnabled(SDL_Joystick *joystick, bool ena static void GAMEINPUT_GuitarUpdate(SDL_Joystick *joystick, IGameInputReading *reading, Uint64 timestamp) { - IGameInputRawDeviceReport* rawState; +#if GAMEINPUT_API_VERSION >= 3 + IGameInputRawDeviceReport *rawState; if (reading->GetRawReport(&rawState)) { static WORD s_GuitarButtons[] = { 0x0010, // SDL_GAMEPAD_BUTTON_SOUTH @@ -753,6 +751,7 @@ static void GAMEINPUT_GuitarUpdate(SDL_Joystick *joystick, IGameInputReading *re SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, effects_mappings[rawData[4] >> 4]); } } +#endif // GAMEINPUT_API_VERSION >= 3 } static void GAMEINPUT_GamepadUpdate(SDL_Joystick *joystick, IGameInputReading *reading, Uint64 timestamp) { @@ -901,7 +900,7 @@ static void GAMEINPUT_JoystickUpdate(SDL_Joystick *joystick) GAMEINPUT_ControllerUpdate(joystick, reading, timestamp); } -#ifdef GAMEINPUT_SENSOR_SUPPORT +#if GAMEINPUT_API_VERSION >= 3 if (hwdata->report_sensors) { GameInputSensorsState sensor_state; @@ -924,7 +923,7 @@ static void GAMEINPUT_JoystickUpdate(SDL_Joystick *joystick) } } } -#endif // GAMEINPUT_SENSOR_SUPPORT +#endif // GAMEINPUT_API_VERSION >= 3 reading->Release(); diff --git a/third-party/SDL/src/joystick/hidapi/SDL_hidapi_8bitdo.c b/third-party/SDL/src/joystick/hidapi/SDL_hidapi_8bitdo.c index 8d70f4aaab..6b1dece533 100644 --- a/third-party/SDL/src/joystick/hidapi/SDL_hidapi_8bitdo.c +++ b/third-party/SDL/src/joystick/hidapi/SDL_hidapi_8bitdo.c @@ -595,7 +595,7 @@ static void HIDAPI_Driver8BitDo_HandleStatePacket(SDL_Joystick *joystick, SDL_Dr Uint32 tick = LOAD32(data[27], data[28], data[29], data[30]); if (ctx->last_tick) { - if (ctx->last_tick < tick) { + if (ctx->last_tick <= tick) { delta = (tick - ctx->last_tick); } else { delta = (SDL_MAX_UINT32 - ctx->last_tick + tick + 1); diff --git a/third-party/SDL/src/joystick/hidapi/SDL_hidapi_flydigi.c b/third-party/SDL/src/joystick/hidapi/SDL_hidapi_flydigi.c index a4958f6935..989609ca53 100644 --- a/third-party/SDL/src/joystick/hidapi/SDL_hidapi_flydigi.c +++ b/third-party/SDL/src/joystick/hidapi/SDL_hidapi_flydigi.c @@ -295,6 +295,24 @@ static void HIDAPI_DriverFlydigi_SetAvailable(SDL_HIDAPI_Device *device, bool av ctx->available = available; } +static int HIDAPI_DriverFlydigi_WritePacket(SDL_HIDAPI_Device *device, const Uint8 *data, size_t size) +{ + // We know that at the very least the Vader 5 now uses unnumbered reports for commands instead of FLYDIGI_V2_CMD_REPORT_ID. + // If other Flydigi things prove to do the same, we can tweak this check to be more general. + bool bUsesUnnumberedReports = (device->vendor_id == USB_VENDOR_FLYDIGI_V2 && device->product_id == USB_PRODUCT_FLYDIGI_V2_VADER); + + if (bUsesUnnumberedReports && data[0] == FLYDIGI_V2_CMD_REPORT_ID) { + // Zero out the report byte. + Uint8 output[USB_PACKET_LENGTH]; + SDL_memcpy(output, data, SDL_min(size, USB_PACKET_LENGTH)); + output[0] = 0; + return SDL_hid_write(device->dev, output, SDL_min(size, USB_PACKET_LENGTH)); + } + + return SDL_hid_write(device->dev, data, size); +} + + static bool HIDAPI_DriverFlydigi_InitControllerV1(SDL_HIDAPI_Device *device) { SDL_DriverFlydigi_Context *ctx = (SDL_DriverFlydigi_Context *)device->context; @@ -303,7 +321,7 @@ static bool HIDAPI_DriverFlydigi_InitControllerV1(SDL_HIDAPI_Device *device) for (int attempt = 0; ctx->deviceID == 0 && attempt < 30; ++attempt) { const Uint8 request[] = { FLYDIGI_V1_CMD_REPORT_ID, FLYDIGI_V1_GET_INFO_COMMAND, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; // This write will occasionally return -1, so ignore failure here and try again - (void)SDL_hid_write(device->dev, request, sizeof(request)); + (void)HIDAPI_DriverFlydigi_WritePacket(device, request, sizeof(request)); // Read the reply for (int i = 0; i < 100; ++i) { @@ -399,7 +417,8 @@ static bool SDL_HIDAPI_Flydigi_SendInfoRequest(SDL_HIDAPI_Device *device) 2, 0 }; - if (SDL_hid_write(device->dev, cmd, sizeof(cmd)) < 0) { + + if (HIDAPI_DriverFlydigi_WritePacket(device, cmd, sizeof(cmd)) < 0) { return SDL_SetError("Couldn't query controller info"); } return true; @@ -441,7 +460,8 @@ static bool SDL_HIDAPI_Flydigi_SendStatusRequest(SDL_HIDAPI_Device *device) FLYDIGI_V2_MAGIC2, FLYDIGI_V2_GET_STATUS_COMMAND }; - if (SDL_hid_write(device->dev, cmd, sizeof(cmd)) < 0) { + + if (HIDAPI_DriverFlydigi_WritePacket(device, cmd, sizeof(cmd)) < 0) { return SDL_SetError("Couldn't query controller status"); } return true; @@ -469,7 +489,7 @@ static bool SDL_HIDAPI_Flydigi_SendAcquireRequest(SDL_HIDAPI_Device *device, boo 'S', 'D', 'L', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - if (SDL_hid_write(device->dev, cmd, sizeof(cmd)) < 0) { + if (HIDAPI_DriverFlydigi_WritePacket(device, cmd, sizeof(cmd)) < 0) { return SDL_SetError("Couldn't send acquire command"); } return true; diff --git a/third-party/SDL/src/joystick/hidapi/SDL_hidapi_gamecube.c b/third-party/SDL/src/joystick/hidapi/SDL_hidapi_gamecube.c index 5b39b8b03e..3b41e82334 100644 --- a/third-party/SDL/src/joystick/hidapi/SDL_hidapi_gamecube.c +++ b/third-party/SDL/src/joystick/hidapi/SDL_hidapi_gamecube.c @@ -37,6 +37,7 @@ #endif #define MAX_CONTROLLERS 4 +#define PC_RUMBLE_REFRESH 16 typedef struct { @@ -50,6 +51,8 @@ typedef struct // Without this variable, hid_write starts to lag a TON bool rumbleUpdate; bool useRumbleBrake; + bool rumbleActive; + Uint64 pc_rumble_sent; } SDL_DriverGameCube_Context; static void HIDAPI_DriverGameCube_RegisterHints(SDL_HintCallback callback, void *userdata) @@ -202,12 +205,26 @@ static bool HIDAPI_DriverGameCube_InitDevice(SDL_HIDAPI_Device *device) } if (ctx->pc_mode) { + // Check to see if this firmware supports rumble + bool rumbleAllowed = false; + if ((size = SDL_hid_read_timeout(device->dev, packet, sizeof(packet), 0)) > 0) { +#ifdef DEBUG_GAMECUBE_PROTOCOL + HIDAPI_DumpPacket("Nintendo GameCube packet: size = %d", packet, size); +#endif + if (size == 9) { + // This is firmware version 0x7 or newer + // Rumble is supported if the second USB cable is plugged in + rumbleAllowed = true; + } + } #ifdef SDL_PLATFORM_WIN32 // We get a separate device for each slot + ctx->rumbleAllowed[0] = rumbleAllowed; ResetAxisRange(ctx, 0); HIDAPI_JoystickConnected(device, &ctx->joysticks[0]); #else for (i = 0; i < MAX_CONTROLLERS; ++i) { + ctx->rumbleAllowed[i] = rumbleAllowed; ResetAxisRange(ctx, i); HIDAPI_JoystickConnected(device, &ctx->joysticks[i]); } @@ -427,6 +444,14 @@ static void HIDAPI_DriverGameCube_HandleNintendoPacket(SDL_HIDAPI_Device *device } } +static void HIDAPI_DriverGameCube_SendPCRumble(SDL_HIDAPI_Device *device, SDL_DriverGameCube_Context *ctx) +{ + Uint8 rumblepkt[3] = { 0x00, 0x00, 0x00 }; + rumblepkt[1] = rumblepkt[2] = ctx->rumbleActive ? 0xFF : 0x00; + SDL_HIDAPI_SendRumble(device, rumblepkt, sizeof(rumblepkt)); + ctx->pc_rumble_sent = SDL_GetTicks(); +} + static bool HIDAPI_DriverGameCube_UpdateDevice(SDL_HIDAPI_Device *device) { SDL_DriverGameCube_Context *ctx = (SDL_DriverGameCube_Context *)device->context; @@ -456,6 +481,11 @@ static bool HIDAPI_DriverGameCube_UpdateDevice(SDL_HIDAPI_Device *device) } } + // PC_Mode rumble needs constant packets in order to keep rumble going + if (ctx->pc_mode && ctx->rumbleActive && SDL_GetTicks() >= (ctx->pc_rumble_sent + PC_RUMBLE_REFRESH)) { + HIDAPI_DriverGameCube_SendPCRumble(device, ctx); + } + // Write rumble packet if (ctx->rumbleUpdate) { SDL_HIDAPI_SendRumble(device, ctx->rumble, sizeof(ctx->rumble)); @@ -496,33 +526,51 @@ static bool HIDAPI_DriverGameCube_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_ SDL_AssertJoysticksLocked(); if (ctx->pc_mode) { - return SDL_Unsupported(); - } - - for (i = 0; i < MAX_CONTROLLERS; i += 1) { - if (joystick->instance_id == ctx->joysticks[i]) { - if (ctx->wireless[i]) { - return SDL_SetError("Nintendo GameCube WaveBird controllers do not support rumble"); - } - if (!ctx->rumbleAllowed[i]) { - return SDL_SetError("Second USB cable for WUP-028 not connected"); - } - if (ctx->useRumbleBrake) { - if (low_frequency_rumble == 0 && high_frequency_rumble > 0) { - val = 0; // if only low is 0 we want to do a regular stop - } else if (low_frequency_rumble == 0 && high_frequency_rumble == 0) { - val = 2; // if both frequencies are 0 we want to do a hard stop - } else { - val = 1; // normal rumble + for (i = 0; i < MAX_CONTROLLERS; i += 1) { + if (joystick->instance_id == ctx->joysticks[i]) { + if (!ctx->rumbleAllowed[i]) { + return SDL_SetError("Rumble is disabled because the adapter is not at least of firmware 0x7"); } - } else { - val = (low_frequency_rumble > 0 || high_frequency_rumble > 0); + bool shouldrumble = false, coast = false; + if (ctx->useRumbleBrake) { + coast = (low_frequency_rumble == 0 && high_frequency_rumble > 0); + } + shouldrumble = (low_frequency_rumble > 0 || high_frequency_rumble > 0); + if (coast) { + ctx->rumbleActive = false; + } else if (shouldrumble != ctx->rumbleActive) { + ctx->rumbleActive = shouldrumble; + HIDAPI_DriverGameCube_SendPCRumble(device, ctx); + } + return true; } - if (val != ctx->rumble[i + 1]) { - ctx->rumble[i + 1] = val; - ctx->rumbleUpdate = true; + } + } else { + for (i = 0; i < MAX_CONTROLLERS; i += 1) { + if (joystick->instance_id == ctx->joysticks[i]) { + if (ctx->wireless[i]) { + return SDL_SetError("Nintendo GameCube WaveBird controllers do not support rumble"); + } + if (!ctx->rumbleAllowed[i]) { + return SDL_SetError("Second USB cable for WUP-028 not connected"); + } + if (ctx->useRumbleBrake) { + if (low_frequency_rumble == 0 && high_frequency_rumble > 0) { + val = 0; // if only low is 0 we want to do a regular stop + } else if (low_frequency_rumble == 0 && high_frequency_rumble == 0) { + val = 2; // if both frequencies are 0 we want to do a hard stop + } else { + val = 1; // normal rumble + } + } else { + val = (low_frequency_rumble > 0 || high_frequency_rumble > 0); + } + if (val != ctx->rumble[i + 1]) { + ctx->rumble[i + 1] = val; + ctx->rumbleUpdate = true; + } + return true; } - return true; } } @@ -541,10 +589,17 @@ static Uint32 HIDAPI_DriverGameCube_GetJoystickCapabilities(SDL_HIDAPI_Device *d Uint32 result = 0; SDL_AssertJoysticksLocked(); - - if (!ctx->pc_mode) { - Uint8 i; - + Uint8 i; + if (ctx->pc_mode) { + for (i = 0; i < MAX_CONTROLLERS; i += 1) { + if (joystick->instance_id == ctx->joysticks[i]) { + if (ctx->rumbleAllowed[i]) { + result |= SDL_JOYSTICK_CAP_RUMBLE; + break; + } + } + } + } else { for (i = 0; i < MAX_CONTROLLERS; i += 1) { if (joystick->instance_id == ctx->joysticks[i]) { if (!ctx->wireless[i] && ctx->rumbleAllowed[i]) { @@ -582,6 +637,11 @@ static void HIDAPI_DriverGameCube_CloseJoystick(SDL_HIDAPI_Device *device, SDL_J SDL_HIDAPI_SendRumble(device, ctx->rumble, sizeof(ctx->rumble)); ctx->rumbleUpdate = false; } + + if (ctx->pc_mode && ctx->rumbleActive) { + ctx->rumbleActive = false; + HIDAPI_DriverGameCube_SendPCRumble(device, ctx); + } } static void HIDAPI_DriverGameCube_FreeDevice(SDL_HIDAPI_Device *device) diff --git a/third-party/SDL/src/joystick/hidapi/SDL_hidapi_ps4.c b/third-party/SDL/src/joystick/hidapi/SDL_hidapi_ps4.c index 62c9ff6273..188a40cb1b 100644 --- a/third-party/SDL/src/joystick/hidapi/SDL_hidapi_ps4.c +++ b/third-party/SDL/src/joystick/hidapi/SDL_hidapi_ps4.c @@ -117,6 +117,14 @@ typedef struct Uint8 ucVolumeSpeaker; } DS4EffectsState_t; +// These enum values match with the validity flags in the PS4 output report +typedef enum +{ + k_EPS4EffectRumble = (1 << 0), + k_EPS4EffectLED = (1 << 1), + k_EPS4EffectLEDBlink = (1 << 2), +} EPS4Effect; + typedef struct { Sint16 bias; @@ -178,7 +186,7 @@ typedef struct PS4StatePacket_t last_state; } SDL_DriverPS4_Context; -static bool HIDAPI_DriverPS4_InternalSendJoystickEffect(SDL_DriverPS4_Context *ctx, const void *effect, int size, bool application_usage); +static bool HIDAPI_DriverPS4_InternalSendJoystickEffect(SDL_DriverPS4_Context *ctx, const void *effect, int size, EPS4Effect mask, bool application_usage); static void HIDAPI_DriverPS4_RegisterHints(SDL_HintCallback callback, void *userdata) { @@ -661,7 +669,7 @@ static float HIDAPI_DriverPS4_ApplyCalibrationData(SDL_DriverPS4_Context *ctx, i return ((float)value - calibration->bias) * calibration->scale; } -static bool HIDAPI_DriverPS4_UpdateEffects(SDL_DriverPS4_Context *ctx, bool application_usage) +static bool HIDAPI_DriverPS4_UpdateEffects(SDL_DriverPS4_Context *ctx, EPS4Effect mask, bool application_usage) { DS4EffectsState_t effects; @@ -682,7 +690,7 @@ static bool HIDAPI_DriverPS4_UpdateEffects(SDL_DriverPS4_Context *ctx, bool appl SetLedsForPlayerIndex(&effects, ctx->player_index); } } - return HIDAPI_DriverPS4_InternalSendJoystickEffect(ctx, &effects, sizeof(effects), application_usage); + return HIDAPI_DriverPS4_InternalSendJoystickEffect(ctx, &effects, sizeof(effects), mask, application_usage); } static void HIDAPI_DriverPS4_TickleBluetooth(SDL_HIDAPI_Device *device) @@ -749,7 +757,7 @@ static void HIDAPI_DriverPS4_SetEnhancedMode(SDL_DriverPS4_Context *ctx) ctx->enhanced_mode = true; // Switch into enhanced report mode - HIDAPI_DriverPS4_UpdateEffects(ctx, false); + HIDAPI_DriverPS4_UpdateEffects(ctx, 0, false); } } @@ -825,7 +833,7 @@ static void SDLCALL SDL_PS4ReportIntervalHintChanged(void *userdata, const char if (new_report_interval != ctx->report_interval) { ctx->report_interval = (Uint8)new_report_interval; - HIDAPI_DriverPS4_UpdateEffects(ctx, false); + HIDAPI_DriverPS4_UpdateEffects(ctx, 0, false); SDL_LockJoysticks(); SDL_PrivateJoystickSensorRate(ctx->joystick, SDL_SENSOR_GYRO, (float)(1000 / ctx->report_interval)); SDL_PrivateJoystickSensorRate(ctx->joystick, SDL_SENSOR_ACCEL, (float)(1000 / ctx->report_interval)); @@ -845,7 +853,7 @@ static void HIDAPI_DriverPS4_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL // This will set the new LED state based on the new player index // SDL automatically calls this, so it doesn't count as an application action to enable enhanced mode - HIDAPI_DriverPS4_UpdateEffects(ctx, false); + HIDAPI_DriverPS4_UpdateEffects(ctx, k_EPS4EffectLED, false); } static bool HIDAPI_DriverPS4_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) @@ -892,7 +900,7 @@ static bool HIDAPI_DriverPS4_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joyst ctx->rumble_left = (low_frequency_rumble >> 8); ctx->rumble_right = (high_frequency_rumble >> 8); - return HIDAPI_DriverPS4_UpdateEffects(ctx, true); + return HIDAPI_DriverPS4_UpdateEffects(ctx, k_EPS4EffectRumble, true); } static bool HIDAPI_DriverPS4_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble) @@ -930,10 +938,10 @@ static bool HIDAPI_DriverPS4_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joyst ctx->led_green = green; ctx->led_blue = blue; - return HIDAPI_DriverPS4_UpdateEffects(ctx, true); + return HIDAPI_DriverPS4_UpdateEffects(ctx, k_EPS4EffectLED, true); } -static bool HIDAPI_DriverPS4_InternalSendJoystickEffect(SDL_DriverPS4_Context *ctx, const void *effect, int size, bool application_usage) +static bool HIDAPI_DriverPS4_InternalSendJoystickEffect(SDL_DriverPS4_Context *ctx, const void *effect, int size, EPS4Effect mask, bool application_usage) { Uint8 data[78]; int report_size, offset; @@ -959,13 +967,21 @@ static bool HIDAPI_DriverPS4_InternalSendJoystickEffect(SDL_DriverPS4_Context *c if (ctx->device->is_bluetooth && ctx->official_controller) { data[0] = k_EPS4ReportIdBluetoothEffects; data[1] = 0xC0 | ctx->report_interval; // Magic value HID + CRC, also sets update interval - data[3] = 0x03; // 0x1 is rumble, 0x2 is lightbar, 0x4 is the blink interval + + // Some third-party PS4 gamepads expect to receive both rumble and LED together, + // so we will only send them separately to official Sony gamepads. Unfortunately, + // this means that we'll fight with other apps that might be controlling one or + // the other separately for third-party PS4 gamepads. We can add a whitelist for + // compatible third-party gamepads later if we want. + data[3] = (Uint8)(ctx->official_controller ? mask : (k_EPS4EffectRumble | k_EPS4EffectLED)); report_size = 78; offset = 6; } else { data[0] = k_EPS4ReportIdUsbEffects; - data[1] = 0x07; // Magic value + + // FIXME: Should we send a consistent default effect mask between BT and USB? + data[1] = (Uint8)(ctx->official_controller ? mask : (k_EPS4EffectRumble | k_EPS4EffectLED | k_EPS4EffectLEDBlink)); report_size = 32; offset = 4; @@ -991,8 +1007,18 @@ static bool HIDAPI_DriverPS4_InternalSendJoystickEffect(SDL_DriverPS4_Context *c static bool HIDAPI_DriverPS4_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, const void *effect, int size) { SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context; + EPS4Effect mask; - return HIDAPI_DriverPS4_InternalSendJoystickEffect(ctx, effect, size, true); + // Unlike the PS5 driver, the PS4 driver does not expect the validity bits as part of + // the provided effect data. The default values we provide also diverge for different + // gamepads and connection mediums. We should probably clean this up eventually. + if (ctx->device->is_bluetooth && ctx->official_controller) { + mask = k_EPS4EffectRumble | k_EPS4EffectLED; + } else { + mask = k_EPS4EffectRumble | k_EPS4EffectLED | k_EPS4EffectLEDBlink; + } + + return HIDAPI_DriverPS4_InternalSendJoystickEffect(ctx, effect, size, mask, true); } static bool HIDAPI_DriverPS4_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, bool enabled) @@ -1150,7 +1176,7 @@ static void HIDAPI_DriverPS4_HandleStatePacket(SDL_Joystick *joystick, SDL_hid_d float data[3]; tick = LOAD16(packet->rgucTimestamp[0], packet->rgucTimestamp[1]); - if (ctx->last_tick < tick) { + if (ctx->last_tick <= tick) { delta = (tick - ctx->last_tick); } else { delta = (SDL_MAX_UINT16 - ctx->last_tick + tick + 1); diff --git a/third-party/SDL/src/joystick/hidapi/SDL_hidapi_ps5.c b/third-party/SDL/src/joystick/hidapi/SDL_hidapi_ps5.c index abc1a922e9..b67fc59f2b 100644 --- a/third-party/SDL/src/joystick/hidapi/SDL_hidapi_ps5.c +++ b/third-party/SDL/src/joystick/hidapi/SDL_hidapi_ps5.c @@ -298,6 +298,12 @@ static bool HIDAPI_DriverPS5_IsSupportedDevice(SDL_HIDAPI_Device *device, const Uint8 data[USB_PACKET_LENGTH]; int size; + if (vendor_id == USB_VENDOR_BACKBONE && + product_id == USB_PRODUCT_BACKBONE_ONE_PS5_V2) { + // This product doesn't appear to use the DualSense protocol + return false; + } + if (type == SDL_GAMEPAD_TYPE_PS5) { return true; } @@ -1358,7 +1364,7 @@ static void HIDAPI_DriverPS5_HandleStatePacketCommon(SDL_Joystick *joystick, SDL Uint32 delta; Uint16 tick = LOAD16(packet->rgucSensorTimestamp[0], packet->rgucSensorTimestamp[1]); - if (ctx->last_tick < tick) { + if (ctx->last_tick <= tick) { delta = (tick - ctx->last_tick); } else { delta = (SDL_MAX_UINT16 - ctx->last_tick + tick + 1); diff --git a/third-party/SDL/src/joystick/hidapi/SDL_hidapi_steam.c b/third-party/SDL/src/joystick/hidapi/SDL_hidapi_steam.c index 0ebb723af3..cb6add70d2 100644 --- a/third-party/SDL/src/joystick/hidapi/SDL_hidapi_steam.c +++ b/third-party/SDL/src/joystick/hidapi/SDL_hidapi_steam.c @@ -29,7 +29,9 @@ #ifdef SDL_JOYSTICK_HIDAPI_STEAM // Define this if you want to log all packets from the controller -// #define DEBUG_STEAM_PROTOCOL +#if 0 +#define DEBUG_STEAM_PROTOCOL +#endif #define SDL_HINT_JOYSTICK_HIDAPI_STEAM_PAIRING_ENABLED "SDL_JOYSTICK_HIDAPI_STEAM_PAIRING_ENABLED" diff --git a/third-party/SDL/src/joystick/hidapi/SDL_hidapi_steam_hori.c b/third-party/SDL/src/joystick/hidapi/SDL_hidapi_steam_hori.c index 8d445c9791..a480ff2b7d 100644 --- a/third-party/SDL/src/joystick/hidapi/SDL_hidapi_steam_hori.c +++ b/third-party/SDL/src/joystick/hidapi/SDL_hidapi_steam_hori.c @@ -307,7 +307,7 @@ static void HIDAPI_DriverSteamHori_HandleStatePacket(SDL_Joystick *joystick, SDL Uint32 delta; Uint16 tick = LOAD16(data[10], data[11]); - if (ctx->last_tick < tick) { + if (ctx->last_tick <= tick) { delta = (tick - ctx->last_tick); } else { delta = (SDL_MAX_UINT16 - ctx->last_tick + tick + 1); diff --git a/third-party/SDL/src/joystick/hidapi/SDL_hidapi_steam_triton.c b/third-party/SDL/src/joystick/hidapi/SDL_hidapi_steam_triton.c index 453becdec2..01e4efff96 100644 --- a/third-party/SDL/src/joystick/hidapi/SDL_hidapi_steam_triton.c +++ b/third-party/SDL/src/joystick/hidapi/SDL_hidapi_steam_triton.c @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 2023 Max Maisel + Copyright (C) 1997-2026 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 @@ -27,6 +27,11 @@ #ifdef SDL_JOYSTICK_HIDAPI_STEAM_TRITON +// Define this if you want to log all packets from the controller +#if 0 +#define DEBUG_STEAM_PROTOCOL +#endif + /*****************************************************************************************************/ #include "steam/controller_constants.h" @@ -35,13 +40,22 @@ // Always 1kHz according to USB descriptor, but actually about 4 ms. #define TRITON_SENSOR_UPDATE_INTERVAL_US 4032 +// Steam Controller hardware safety timeout is around 50ms, so we resend rumble every 40ms +#define TRITON_RUMBLE_RESEND_INTERVAL_MS 40 + enum { - SDL_GAMEPAD_BUTTON_STEAM_DECK_QAM = 11, - SDL_GAMEPAD_BUTTON_STEAM_DECK_RIGHT_PADDLE1, - SDL_GAMEPAD_BUTTON_STEAM_DECK_LEFT_PADDLE1, - SDL_GAMEPAD_BUTTON_STEAM_DECK_RIGHT_PADDLE2, - SDL_GAMEPAD_BUTTON_STEAM_DECK_LEFT_PADDLE2, + SDL_GAMEPAD_BUTTON_TRITON_QAM = 11, + SDL_GAMEPAD_BUTTON_TRITON_RIGHT_PADDLE1, + SDL_GAMEPAD_BUTTON_TRITON_LEFT_PADDLE1, + SDL_GAMEPAD_BUTTON_TRITON_RIGHT_PADDLE2, + SDL_GAMEPAD_BUTTON_TRITON_LEFT_PADDLE2, + SDL_GAMEPAD_BUTTON_TRITON_RIGHT_TOUCHPAD, + SDL_GAMEPAD_BUTTON_TRITON_LEFT_TOUCHPAD, + SDL_GAMEPAD_BUTTON_TRITON_RIGHT_JOYSTICK_TOUCH, + SDL_GAMEPAD_BUTTON_TRITON_LEFT_JOYSTICK_TOUCH, + SDL_GAMEPAD_BUTTON_TRITON_RIGHT_GRIP_TOUCH, + SDL_GAMEPAD_BUTTON_TRITON_LEFT_GRIP_TOUCH, SDL_GAMEPAD_NUM_TRITON_BUTTONS, }; @@ -73,29 +87,39 @@ typedef enum TRITON_LBUTTON_L5 = 0x00040000, TRITON_LBUTTON_L = 0x00080000, - /* - STEAM_RIGHTSTICK_FINGERDOWN_MASK, // Right Stick Touch 0x00100000 - STEAM_RIGHTPAD_FINGERDOWN_MASK, // Right Pad Touch 0x00200000 - STEAM_BUTTON_RIGHTPAD_CLICKED_MASK, // Right Pressure Click 0x00400000 - STEAM_RIGHT_TRIGGER_MASK, // Right Trigger Click 0x00800000 + TRITON_RIGHT_JOYSTICK_TOUCH = 0x00100000, + TRITON_RIGHT_TOUCHPAD_TOUCH = 0x00200000, + TRITON_RIGHT_TOUCHPAD_CLICK = 0x00400000, + TRITON_RIGHT_TRIGGER_CLICK = 0x00800000, - STEAM_LEFTSTICK_FINGERDOWN_MASK, // Left Stick Touch 0x01000000 - STEAM_LEFTPAD_FINGERDOWN_MASK, // Left Pad Touch 0x02000000 - STEAM_BUTTON_LEFTPAD_CLICKED_MASK, // Left Pressure Click 0x04000000 - STEAM_LEFT_TRIGGER_MASK, // Left Trigger Click 0x08000000 - STEAM_RIGHT_AUX_MASK, // Right Pinky Touch 0x10000000 - STEAM_LEFT_AUX_MASK, // Left Pinky Touch 0x20000000 - */ + TRITON_LEFT_JOYSTICK_TOUCH = 0x01000000, + TRITON_LEFT_TOUCHPAD_TOUCH = 0x02000000, + TRITON_LEFT_TOUCHPAD_CLICK = 0x04000000, + TRITON_LEFT_TRIGGER_CLICK = 0x08000000, + + TRITON_RIGHT_GRIP_TOUCH = 0x10000000, + TRITON_LEFT_GRIP_TOUCH = 0x20000000, } TritonButtons; typedef struct { bool connected; bool report_sensors; - Uint32 last_sensor_tick; + Uint16 last_sensor_tick16; + Uint32 last_sensor_tick32; Uint64 sensor_timestamp_ns; Uint64 last_button_state; Uint64 last_lizard_update; + Uint16 low_frequency_rumble; + Uint16 high_frequency_rumble; + Uint64 last_rumble_time; + + bool left_touch_down; + float left_touch_x; + float left_touch_y; + bool right_touch_down; + float right_touch_x; + float right_touch_y; } SDL_DriverSteamTriton_Context; static bool IsProteusDongle(Uint16 product_id) @@ -123,77 +147,86 @@ static bool DisableSteamTritonLizardMode(SDL_hid_device *dev) return true; } -static void HIDAPI_DriverSteamTriton_HandleState(SDL_HIDAPI_Device *device, - SDL_Joystick *joystick, - TritonMTUFull_t *pTritonReport) +// Triton newer state MTUs are identical until touchpads. Parse them using this routine. +// Expects report to be a TritonMTUNoQuat_t, so cast as needed +static void HIDAPI_DriverSteamTriton_HandleGenericState(SDL_DriverSteamTriton_Context *ctx, SDL_Joystick *joystick, Uint64 timestamp, TritonMTUNoQuat_t *pTritonReport) { - float values[3]; - SDL_DriverSteamTriton_Context *ctx = (SDL_DriverSteamTriton_Context *)device->context; - Uint64 timestamp = SDL_GetTicksNS(); - - if (pTritonReport->uButtons != ctx->last_button_state) { + if (pTritonReport->buttons != ctx->last_button_state) { Uint8 hat = 0; SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SOUTH, - ((pTritonReport->uButtons & TRITON_LBUTTON_A) != 0)); + ((pTritonReport->buttons & TRITON_LBUTTON_A) != 0)); SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_EAST, - ((pTritonReport->uButtons & TRITON_LBUTTON_B) != 0)); + ((pTritonReport->buttons & TRITON_LBUTTON_B) != 0)); SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_WEST, - ((pTritonReport->uButtons & TRITON_LBUTTON_X) != 0)); + ((pTritonReport->buttons & TRITON_LBUTTON_X) != 0)); SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_NORTH, - ((pTritonReport->uButtons & TRITON_LBUTTON_Y) != 0)); + ((pTritonReport->buttons & TRITON_LBUTTON_Y) != 0)); SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, - ((pTritonReport->uButtons & TRITON_LBUTTON_L) != 0)); + ((pTritonReport->buttons & TRITON_LBUTTON_L) != 0)); SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, - ((pTritonReport->uButtons & TRITON_LBUTTON_R) != 0)); + ((pTritonReport->buttons & TRITON_LBUTTON_R) != 0)); SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK, - ((pTritonReport->uButtons & TRITON_LBUTTON_MENU) != 0)); + ((pTritonReport->buttons & TRITON_LBUTTON_MENU) != 0)); SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, - ((pTritonReport->uButtons & TRITON_LBUTTON_VIEW) != 0)); + ((pTritonReport->buttons & TRITON_LBUTTON_VIEW) != 0)); SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, - ((pTritonReport->uButtons & TRITON_LBUTTON_STEAM) != 0)); - SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_STEAM_DECK_QAM, - ((pTritonReport->uButtons & TRITON_HBUTTON_QAM) != 0)); + ((pTritonReport->buttons & TRITON_LBUTTON_STEAM) != 0)); + SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_TRITON_QAM, + ((pTritonReport->buttons & TRITON_HBUTTON_QAM) != 0)); SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, - ((pTritonReport->uButtons & TRITON_LBUTTON_L3) != 0)); + ((pTritonReport->buttons & TRITON_LBUTTON_L3) != 0)); SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK, - ((pTritonReport->uButtons & TRITON_LBUTTON_R3) != 0)); + ((pTritonReport->buttons & TRITON_LBUTTON_R3) != 0)); - SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_STEAM_DECK_RIGHT_PADDLE1, - ((pTritonReport->uButtons & TRITON_HBUTTON_R4) != 0)); - SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_STEAM_DECK_LEFT_PADDLE1, - ((pTritonReport->uButtons & TRITON_HBUTTON_L4) != 0)); - SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_STEAM_DECK_RIGHT_PADDLE2, - ((pTritonReport->uButtons & TRITON_LBUTTON_R5) != 0)); - SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_STEAM_DECK_LEFT_PADDLE2, - ((pTritonReport->uButtons & TRITON_LBUTTON_L5) != 0)); + SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_TRITON_RIGHT_PADDLE1, + ((pTritonReport->buttons & TRITON_HBUTTON_R4) != 0)); + SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_TRITON_LEFT_PADDLE1, + ((pTritonReport->buttons & TRITON_HBUTTON_L4) != 0)); + SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_TRITON_RIGHT_PADDLE2, + ((pTritonReport->buttons & TRITON_LBUTTON_R5) != 0)); + SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_TRITON_LEFT_PADDLE2, + ((pTritonReport->buttons & TRITON_LBUTTON_L5) != 0)); - if (pTritonReport->uButtons & TRITON_LBUTTON_DPAD_UP) { + SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_TRITON_RIGHT_TOUCHPAD, + ((pTritonReport->buttons & TRITON_RIGHT_TOUCHPAD_CLICK) != 0)); + SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_TRITON_LEFT_TOUCHPAD, + ((pTritonReport->buttons & TRITON_LEFT_TOUCHPAD_CLICK) != 0)); + + SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_TRITON_RIGHT_JOYSTICK_TOUCH, + ((pTritonReport->buttons & TRITON_RIGHT_JOYSTICK_TOUCH) != 0)); + SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_TRITON_LEFT_JOYSTICK_TOUCH, + ((pTritonReport->buttons & TRITON_LEFT_JOYSTICK_TOUCH) != 0)); + + SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_TRITON_RIGHT_GRIP_TOUCH, + ((pTritonReport->buttons & TRITON_RIGHT_GRIP_TOUCH) != 0)); + SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_TRITON_LEFT_GRIP_TOUCH, + ((pTritonReport->buttons & TRITON_LEFT_GRIP_TOUCH) != 0)); + + if (pTritonReport->buttons & TRITON_LBUTTON_DPAD_UP) { hat |= SDL_HAT_UP; } - if (pTritonReport->uButtons & TRITON_LBUTTON_DPAD_DOWN) { + if (pTritonReport->buttons & TRITON_LBUTTON_DPAD_DOWN) { hat |= SDL_HAT_DOWN; } - if (pTritonReport->uButtons & TRITON_LBUTTON_DPAD_LEFT) { + if (pTritonReport->buttons & TRITON_LBUTTON_DPAD_LEFT) { hat |= SDL_HAT_LEFT; } - if (pTritonReport->uButtons & TRITON_LBUTTON_DPAD_RIGHT) { + if (pTritonReport->buttons & TRITON_LBUTTON_DPAD_RIGHT) { hat |= SDL_HAT_RIGHT; } SDL_SendJoystickHat(timestamp, joystick, 0, hat); - ctx->last_button_state = pTritonReport->uButtons; + ctx->last_button_state = pTritonReport->buttons; } - // RKRK There're button bits for this if you so choose. SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, (int)pTritonReport->sTriggerLeft * 2 - 32768); SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, (int)pTritonReport->sTriggerRight * 2 - 32768); - SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, pTritonReport->sLeftStickX); SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, @@ -202,9 +235,48 @@ static void HIDAPI_DriverSteamTriton_HandleState(SDL_HIDAPI_Device *device, pTritonReport->sRightStickX); SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, -pTritonReport->sRightStickY); +} - if (ctx->report_sensors && pTritonReport->imu.uTimestamp != ctx->last_sensor_tick) { - Uint32 delta_us = (pTritonReport->imu.uTimestamp - ctx->last_sensor_tick); +static void HIDAPI_DriverSteamTriton_HandleState(SDL_HIDAPI_Device *device, + SDL_Joystick *joystick, + TritonMTUNoQuat_t *pTritonReport) +{ + float values[3]; + SDL_DriverSteamTriton_Context *ctx = (SDL_DriverSteamTriton_Context *)device->context; + Uint64 timestamp = SDL_GetTicksNS(); + + HIDAPI_DriverSteamTriton_HandleGenericState(ctx, joystick, timestamp, pTritonReport); + + bool left_touch_down = (pTritonReport->buttons & TRITON_LEFT_TOUCHPAD_TOUCH) ? true : false; + bool right_touch_down = (pTritonReport->buttons & TRITON_RIGHT_TOUCHPAD_TOUCH) ? true : false; + + if (left_touch_down || ctx->left_touch_down) { + if (left_touch_down) { + ctx->left_touch_x = pTritonReport->sLeftPadX / 65536.0f + 0.5f; + ctx->left_touch_y = -(float)pTritonReport->sLeftPadY / 65536.0f + 0.5f; + } + SDL_SendJoystickTouchpad(timestamp, joystick, 0, 0, + left_touch_down, + ctx->left_touch_x, + ctx->left_touch_y, + pTritonReport->unPressureLeft / 32768.0f); + ctx->left_touch_down = left_touch_down; + } + if (right_touch_down || ctx->right_touch_down) { + if (right_touch_down) { + ctx->right_touch_x = pTritonReport->sRightPadX / 65536.0f + 0.5f; + ctx->right_touch_y = -(float)pTritonReport->sRightPadY / 65536.0f + 0.5f; + } + SDL_SendJoystickTouchpad(timestamp, joystick, 1, 0, + right_touch_down, + ctx->right_touch_x, + ctx->right_touch_y, + pTritonReport->unPressureRight / 32768.0f); + ctx->right_touch_down = right_touch_down; + } + + if (ctx->report_sensors && pTritonReport->imu.timestamp != ctx->last_sensor_tick32) { + Uint32 delta_us = (pTritonReport->imu.timestamp - ctx->last_sensor_tick32); ctx->sensor_timestamp_ns += SDL_US_TO_NS(delta_us); @@ -218,7 +290,65 @@ static void HIDAPI_DriverSteamTriton_HandleState(SDL_HIDAPI_Device *device, values[2] = (-pTritonReport->imu.sAccelY / 32768.0f) * 2.0f * SDL_STANDARD_GRAVITY; SDL_SendJoystickSensor(timestamp, joystick, SDL_SENSOR_ACCEL, ctx->sensor_timestamp_ns, values, 3); - ctx->last_sensor_tick = pTritonReport->imu.uTimestamp; + ctx->last_sensor_tick32 = pTritonReport->imu.timestamp; + } +} + +static void HIDAPI_DriverSteamTriton_HandleState_Timestamp(SDL_HIDAPI_Device *device, + SDL_Joystick *joystick, + TritonMTUNoQuat32TS_t *pTritonReport) +{ + float values[3]; + SDL_DriverSteamTriton_Context *ctx = (SDL_DriverSteamTriton_Context *)device->context; + Uint64 timestamp = SDL_GetTicksNS(); + + HIDAPI_DriverSteamTriton_HandleGenericState(ctx, joystick, timestamp, (TritonMTUNoQuat_t *) pTritonReport); + + bool left_touch_down = (pTritonReport->buttons & TRITON_LEFT_TOUCHPAD_TOUCH) ? true : false; + bool right_touch_down = (pTritonReport->buttons & TRITON_RIGHT_TOUCHPAD_TOUCH) ? true : false; + + if (left_touch_down || ctx->left_touch_down) { + if (left_touch_down) { + ctx->left_touch_x = pTritonReport->sLeftPadX / 65536.0f + 0.5f; + ctx->left_touch_y = -(float)pTritonReport->sLeftPadY / 65536.0f + 0.5f; + } + SDL_SendJoystickTouchpad(timestamp, joystick, 0, 0, + left_touch_down, + ctx->left_touch_x, + ctx->left_touch_y, + pTritonReport->unPressureLeft / 32768.0f); + ctx->left_touch_down = left_touch_down; + } + if (right_touch_down || ctx->right_touch_down) { + if (right_touch_down) { + ctx->right_touch_x = pTritonReport->sRightPadX / 65536.0f + 0.5f; + ctx->right_touch_y = -(float)pTritonReport->sRightPadY / 65536.0f + 0.5f; + } + SDL_SendJoystickTouchpad(timestamp, joystick, 1, 0, + right_touch_down, + ctx->right_touch_x, + ctx->right_touch_y, + pTritonReport->unPressureRight / 32768.0f); + ctx->right_touch_down = right_touch_down; + } + + if (ctx->report_sensors && pTritonReport->imu.timestamp != ctx->last_sensor_tick16) { + // The timestamp is in units of 32 microseconds + Uint32 delta_us = (Uint32)(pTritonReport->imu.timestamp - ctx->last_sensor_tick16) * 32; + + ctx->sensor_timestamp_ns += SDL_US_TO_NS(delta_us); + + values[0] = (pTritonReport->imu.sGyroX / 32768.0f) * (2000.0f * (SDL_PI_F / 180.0f)); + values[1] = (pTritonReport->imu.sGyroZ / 32768.0f) * (2000.0f * (SDL_PI_F / 180.0f)); + values[2] = (-pTritonReport->imu.sGyroY / 32768.0f) * (2000.0f * (SDL_PI_F / 180.0f)); + SDL_SendJoystickSensor(timestamp, joystick, SDL_SENSOR_GYRO, ctx->sensor_timestamp_ns, values, 3); + + values[0] = (pTritonReport->imu.sAccelX / 32768.0f) * 2.0f * SDL_STANDARD_GRAVITY; + values[1] = (pTritonReport->imu.sAccelZ / 32768.0f) * 2.0f * SDL_STANDARD_GRAVITY; + values[2] = (-pTritonReport->imu.sAccelY / 32768.0f) * 2.0f * SDL_STANDARD_GRAVITY; + SDL_SendJoystickSensor(timestamp, joystick, SDL_SENSOR_ACCEL, ctx->sensor_timestamp_ns, values, 3); + + ctx->last_sensor_tick16 = pTritonReport->imu.timestamp; } } @@ -319,8 +449,8 @@ static bool HIDAPI_DriverSteamTriton_IsSupportedDevice( return true; } } else if (SDL_IsJoystickSteamTriton(vendor_id, product_id)) { - return true; - } + return true; + } return false; } @@ -354,6 +484,8 @@ static void HIDAPI_DriverSteamTriton_SetDevicePlayerIndex(SDL_HIDAPI_Device *dev { } +static bool HIDAPI_DriverSteamTriton_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble); + static bool HIDAPI_DriverSteamTriton_UpdateDevice(SDL_HIDAPI_Device *device) { SDL_DriverSteamTriton_Context *ctx = (SDL_DriverSteamTriton_Context *)device->context; @@ -369,6 +501,12 @@ static bool HIDAPI_DriverSteamTriton_UpdateDevice(SDL_HIDAPI_Device *device) DisableSteamTritonLizardMode(device->dev); ctx->last_lizard_update = now; } + + if (ctx->low_frequency_rumble || ctx->high_frequency_rumble) { + if ((now - ctx->last_rumble_time) >= TRITON_RUMBLE_RESEND_INTERVAL_MS) { + HIDAPI_DriverSteamTriton_RumbleJoystick(device, joystick, ctx->low_frequency_rumble, ctx->high_frequency_rumble); + } + } } for (;;) { @@ -384,6 +522,10 @@ static bool HIDAPI_DriverSteamTriton_UpdateDevice(SDL_HIDAPI_Device *device) return false; } +#ifdef DEBUG_STEAM_PROTOCOL + HIDAPI_DumpPacket("Steam Controller packet: size = %d", data, r); +#endif + switch (data[0]) { case ID_TRITON_CONTROLLER_STATE: case ID_TRITON_CONTROLLER_STATE_BLE: @@ -393,11 +535,23 @@ static bool HIDAPI_DriverSteamTriton_UpdateDevice(SDL_HIDAPI_Device *device) joystick = SDL_GetJoystickFromID(device->joysticks[0]); } } - if (joystick && r >= (1 + sizeof(TritonMTUFull_t))) { - TritonMTUFull_t *pTritonReport = (TritonMTUFull_t *)&data[1]; + if (joystick && r >= (1 + sizeof(TritonMTUNoQuat_t))) { + TritonMTUNoQuat_t *pTritonReport = (TritonMTUNoQuat_t *)&data[1]; HIDAPI_DriverSteamTriton_HandleState(device, joystick, pTritonReport); } break; + case ID_TRITON_CONTROLLER_STATE_TIMESTAMP: + if (!joystick) { + HIDAPI_DriverSteamTriton_SetControllerConnected(device, true); + if (device->num_joysticks > 0) { + joystick = SDL_GetJoystickFromID(device->joysticks[0]); + } + } + if (joystick && r >= (1 + sizeof(TritonMTUNoQuat32TS_t))) { + TritonMTUNoQuat32TS_t *pTritonReport = (TritonMTUNoQuat32TS_t *)&data[1]; + HIDAPI_DriverSteamTriton_HandleState_Timestamp(device, joystick, pTritonReport); + } + break; case ID_TRITON_BATTERY_STATUS: if (joystick && r >= (1 + sizeof(TritonBatteryStatus_t))) { TritonBatteryStatus_t *pTritonBatteryStatus = (TritonBatteryStatus_t *)&data[1]; @@ -431,18 +585,25 @@ static bool HIDAPI_DriverSteamTriton_OpenJoystick(SDL_HIDAPI_Device *device, SDL SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_GYRO, update_rate_in_hz); SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL, update_rate_in_hz); + SDL_PrivateJoystickAddTouchpad(joystick, 1); + SDL_PrivateJoystickAddTouchpad(joystick, 1); + return true; } static bool HIDAPI_DriverSteamTriton_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble) { + SDL_DriverSteamTriton_Context *ctx = (SDL_DriverSteamTriton_Context *)device->context; int rc; - //RKRK Not sure about size. Probably 64+1 is OK for ORs - Uint8 buffer[HID_RUMBLE_OUTPUT_REPORT_BYTES]; + ctx->low_frequency_rumble = low_frequency_rumble; + ctx->high_frequency_rumble = high_frequency_rumble; + ctx->last_rumble_time = SDL_GetTicks(); + + Uint8 buffer[HID_RUMBLE_OUTPUT_REPORT_BYTES] = { 0 }; OutputReportMsg *msg = (OutputReportMsg *)(buffer); - msg->report_id = ID_OUT_REPORT_HAPTIC_RUMBLE; + msg->report_id = ID_OUT_REPORT_HAPTIC_RUMBLE; msg->payload.hapticRumble.type = 0; msg->payload.hapticRumble.intensity = 0; msg->payload.hapticRumble.left.speed = low_frequency_rumble; @@ -450,9 +611,12 @@ static bool HIDAPI_DriverSteamTriton_RumbleJoystick(SDL_HIDAPI_Device *device, S msg->payload.hapticRumble.right.speed = high_frequency_rumble; msg->payload.hapticRumble.right.gain = 0; - rc = SDL_hid_write(device->dev, buffer, sizeof(buffer)); - if (rc != sizeof(buffer)) { + if (rc < 0) { + SDL_LogError(SDL_LOG_CATEGORY_INPUT, + "Steam Controller HID Write FAILED! rc: %d. SDL_Error: %s", + rc, SDL_GetError()); + return false; } return true; @@ -475,6 +639,13 @@ static bool HIDAPI_DriverSteamTriton_SetJoystickLED(SDL_HIDAPI_Device *device, S static bool HIDAPI_DriverSteamTriton_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, const void *data, int size) { + if (size == HID_FEATURE_REPORT_BYTES) { + int rc = SDL_hid_send_feature_report(device->dev, data, size); + if (rc != size) { + return false; + } + return true; + } return SDL_Unsupported(); } diff --git a/third-party/SDL/src/joystick/hidapi/SDL_hidapi_steamdeck.c b/third-party/SDL/src/joystick/hidapi/SDL_hidapi_steamdeck.c index 018ce00d91..8bfb64dfd6 100644 --- a/third-party/SDL/src/joystick/hidapi/SDL_hidapi_steamdeck.c +++ b/third-party/SDL/src/joystick/hidapi/SDL_hidapi_steamdeck.c @@ -39,6 +39,10 @@ enum SDL_GAMEPAD_BUTTON_STEAM_DECK_LEFT_PADDLE1, SDL_GAMEPAD_BUTTON_STEAM_DECK_RIGHT_PADDLE2, SDL_GAMEPAD_BUTTON_STEAM_DECK_LEFT_PADDLE2, + SDL_GAMEPAD_BUTTON_STEAM_DECK_RIGHT_TOUCHPAD, + SDL_GAMEPAD_BUTTON_STEAM_DECK_LEFT_TOUCHPAD, + SDL_GAMEPAD_BUTTON_STEAM_DECK_RIGHT_JOYSTICK_TOUCH, + SDL_GAMEPAD_BUTTON_STEAM_DECK_LEFT_JOYSTICK_TOUCH, SDL_GAMEPAD_NUM_STEAM_DECK_BUTTONS, }; @@ -63,11 +67,15 @@ typedef enum STEAMDECK_LBUTTON_R5 = 0x00010000, STEAMDECK_LBUTTON_LEFT_PAD = 0x00020000, STEAMDECK_LBUTTON_RIGHT_PAD = 0x00040000, + STEAMDECK_LBUTTON_LEFT_TOUCHPAD_TOUCH = 0x00080000, + STEAMDECK_LBUTTON_RIGHT_TOUCHPAD_TOUCH = 0x00100000, STEAMDECK_LBUTTON_L3 = 0x00400000, STEAMDECK_LBUTTON_R3 = 0x04000000, STEAMDECK_HBUTTON_L4 = 0x00000200, STEAMDECK_HBUTTON_R4 = 0x00000400, + STEAMDECK_HBUTTON_LSTICK_TOUCH = 0x00004000, + STEAMDECK_HBUTTON_RSTICK_TOUCH = 0x00008000, STEAMDECK_HBUTTON_QAM = 0x00040000, } SteamDeckButtons; @@ -77,6 +85,13 @@ typedef struct Uint64 sensor_timestamp_ns; Uint64 last_button_state; Uint8 watchdog_counter; + + bool left_touch_down; + float left_touch_x; + float left_touch_y; + bool right_touch_down; + float right_touch_x; + float right_touch_y; } SDL_DriverSteamDeck_Context; static bool DisableDeckLizardMode(SDL_hid_device *dev) @@ -191,6 +206,16 @@ static void HIDAPI_DriverSteamDeck_HandleState(SDL_HIDAPI_Device *device, SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_STEAM_DECK_LEFT_PADDLE2, ((pInReport->payload.deckState.ulButtonsL & STEAMDECK_LBUTTON_L5) != 0)); + SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_STEAM_DECK_RIGHT_TOUCHPAD, + ((pInReport->payload.deckState.ulButtonsL & STEAMDECK_LBUTTON_RIGHT_PAD) != 0)); + SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_STEAM_DECK_LEFT_TOUCHPAD, + ((pInReport->payload.deckState.ulButtonsL & STEAMDECK_LBUTTON_LEFT_PAD) != 0)); + + SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_STEAM_DECK_RIGHT_JOYSTICK_TOUCH, + ((pInReport->payload.deckState.ulButtonsH & STEAMDECK_HBUTTON_RSTICK_TOUCH) != 0)); + SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_STEAM_DECK_LEFT_JOYSTICK_TOUCH, + ((pInReport->payload.deckState.ulButtonsH & STEAMDECK_HBUTTON_LSTICK_TOUCH) != 0)); + if (pInReport->payload.deckState.ulButtonsL & STEAMDECK_LBUTTON_DPAD_UP) { hat |= SDL_HAT_UP; } @@ -234,17 +259,33 @@ static void HIDAPI_DriverSteamDeck_HandleState(SDL_HIDAPI_Device *device, values[2] = (-pInReport->payload.deckState.sAccelY / 32768.0f) * 2.0f * SDL_STANDARD_GRAVITY; SDL_SendJoystickSensor(timestamp, joystick, SDL_SENSOR_ACCEL, ctx->sensor_timestamp_ns, values, 3); - SDL_SendJoystickTouchpad(timestamp, joystick, 0, 0, - pInReport->payload.deckState.sPressurePadLeft > 0, - pInReport->payload.deckState.sLeftPadX / 65536.0f + 0.5f, - pInReport->payload.deckState.sLeftPadY / 65536.0f + 0.5f, - pInReport->payload.deckState.sPressurePadLeft / 32768.0f); + bool left_touch_down = (pInReport->payload.deckState.ulButtonsL & STEAMDECK_LBUTTON_LEFT_TOUCHPAD_TOUCH) ? true : false; + bool right_touch_down = (pInReport->payload.deckState.ulButtonsL & STEAMDECK_LBUTTON_RIGHT_TOUCHPAD_TOUCH) ? true : false; + if (left_touch_down || ctx->left_touch_down) { + if (left_touch_down) { + ctx->left_touch_x = pInReport->payload.deckState.sLeftPadX / 65536.0f + 0.5f; + ctx->left_touch_y = -(float)pInReport->payload.deckState.sLeftPadY / 65536.0f + 0.5f; - SDL_SendJoystickTouchpad(timestamp, joystick, 1, 0, - pInReport->payload.deckState.sPressurePadRight > 0, - pInReport->payload.deckState.sRightPadX / 65536.0f + 0.5f, - pInReport->payload.deckState.sRightPadY / 65536.0f + 0.5f, - pInReport->payload.deckState.sPressurePadRight / 32768.0f); + } + SDL_SendJoystickTouchpad(timestamp, joystick, 0, 0, + left_touch_down, + ctx->left_touch_x, + ctx->left_touch_y, + pInReport->payload.deckState.sPressurePadLeft / 32768.0f); + ctx->left_touch_down = left_touch_down; + } + if (right_touch_down || ctx->right_touch_down) { + if (right_touch_down) { + ctx->right_touch_x = pInReport->payload.deckState.sRightPadX / 65536.0f + 0.5f; + ctx->right_touch_y = -(float)pInReport->payload.deckState.sRightPadY / 65536.0f + 0.5f; + } + SDL_SendJoystickTouchpad(timestamp, joystick, 1, 0, + right_touch_down, + ctx->right_touch_x, + ctx->right_touch_y, + pInReport->payload.deckState.sPressurePadRight / 32768.0f); + ctx->right_touch_down = right_touch_down; + } } /*****************************************************************************************************/ diff --git a/third-party/SDL/src/joystick/hidapi/SDL_hidapi_switch.c b/third-party/SDL/src/joystick/hidapi/SDL_hidapi_switch.c index 514e158889..8b56dcee01 100644 --- a/third-party/SDL/src/joystick/hidapi/SDL_hidapi_switch.c +++ b/third-party/SDL/src/joystick/hidapi/SDL_hidapi_switch.c @@ -1458,11 +1458,11 @@ static void UpdateDeviceIdentity(SDL_HIDAPI_Device *device) device->type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO; break; case k_eSwitchDeviceInfoControllerType_HVCLeft: - HIDAPI_SetDeviceName(device, "Nintendo HVC Controller (1)"); + HIDAPI_SetDeviceName(device, "Nintendo Family Computer Controller (1)"); device->type = SDL_GAMEPAD_TYPE_STANDARD; break; case k_eSwitchDeviceInfoControllerType_HVCRight: - HIDAPI_SetDeviceName(device, "Nintendo HVC Controller (2)"); + HIDAPI_SetDeviceName(device, "Nintendo Family Computer Controller (2)"); device->type = SDL_GAMEPAD_TYPE_STANDARD; break; case k_eSwitchDeviceInfoControllerType_NESLeft: diff --git a/third-party/SDL/src/joystick/hidapi/SDL_hidapi_xbox360.c b/third-party/SDL/src/joystick/hidapi/SDL_hidapi_xbox360.c index e140cef92d..208771e5f9 100644 --- a/third-party/SDL/src/joystick/hidapi/SDL_hidapi_xbox360.c +++ b/third-party/SDL/src/joystick/hidapi/SDL_hidapi_xbox360.c @@ -32,6 +32,10 @@ // Define this if you want to log all packets from the controller // #define DEBUG_XBOX_PROTOCOL +#ifdef SDL_PLATFORM_MACOS +#include +#endif + typedef struct { SDL_HIDAPI_Device *device; @@ -39,6 +43,9 @@ typedef struct int player_index; bool player_lights; Uint8 last_state[USB_PACKET_LENGTH]; +#ifdef SDL_PLATFORM_MACOS + bool controlled_by_360controller; +#endif } SDL_DriverXbox360_Context; static void HIDAPI_DriverXbox360_RegisterHints(SDL_HintCallback callback, void *userdata) @@ -59,6 +66,22 @@ static bool HIDAPI_DriverXbox360_IsEnabled(void) SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_XBOX, SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI, SDL_HIDAPI_DEFAULT))); } +#ifdef SDL_PLATFORM_MACOS +static bool IsControlledBy360ControllerDriverMacOS(SDL_HIDAPI_Device *device) +{ + bool controlled_by_360controller = false; + if (device && device->path && SDL_strncmp("DevSrvsID:", device->path, 10) == 0) { + uint64_t entry_id = SDL_strtoull(device->path + 10, NULL, 10); + io_service_t service = IOServiceGetMatchingService(0, IORegistryEntryIDMatching(entry_id)); + if (service != MACH_PORT_NULL) { + controlled_by_360controller = IOObjectConformsTo(service, "Xbox360ControllerClass"); + IOObjectRelease(service); + } + } + return controlled_by_360controller; +} +#endif + static bool HIDAPI_DriverXbox360_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) { const int XB360W_IFACE_PROTOCOL = 129; // Wireless @@ -80,13 +103,22 @@ static bool HIDAPI_DriverXbox360_IsSupportedDevice(SDL_HIDAPI_Device *device, co // This is the chatpad or other input interface, not the Xbox 360 interface return false; } +#ifdef SDL_PLATFORM_MACOS + if (IsControlledBy360ControllerDriverMacOS(device)) { + // Wired Xbox controllers are handled by this driver, when they are + // controlled by the 360Controller driver available from: + // https://github.com/360Controller/360Controller/releases + return true; + } +#endif #if defined(SDL_PLATFORM_MACOS) && defined(SDL_JOYSTICK_MFI) if (SDL_IsJoystickSteamVirtualGamepad(vendor_id, product_id, version)) { // GCController support doesn't work with the Steam Virtual Gamepad return true; } else { - // On macOS you can't write output reports to wired XBox controllers, - // so we'll just use the GCController support instead. + // On macOS when it isn't controlled by the 360Controller driver and + // it doesn't look like a Steam virtual gamepad we should rely on + // GCController support instead. return false; } #else @@ -138,6 +170,9 @@ static bool HIDAPI_DriverXbox360_InitDevice(SDL_HIDAPI_Device *device) return false; } ctx->device = device; +#ifdef SDL_PLATFORM_MACOS + ctx->controlled_by_360controller = IsControlledBy360ControllerDriverMacOS(device); +#endif device->context = ctx; @@ -198,6 +233,19 @@ static bool HIDAPI_DriverXbox360_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joy static bool HIDAPI_DriverXbox360_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble) { +#ifdef SDL_PLATFORM_MACOS + if (((SDL_DriverXbox360_Context *)device->context)->controlled_by_360controller) { + // On macOS the 360Controller driver uses this short report, + // and we need to prefix it with a magic token so hidapi passes it through untouched + Uint8 rumble_packet[] = { 'M', 'A', 'G', 'I', 'C', '0', 0x00, 0x04, 0x00, 0x00 }; + rumble_packet[6 + 2] = (low_frequency_rumble >> 8); + rumble_packet[6 + 3] = (high_frequency_rumble >> 8); + if (SDL_HIDAPI_SendRumble(device, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) { + return SDL_SetError("Couldn't send rumble packet"); + } + return true; + } +#endif Uint8 rumble_packet[] = { 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; rumble_packet[3] = (low_frequency_rumble >> 8); diff --git a/third-party/SDL/src/joystick/hidapi/SDL_hidapi_xboxone.c b/third-party/SDL/src/joystick/hidapi/SDL_hidapi_xboxone.c index 896423f848..a3fa66e7c8 100644 --- a/third-party/SDL/src/joystick/hidapi/SDL_hidapi_xboxone.c +++ b/third-party/SDL/src/joystick/hidapi/SDL_hidapi_xboxone.c @@ -374,6 +374,12 @@ static bool HIDAPI_DriverXboxOne_IsSupportedDevice(SDL_HIDAPI_Device *device, co // we'll just use the GCController support instead. return false; } +#endif +#ifdef SDL_PLATFORM_WIN32 + if (device && SDL_strncmp(device->path, "\\\\?\\HID#", 8) == 0) { + // Windows provides a fake HID endpoint for XGIP controllers, don't use this + return false; + } #endif if (interface_class && (interface_class != LIBUSB_CLASS_VENDOR_SPEC || diff --git a/third-party/SDL/src/joystick/hidapi/SDL_hidapi_zuiki.c b/third-party/SDL/src/joystick/hidapi/SDL_hidapi_zuiki.c index ad7f3d80be..d5958be09d 100644 --- a/third-party/SDL/src/joystick/hidapi/SDL_hidapi_zuiki.c +++ b/third-party/SDL/src/joystick/hidapi/SDL_hidapi_zuiki.c @@ -62,8 +62,8 @@ static float median_filter_update(MedianFilter_t* mf, float input) { if (mf->count < FILTER_SIZE) mf->count++; float temp[FILTER_SIZE]; SDL_memcpy(temp, mf->buffer, sizeof(temp)); - for (int i = 0; i < mf->count - 1; i++) { - for (int j = i + 1; j < mf->count; j++) { + for (uint8_t i = 0; i < mf->count - 1; i++) { + for (uint8_t j = i + 1; j < mf->count; j++) { if (temp[i] > temp[j]) { float t = temp[i]; temp[i] = temp[j]; diff --git a/third-party/SDL/src/joystick/hidapi/SDL_hidapijoystick.c b/third-party/SDL/src/joystick/hidapi/SDL_hidapijoystick.c index 7629b1ac59..0fdaa9ad02 100644 --- a/third-party/SDL/src/joystick/hidapi/SDL_hidapijoystick.c +++ b/third-party/SDL/src/joystick/hidapi/SDL_hidapijoystick.c @@ -285,6 +285,7 @@ static SDL_GamepadType SDL_GetJoystickGameControllerProtocol(const char *name, U 0x24c6, // PowerA 0x2c22, // Qanba 0x2dc8, // 8BitDo + 0x3537, // GameSir 0x37d7, // Flydigi 0x9886, // ASTRO Gaming }; @@ -897,14 +898,19 @@ static SDL_HIDAPI_Device *HIDAPI_AddDevice(const struct SDL_hid_device_info *inf for (curr = SDL_HIDAPI_devices, last = NULL; curr; last = curr, curr = curr->next) { } + char *path = SDL_strdup(info->path); + if (!path) { + SDL_OutOfMemory(); + return NULL; + } + device = (SDL_HIDAPI_Device *)SDL_calloc(1, sizeof(*device)); if (!device) { + SDL_free(path); return NULL; } SDL_SetObjectValid(device, SDL_OBJECT_TYPE_HIDAPI_JOYSTICK, true); - if (info->path) { - device->path = SDL_strdup(info->path); - } + device->path = path; device->seen = true; device->vendor_id = info->vendor_id; device->product_id = info->product_id; @@ -1136,6 +1142,11 @@ static void HIDAPI_UpdateDeviceList(void) devs = SDL_hid_enumerate(0, 0); if (devs) { for (info = devs; info; info = info->next) { + if (!info->path) { + // We can't open this, ignore it + continue; + } + device = HIDAPI_GetJoystickByInfo(info->path, info->vendor_id, info->product_id); if (device) { device->seen = true; diff --git a/third-party/SDL/src/joystick/hidapi/steam/controller_constants.h b/third-party/SDL/src/joystick/hidapi/steam/controller_constants.h index b60021e0c5..e838a8924a 100644 --- a/third-party/SDL/src/joystick/hidapi/steam/controller_constants.h +++ b/third-party/SDL/src/joystick/hidapi/steam/controller_constants.h @@ -585,4 +585,4 @@ typedef enum } #endif -#endif // _CONTROLLER_CONSTANTS_H +#endif // _CONTROLLER_CONSTANTS_ diff --git a/third-party/SDL/src/joystick/hidapi/steam/controller_structs.h b/third-party/SDL/src/joystick/hidapi/steam/controller_structs.h index 2657346421..69c10ed689 100644 --- a/third-party/SDL/src/joystick/hidapi/steam/controller_structs.h +++ b/third-party/SDL/src/joystick/hidapi/steam/controller_structs.h @@ -179,9 +179,8 @@ typedef struct uint16_t on_us; uint16_t off_us; uint16_t repeat_count; - uint16_t gain_db; } MsgHapticPulse; -#define HID_HAPTIC_PULSE_OUTPUT_REPORT_BYTES 10 +#define HID_HAPTIC_PULSE_OUTPUT_REPORT_BYTES 8 typedef struct { @@ -228,8 +227,8 @@ typedef enum ID_OUT_REPORT_HAPTIC_PULSE = 0x81, ID_OUT_REPORT_HAPTIC_COMMAND = 0x82, ID_OUT_REPORT_HAPTIC_LFO_TONE = 0x83, - ID_OUT_REPORT_HAPTIC_LOG_SWEEP = 0x85, - ID_OUT_REPORT_HAPTIC_SCRIPT = 0x86, + ID_OUT_REPORT_HAPTIC_LOG_SWEEP = 0x84, + ID_OUT_REPORT_HAPTIC_SCRIPT = 0x85, } ValveTritonOutReportMessageIDs; typedef struct @@ -556,6 +555,8 @@ enum ETritonReportIDTypes ID_TRITON_BATTERY_STATUS = 0x43, ID_TRITON_CONTROLLER_STATE_BLE = 0x45, ID_TRITON_WIRELESS_STATUS_X = 0x46, + ID_TRITON_CONTROLLER_STATE_TIMESTAMP = 0x47, + ID_TRITON_WIRELESS_STATUS = 0x79, }; @@ -567,7 +568,7 @@ enum ETritonWirelessState typedef struct { - uint32_t uTimestamp; + uint32_t timestamp; short sAccelX; short sAccelY; short sAccelZ; @@ -582,10 +583,33 @@ typedef struct short sGyroQuatZ; } TritonMTUIMU_t; +typedef struct { + uint32_t timestamp; + short sAccelX; + short sAccelY; + short sAccelZ; + + short sGyroX; + short sGyroY; + short sGyroZ; +} TritonMTUIMUNoQuat_t; + typedef struct { - uint8_t cSeq_num; - uint32_t uButtons; + uint16_t timestamp; + short sAccelX; + short sAccelY; + short sAccelZ; + + short sGyroX; + short sGyroY; + short sGyroZ; +} TritonMTUIMUNoQuat32usTS_t; + +typedef struct +{ + uint8_t seq_num; + uint32_t buttons; short sTriggerLeft; short sTriggerRight; @@ -596,14 +620,61 @@ typedef struct short sLeftPadX; short sLeftPadY; - unsigned short ucPressureLeft; + unsigned short unPressureLeft; short sRightPadX; short sRightPadY; - unsigned short ucPressureRight; + unsigned short unPressureRight; TritonMTUIMU_t imu; } TritonMTUFull_t; +typedef struct { + uint8_t seq_num; + uint32_t buttons; + short sTriggerLeft; + short sTriggerRight; + + short sLeftStickX; + short sLeftStickY; + short sRightStickX; + short sRightStickY; + + short sLeftPadX; + short sLeftPadY; + unsigned short unPressureLeft; + + short sRightPadX; + short sRightPadY; + unsigned short unPressureRight; + TritonMTUIMUNoQuat_t imu; +} TritonMTUNoQuat_t; + +// New Ibex packet that adds a timestamp to the trackpad sampling +// and reduces the size of the IMU timestamp. Timestamps are now 16 bits +typedef struct +{ + uint8_t seq_num; + uint32_t buttons; + short sTriggerLeft; + short sTriggerRight; + + short sLeftStickX; + short sLeftStickY; + short sRightStickX; + short sRightStickY; + + unsigned short unTrackpadTimestamp; + short sLeftPadX; + short sLeftPadY; + unsigned short unPressureLeft; + + short sRightPadX; + short sRightPadY; + unsigned short unPressureRight; + + TritonMTUIMUNoQuat32usTS_t imu; +} TritonMTUNoQuat32TS_t; + enum EChargeState { k_EChargeStateReset, @@ -632,4 +703,4 @@ typedef struct #pragma pack() -#endif // _CONTROLLER_STRUCTS +#endif // _CONTROLLER_STRUCTS_ diff --git a/third-party/SDL/src/joystick/linux/SDL_sysjoystick.c b/third-party/SDL/src/joystick/linux/SDL_sysjoystick.c index 4a924f41a5..ede0d5efdc 100644 --- a/third-party/SDL/src/joystick/linux/SDL_sysjoystick.c +++ b/third-party/SDL/src/joystick/linux/SDL_sysjoystick.c @@ -2086,7 +2086,7 @@ static void HandleInputEvents(SDL_Joystick *joystick) if (code == MSC_TIMESTAMP) { Sint32 tick = event->value; Sint32 delta; - if (joystick->hwdata->last_tick < tick) { + if (joystick->hwdata->last_tick <= tick) { delta = (tick - joystick->hwdata->last_tick); } else { delta = (SDL_MAX_SINT32 - joystick->hwdata->last_tick + tick + 1); diff --git a/third-party/SDL/src/joystick/sort_controllers.py b/third-party/SDL/src/joystick/sort_controllers.py old mode 100644 new mode 100755 diff --git a/third-party/SDL/src/joystick/usb_ids.h b/third-party/SDL/src/joystick/usb_ids.h index f916e6cb8b..5e48181c2e 100644 --- a/third-party/SDL/src/joystick/usb_ids.h +++ b/third-party/SDL/src/joystick/usb_ids.h @@ -79,6 +79,7 @@ #define USB_PRODUCT_ASTRO_C40_XBOX360 0x0024 #define USB_PRODUCT_BACKBONE_ONE_IOS 0x0103 #define USB_PRODUCT_BACKBONE_ONE_IOS_PS5 0x0104 +#define USB_PRODUCT_BACKBONE_ONE_PS5_V2 0x0304 #define USB_PRODUCT_BDA_XB1_CLASSIC 0x581a #define USB_PRODUCT_BDA_XB1_FIGHTPAD 0x791a #define USB_PRODUCT_BDA_XB1_SPECTRA_PRO 0x592a diff --git a/third-party/SDL/src/joystick/windows/SDL_rawinputjoystick.c b/third-party/SDL/src/joystick/windows/SDL_rawinputjoystick.c index c8cd9cd3a7..6846288aa7 100644 --- a/third-party/SDL/src/joystick/windows/SDL_rawinputjoystick.c +++ b/third-party/SDL/src/joystick/windows/SDL_rawinputjoystick.c @@ -56,7 +56,7 @@ #ifdef SDL_JOYSTICK_RAWINPUT_WGI #include "../../core/windows/SDL_windows.h" -typedef struct WindowsGamingInputGamepadState WindowsGamingInputGamepadState; +struct WindowsGamingInputGamepadState; #define GamepadButtons_GUIDE 0x40000000 #define COBJMACROS #include "windows.gaming.input.h" @@ -157,7 +157,7 @@ struct joystick_hwdata Uint8 wgi_correlation_id; Uint8 wgi_correlation_count; Uint8 wgi_uncorrelate_count; - WindowsGamingInputGamepadState *wgi_slot; + struct WindowsGamingInputGamepadState *wgi_slot; struct __x_ABI_CWindows_CGaming_CInput_CGamepadVibration vibration; #endif @@ -1976,20 +1976,24 @@ static void RAWINPUT_UpdateOtherAPIs(SDL_Joystick *joystick) state = SDL_POWERSTATE_ON_BATTERY; break; } - switch (battery_info->BatteryLevel) { - case BATTERY_LEVEL_EMPTY: - percent = 10; - break; - case BATTERY_LEVEL_LOW: - percent = 40; - break; - case BATTERY_LEVEL_MEDIUM: - percent = 70; - break; - default: - case BATTERY_LEVEL_FULL: - percent = 100; - break; + if (state == SDL_POWERSTATE_ON_BATTERY || state == SDL_POWERSTATE_CHARGING) { + switch (battery_info->BatteryLevel) { + case BATTERY_LEVEL_EMPTY: + percent = 10; + break; + case BATTERY_LEVEL_LOW: + percent = 40; + break; + case BATTERY_LEVEL_MEDIUM: + percent = 70; + break; + default: + case BATTERY_LEVEL_FULL: + percent = 100; + break; + } + } else { + percent = -1; } SDL_SendJoystickPowerInfo(joystick, state, percent); } diff --git a/third-party/SDL/src/joystick/windows/SDL_windows_gaming_input.c b/third-party/SDL/src/joystick/windows/SDL_windows_gaming_input.c index 4608bba091..f9076740ac 100644 --- a/third-party/SDL/src/joystick/windows/SDL_windows_gaming_input.c +++ b/third-party/SDL/src/joystick/windows/SDL_windows_gaming_input.c @@ -61,7 +61,7 @@ typedef struct WindowsGamingInputControllerState int steam_virtual_gamepad_slot; } WindowsGamingInputControllerState; -typedef HRESULT(WINAPI *CoIncrementMTAUsage_t)(CO_MTA_USAGE_COOKIE *pCookie); +typedef HRESULT(WINAPI *CoIncrementMTAUsage_t)(HANDLE* pCookie); // CO_MTA_USAGE_COOKIE* typedef HRESULT(WINAPI *RoGetActivationFactory_t)(HSTRING activatableClassId, REFIID iid, void **factory); typedef HRESULT(WINAPI *WindowsCreateStringReference_t)(PCWSTR sourceString, UINT32 length, HSTRING_HEADER *hstringHeader, HSTRING *string); typedef HRESULT(WINAPI *WindowsDeleteString_t)(HSTRING string); @@ -609,7 +609,7 @@ static bool WGI_JoystickInit(void) * As a workaround, we will keep a reference to the MTA to prevent COM from unloading DLLs later. * See https://github.com/libsdl-org/SDL/issues/5552 for more details. */ - static CO_MTA_USAGE_COOKIE cookie = NULL; + static HANDLE cookie = NULL; // CO_MTA_USAGE_COOKIE if (!cookie) { hr = wgi.CoIncrementMTAUsage(&cookie); if (FAILED(hr)) { diff --git a/third-party/SDL/src/joystick/windows/SDL_xinputjoystick.c b/third-party/SDL/src/joystick/windows/SDL_xinputjoystick.c index 8f26bcd7db..d7a132e1d1 100644 --- a/third-party/SDL/src/joystick/windows/SDL_xinputjoystick.c +++ b/third-party/SDL/src/joystick/windows/SDL_xinputjoystick.c @@ -300,20 +300,24 @@ static void UpdateXInputJoystickBatteryInformation(SDL_Joystick *joystick, XINPU state = SDL_POWERSTATE_ON_BATTERY; break; } - switch (pBatteryInformation->BatteryLevel) { - case BATTERY_LEVEL_EMPTY: - percent = 10; - break; - case BATTERY_LEVEL_LOW: - percent = 40; - break; - case BATTERY_LEVEL_MEDIUM: - percent = 70; - break; - default: - case BATTERY_LEVEL_FULL: - percent = 100; - break; + if (state == SDL_POWERSTATE_ON_BATTERY || state == SDL_POWERSTATE_CHARGING) { + switch (pBatteryInformation->BatteryLevel) { + case BATTERY_LEVEL_EMPTY: + percent = 10; + break; + case BATTERY_LEVEL_LOW: + percent = 40; + break; + case BATTERY_LEVEL_MEDIUM: + percent = 70; + break; + default: + case BATTERY_LEVEL_FULL: + percent = 100; + break; + } + } else { + percent = -1; } SDL_SendJoystickPowerInfo(joystick, state, percent); } @@ -391,6 +395,7 @@ void SDL_XINPUT_JoystickUpdate(SDL_Joystick *joystick) return; } + // FIXME: This does end up making a device ioctl() to query data, we shouldn't do this every update. SDL_zero(XBatteryInformation); if (XINPUTGETBATTERYINFORMATION) { result = XINPUTGETBATTERYINFORMATION(joystick->hwdata->userid, BATTERY_DEVTYPE_GAMEPAD, &XBatteryInformation); diff --git a/third-party/SDL/src/misc/unix/SDL_sysurl.c b/third-party/SDL/src/misc/unix/SDL_sysurl.c index 8745576e2c..32e2d476ea 100644 --- a/third-party/SDL/src/misc/unix/SDL_sysurl.c +++ b/third-party/SDL/src/misc/unix/SDL_sysurl.c @@ -33,8 +33,42 @@ extern char **environ; #endif +#ifdef HAVE_DBUS_DBUS_H +#include "../../core/linux/SDL_dbus.h" +#endif + +#ifdef SDL_VIDEO_DRIVER_WAYLAND +#include "../../video/wayland/SDL_waylandutil.h" +#endif + +// Wayland requires an activation token for the browser to take focus. +static void GetActivationToken(char **token, char **window_id) +{ +#ifdef SDL_VIDEO_DRIVER_WAYLAND + SDL_VideoDevice *vid = SDL_GetVideoDevice(); + + if (vid && SDL_strcmp(vid->name, "wayland") == 0) { + Wayland_GetActivationTokenForExport(vid, token, window_id); + } +#endif +} + bool SDL_SYS_OpenURL(const char *url) { + char *activation_token = NULL; + char *window_id = NULL; + + GetActivationToken(&activation_token, &window_id); + + // Prefer the D-Bus portal, if available. +#ifdef HAVE_DBUS_DBUS_H + if (SDL_DBus_OpenURI(url, window_id, activation_token)) { + SDL_free(activation_token); + SDL_free(window_id); + return true; + } +#endif + const char *args[] = { "xdg-open", url, NULL }; SDL_Environment *env = NULL; SDL_Process *process = NULL; @@ -47,6 +81,9 @@ bool SDL_SYS_OpenURL(const char *url) // Clear LD_PRELOAD so Chrome opens correctly when this application is launched by Steam SDL_UnsetEnvironmentVariable(env, "LD_PRELOAD"); + if (activation_token) { + SDL_SetEnvironmentVariable(env, "XDG_ACTIVATION_TOKEN", activation_token, false); + } SDL_PropertiesID props = SDL_CreateProperties(); if (!props) { @@ -64,6 +101,8 @@ bool SDL_SYS_OpenURL(const char *url) result = true; done: + SDL_free(activation_token); + SDL_free(window_id); SDL_DestroyEnvironment(env); SDL_DestroyProcess(process); diff --git a/third-party/SDL/src/render/SDL_render.c b/third-party/SDL/src/render/SDL_render.c index a4461f5d2b..b77d33e6ef 100644 --- a/third-party/SDL/src/render/SDL_render.c +++ b/third-party/SDL/src/render/SDL_render.c @@ -320,6 +320,12 @@ static bool FlushRenderCommands(SDL_Renderer *renderer) DebugLogRenderCommands(renderer->render_commands); +#if DONT_DRAW_WHILE_HIDDEN + // Don't send commands to the GPU while we're hidden + if (renderer->hidden) { + result = true; + } else +#endif result = renderer->RunCommandQueue(renderer, renderer->render_commands, renderer->vertex_data, renderer->vertex_data_used); // Move the whole render command queue to the unused pool so we can reuse them next time. @@ -3581,13 +3587,6 @@ bool SDL_RenderPoints(SDL_Renderer *renderer, const SDL_FPoint *points, int coun return true; } -#if DONT_DRAW_WHILE_HIDDEN - // Don't draw while we're hidden - if (renderer->hidden) { - return true; - } -#endif - const SDL_RenderViewState *view = renderer->view; if ((view->current_scale.x != 1.0f) || (view->current_scale.y != 1.0f)) { result = RenderPointsWithRects(renderer, points, count); @@ -3788,13 +3787,6 @@ bool SDL_RenderLines(SDL_Renderer *renderer, const SDL_FPoint *points, int count return true; } -#if DONT_DRAW_WHILE_HIDDEN - // Don't draw while we're hidden - if (renderer->hidden) { - return true; - } -#endif - SDL_RenderViewState *view = renderer->view; const bool islogical = (view->logical_presentation_mode != SDL_LOGICAL_PRESENTATION_DISABLED); @@ -3968,13 +3960,6 @@ bool SDL_RenderRects(SDL_Renderer *renderer, const SDL_FRect *rects, int count) return true; } -#if DONT_DRAW_WHILE_HIDDEN - // Don't draw while we're hidden - if (renderer->hidden) { - return true; - } -#endif - for (i = 0; i < count; ++i) { if (!SDL_RenderRect(renderer, &rects[i])) { return false; @@ -4014,13 +3999,6 @@ bool SDL_RenderFillRects(SDL_Renderer *renderer, const SDL_FRect *rects, int cou return true; } -#if DONT_DRAW_WHILE_HIDDEN - // Don't draw while we're hidden - if (renderer->hidden) { - return true; - } -#endif - frects = SDL_small_alloc(SDL_FRect, count, &isstack); if (!frects) { return false; @@ -4111,13 +4089,6 @@ bool SDL_RenderTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_F return SDL_SetError("Texture was not created with this renderer"); } -#if DONT_DRAW_WHILE_HIDDEN - // Don't draw while we're hidden - if (renderer->hidden) { - return true; - } -#endif - SDL_FRect real_srcrect; real_srcrect.x = 0.0f; real_srcrect.y = 0.0f; @@ -4165,13 +4136,6 @@ bool SDL_RenderTextureAffine(SDL_Renderer *renderer, SDL_Texture *texture, 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; @@ -4292,13 +4256,6 @@ bool SDL_RenderTextureRotated(SDL_Renderer *renderer, SDL_Texture *texture, 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; @@ -4553,13 +4510,6 @@ bool SDL_RenderTextureTiled(SDL_Renderer *renderer, SDL_Texture *texture, const return SDL_InvalidParamError("scale"); } -#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; @@ -5339,13 +5289,6 @@ bool SDL_RenderGeometryRaw(SDL_Renderer *renderer, return SDL_Unsupported(); } -#if DONT_DRAW_WHILE_HIDDEN - // Don't draw while we're hidden - if (renderer->hidden) { - return true; - } -#endif - if (num_vertices < 3) { return true; } diff --git a/third-party/SDL/src/render/direct3d11/SDL_render_d3d11.c b/third-party/SDL/src/render/direct3d11/SDL_render_d3d11.c index 4cfdf9bab6..5f2f4cd7b5 100644 --- a/third-party/SDL/src/render/direct3d11/SDL_render_d3d11.c +++ b/third-party/SDL/src/render/direct3d11/SDL_render_d3d11.c @@ -926,7 +926,7 @@ static HRESULT D3D11_CreateSwapChain(SDL_Renderer *renderer, int w, int h) IDXGIFactory_MakeWindowAssociation(data->dxgiFactory, hwnd, DXGI_MWA_NO_WINDOW_CHANGES); #else - SDL_SetError(SDL_FUNCTION ", Unable to find something to attach a swap chain to"); + SDL_SetError("%s, Unable to find something to attach a swap chain to", SDL_FUNCTION); goto done; #endif // defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_WINGDK) / else } diff --git a/third-party/SDL/src/render/direct3d12/SDL_render_d3d12.c b/third-party/SDL/src/render/direct3d12/SDL_render_d3d12.c index f4667dd052..d743804b09 100644 --- a/third-party/SDL/src/render/direct3d12/SDL_render_d3d12.c +++ b/third-party/SDL/src/render/direct3d12/SDL_render_d3d12.c @@ -929,7 +929,7 @@ static HRESULT D3D12_CreateDeviceResources(SDL_Renderer *renderer) D3D_GUID(SDL_IID_IDXGIAdapter4), (void **)&data->dxgiAdapter); if (FAILED(result)) { - WIN_SetErrorFromHRESULT("IDXGIFactory6::EnumAdapterByGPUPreference", result); + WIN_SetErrorFromHRESULT("IDXGIFactory6::EnumAdapterByGpuPreference", result); goto done; } diff --git a/third-party/SDL/src/render/gpu/SDL_render_gpu.c b/third-party/SDL/src/render/gpu/SDL_render_gpu.c index 7b15e4adc0..f8ab103c3c 100644 --- a/third-party/SDL/src/render/gpu/SDL_render_gpu.c +++ b/third-party/SDL/src/render/gpu/SDL_render_gpu.c @@ -22,6 +22,7 @@ #ifdef SDL_VIDEO_RENDER_GPU +#include "../../events/SDL_windowevents_c.h" #include "../../video/SDL_pixels_c.h" #include "../SDL_d3dmath.h" #include "../SDL_sysrender.h" @@ -1501,6 +1502,9 @@ static bool GPU_RenderPresent(SDL_Renderer *renderer) if (swapchain_texture_width != data->backbuffer.width || swapchain_texture_height != data->backbuffer.height) { CreateBackbuffer(data, swapchain_texture_width, swapchain_texture_height, SDL_GetGPUSwapchainTextureFormat(data->device, renderer->window)); + + // Notify the application that it needs to redraw this frame + SDL_SendWindowEvent(renderer->window, SDL_EVENT_WINDOW_EXPOSED, 0, 0); } } else { SDL_SubmitGPUCommandBuffer(data->state.command_buffer); diff --git a/third-party/SDL/src/render/gpu/shaders/build-shaders.sh b/third-party/SDL/src/render/gpu/shaders/build-shaders.sh old mode 100644 new mode 100755 diff --git a/third-party/SDL/src/render/metal/SDL_render_metal.m b/third-party/SDL/src/render/metal/SDL_render_metal.m index 60085ed25b..c8a4579182 100644 --- a/third-party/SDL/src/render/metal/SDL_render_metal.m +++ b/third-party/SDL/src/render/metal/SDL_render_metal.m @@ -682,7 +682,7 @@ static bool METAL_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD { @autoreleasepool { SDL3METAL_RenderData *data = (__bridge SDL3METAL_RenderData *)renderer->internal; - MTLPixelFormat pixfmt; + MTLPixelFormat pixfmt = MTLPixelFormatInvalid; MTLTextureDescriptor *mtltexdesc; id mtltexture = nil, mtltextureUv = nil; SDL3METAL_TextureData *texturedata; @@ -715,6 +715,26 @@ static bool METAL_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD case SDL_PIXELFORMAT_ABGR2101010: pixfmt = MTLPixelFormatRGB10A2Unorm; break; + case SDL_PIXELFORMAT_RGB565: + if (@available(macOS 11.0, *)) { + pixfmt = MTLPixelFormatB5G6R5Unorm; + } + break; + case SDL_PIXELFORMAT_RGBA5551: + if (@available(macOS 11.0, *)) { + pixfmt = MTLPixelFormatA1BGR5Unorm; + } + break; + case SDL_PIXELFORMAT_ARGB1555: + if (@available(macOS 11.0, *)) { + pixfmt = MTLPixelFormatBGR5A1Unorm; + } + break; + case SDL_PIXELFORMAT_RGBA4444: + if (@available(macOS 11.0, *)) { + pixfmt = MTLPixelFormatABGR4Unorm; + } + break; case SDL_PIXELFORMAT_INDEX8: case SDL_PIXELFORMAT_IYUV: case SDL_PIXELFORMAT_YV12: @@ -732,6 +752,10 @@ static bool METAL_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD pixfmt = MTLPixelFormatRGBA32Float; break; default: + break; + } + + if (pixfmt == MTLPixelFormatInvalid) { return SDL_SetError("Texture format %s not supported by Metal", SDL_GetPixelFormatName(texture->format)); } @@ -1514,14 +1538,14 @@ static bool SetDrawState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, c METAL_GetOutputSize(renderer, &output.w, &output.h); } - if (SDL_GetRectIntersection(&output, &clip, &clip)) { - MTLScissorRect mtlrect; - mtlrect.x = clip.x; - mtlrect.y = clip.y; - mtlrect.width = clip.w; - mtlrect.height = clip.h; - [data.mtlcmdencoder setScissorRect:mtlrect]; - } + SDL_GetRectIntersection(&output, &clip, &clip); + + MTLScissorRect mtlrect; + mtlrect.x = clip.x; + mtlrect.y = clip.y; + mtlrect.width = clip.w; + mtlrect.height = clip.h; + [data.mtlcmdencoder setScissorRect:mtlrect]; statecache->cliprect_dirty = false; } @@ -1951,6 +1975,21 @@ static SDL_Surface *METAL_RenderReadPixels(SDL_Renderer *renderer, const SDL_Rec case MTLPixelFormatRGBA16Float: format = SDL_PIXELFORMAT_RGBA64_FLOAT; break; + case MTLPixelFormatRGBA32Float: + format = SDL_PIXELFORMAT_RGBA128_FLOAT; + break; + case MTLPixelFormatB5G6R5Unorm: + format = SDL_PIXELFORMAT_RGB565; + break; + case MTLPixelFormatA1BGR5Unorm: + format = SDL_PIXELFORMAT_RGBA5551; + break; + case MTLPixelFormatBGR5A1Unorm: + format = SDL_PIXELFORMAT_ARGB1555; + break; + case MTLPixelFormatABGR4Unorm: + format = SDL_PIXELFORMAT_RGBA4444; + break; default: SDL_SetError("Unknown framebuffer pixel format"); return NULL; @@ -2367,6 +2406,14 @@ static bool METAL_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_ABGR2101010); SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_RGBA64_FLOAT); SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_RGBA128_FLOAT); + if (@available(macOS 11.0, iOS 13.0, tvOS 13.0, *)) { + if ([mtldevice supportsFamily:MTLGPUFamilyApple1]) { + SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_RGB565); + SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_RGBA5551); + SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_ARGB1555); + SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_RGBA4444); + } + } SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_INDEX8); SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_YV12); SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_IYUV); diff --git a/third-party/SDL/src/render/metal/build-metal-shaders.sh b/third-party/SDL/src/render/metal/build-metal-shaders.sh old mode 100644 new mode 100755 diff --git a/third-party/SDL/src/render/opengles2/SDL_render_gles2.c b/third-party/SDL/src/render/opengles2/SDL_render_gles2.c index 52423884dc..a37f2b1b74 100644 --- a/third-party/SDL/src/render/opengles2/SDL_render_gles2.c +++ b/third-party/SDL/src/render/opengles2/SDL_render_gles2.c @@ -54,15 +54,6 @@ * Context structures * *************************************************************************************************/ -typedef struct GLES2_FBOList GLES2_FBOList; - -struct GLES2_FBOList -{ - Uint32 w, h; - GLuint FBO; - GLES2_FBOList *next; -}; - typedef struct { GLuint texture; @@ -71,6 +62,7 @@ typedef struct typedef struct { GLuint texture; + GLuint fbo; // framebuffer object; this is zero unless this texture is a render target. bool texture_external; GLenum texture_type; GLenum pixel_format; @@ -90,7 +82,6 @@ typedef struct SDL_ScaleMode texture_scale_mode; SDL_TextureAddressMode texture_address_mode_u; SDL_TextureAddressMode texture_address_mode_v; - GLES2_FBOList *fbo; } GLES2_TextureData; typedef enum @@ -193,7 +184,6 @@ typedef struct GLES2_RenderData #define SDL_PROC(ret, func, params) ret (APIENTRY *func) params; #include "SDL_gles2funcs.h" #undef SDL_PROC - GLES2_FBOList *framebuffers; GLuint window_framebuffer; GLuint shader_id_cache[GLES2_SHADER_COUNT]; @@ -301,23 +291,6 @@ static bool GLES2_LoadFunctions(GLES2_RenderData *data) return true; } -static GLES2_FBOList *GLES2_GetFBO(GLES2_RenderData *data, Uint32 w, Uint32 h) -{ - GLES2_FBOList *result = data->framebuffers; - while ((result) && ((result->w != w) || (result->h != h))) { - result = result->next; - } - if (!result) { - result = (GLES2_FBOList *)SDL_malloc(sizeof(GLES2_FBOList)); - result->w = w; - result->h = h; - data->glGenFramebuffers(1, &result->FBO); - result->next = data->framebuffers; - data->framebuffers = result; - } - return result; -} - static bool GLES2_ActivateRenderer(SDL_Renderer *renderer) { GLES2_RenderData *data = (GLES2_RenderData *)renderer->internal; @@ -1685,14 +1658,6 @@ static void GLES2_DestroyRenderer(SDL_Renderer *renderer) } if (data->context) { - while (data->framebuffers) { - GLES2_FBOList *nextnode = data->framebuffers->next; - data->glDeleteFramebuffers(1, &data->framebuffers->FBO); - GL_CheckError("", renderer); - SDL_free(data->framebuffers); - data->framebuffers = nextnode; - } - #if USE_VERTEX_BUFFER_OBJECTS data->glDeleteBuffers(SDL_arraysize(data->vertex_buffers), data->vertex_buffers); GL_CheckError("", renderer); @@ -1956,6 +1921,11 @@ static bool GLES2_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD if (texture->format != SDL_PIXELFORMAT_EXTERNAL_OES) { renderdata->glTexImage2D(data->texture_type, 0, format, texture->w, texture->h, 0, format, type, NULL); if (!GL_CheckError("glTexImage2D()", renderer)) { + if (!data->texture_external) { + renderdata->glDeleteTextures(1, &data->texture); + } + SDL_free(data->pixel_data); + SDL_free(data); return false; } } @@ -1965,9 +1935,20 @@ static bool GLES2_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD SDL_SetNumberProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_OPENGLES2_TEXTURE_TARGET_NUMBER, data->texture_type); if (texture->access == SDL_TEXTUREACCESS_TARGET) { - data->fbo = GLES2_GetFBO((GLES2_RenderData *)renderer->internal, texture->w, texture->h); - } else { - data->fbo = NULL; + renderdata->glGenFramebuffers(1, &data->fbo); + renderdata->glBindFramebuffer(GL_FRAMEBUFFER, data->fbo); + renderdata->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, data->texture_type, data->texture, 0); + const GLenum status = renderdata->glCheckFramebufferStatus(GL_FRAMEBUFFER); + renderdata->glBindFramebuffer(GL_FRAMEBUFFER, renderer->target ? ((GLES2_TextureData *)renderer->target->internal)->fbo : renderdata->window_framebuffer); // rebind previous fbo. + if (status != GL_FRAMEBUFFER_COMPLETE) { + renderdata->glDeleteFramebuffers(1, &data->fbo); + if (!data->texture_external) { + renderdata->glDeleteTextures(1, &data->texture); + } + SDL_free(data->pixel_data); + SDL_free(data); + return SDL_SetError("Texture framebuffer was incomplete"); + } } return GL_CheckError("", renderer); @@ -2206,24 +2187,8 @@ static void GLES2_UnlockTexture(SDL_Renderer *renderer, SDL_Texture *texture) static bool GLES2_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture) { GLES2_RenderData *data = (GLES2_RenderData *)renderer->internal; - GLES2_TextureData *texturedata = NULL; - GLenum status; - data->drawstate.viewport_dirty = true; - - if (!texture) { - data->glBindFramebuffer(GL_FRAMEBUFFER, data->window_framebuffer); - } else { - texturedata = (GLES2_TextureData *)texture->internal; - data->glBindFramebuffer(GL_FRAMEBUFFER, texturedata->fbo->FBO); - // TODO: check if texture pixel format allows this operation - data->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texturedata->texture_type, texturedata->texture, 0); - // Check FBO status - status = data->glCheckFramebufferStatus(GL_FRAMEBUFFER); - if (status != GL_FRAMEBUFFER_COMPLETE) { - return SDL_SetError("glFramebufferTexture2D() failed"); - } - } + data->glBindFramebuffer(GL_FRAMEBUFFER, texture ? ((GLES2_TextureData *)texture->internal)->fbo : data->window_framebuffer); return true; } @@ -2244,6 +2209,9 @@ static void GLES2_DestroyTexture(SDL_Renderer *renderer, SDL_Texture *texture) // Destroy the texture if (tdata) { + if (tdata->fbo) { + data->glDeleteFramebuffers(1, &tdata->fbo); + } if (tdata->texture && !tdata->texture_external) { data->glDeleteTextures(1, &tdata->texture); } @@ -2433,7 +2401,6 @@ static bool GLES2_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL data->glGenBuffers(SDL_arraysize(data->vertex_buffers), data->vertex_buffers); #endif - data->framebuffers = NULL; data->glGetIntegerv(GL_FRAMEBUFFER_BINDING, &window_framebuffer); data->window_framebuffer = (GLuint)window_framebuffer; @@ -2464,7 +2431,9 @@ static bool GLES2_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL // Full NPOT textures (that can use GL_REPEAT, etc) are a core feature of GLES3, // and an extension in GLES2. - if ((major < 3) && !SDL_GL_ExtensionSupported("GL_ARB_texture_non_power_of_two")) { + if (major < 3 && + !SDL_GL_ExtensionSupported("GL_ARB_texture_non_power_of_two") && + !SDL_GL_ExtensionSupported("GL_OES_texture_npot")) { renderer->npot_texture_wrap_unsupported = true; } diff --git a/third-party/SDL/src/render/ps2/SDL_render_ps2.c b/third-party/SDL/src/render/ps2/SDL_render_ps2.c index 08314e4d86..18108cc67e 100644 --- a/third-party/SDL/src/render/ps2/SDL_render_ps2.c +++ b/third-party/SDL/src/render/ps2/SDL_render_ps2.c @@ -450,7 +450,7 @@ static bool PS2_RenderGeometry(SDL_Renderer *renderer, void *vertices, SDL_Rende PS2_SetBlendMode(data, cmd->data.draw.blend); if (cmd->data.draw.texture) { - const GSPRIMUVPOINT *verts = (GSPRIMUVPOINT *) (vertices + cmd->data.draw.first); + const GSPRIMUVPOINT *verts = (GSPRIMUVPOINT *) ((Uint8*)vertices + cmd->data.draw.first); GSTEXTURE *ps2_tex = (GSTEXTURE *)cmd->data.draw.texture->internal; switch (cmd->data.draw.texture_scale_mode) { @@ -467,7 +467,7 @@ static bool PS2_RenderGeometry(SDL_Renderer *renderer, void *vertices, SDL_Rende gsKit_TexManager_bind(data->gsGlobal, ps2_tex); gsKit_prim_list_triangle_goraud_texture_uv_3d(data->gsGlobal, ps2_tex, count, verts); } else { - const GSPRIMPOINT *verts = (GSPRIMPOINT *)(vertices + cmd->data.draw.first); + const GSPRIMPOINT *verts = (GSPRIMPOINT *)((Uint8*)vertices + cmd->data.draw.first); gsKit_prim_list_triangle_gouraud_3d(data->gsGlobal, count, verts); } @@ -478,7 +478,7 @@ static bool PS2_RenderLines(SDL_Renderer *renderer, void *vertices, SDL_RenderCo { PS2_RenderData *data = (PS2_RenderData *)renderer->internal; const size_t count = cmd->data.draw.count; - const GSPRIMPOINT *verts = (GSPRIMPOINT *)(vertices + cmd->data.draw.first); + const GSPRIMPOINT *verts = (GSPRIMPOINT *)((Uint8*)vertices + cmd->data.draw.first); PS2_SetBlendMode(data, cmd->data.draw.blend); gsKit_prim_list_line_goraud_3d(data->gsGlobal, count, verts); @@ -491,7 +491,7 @@ static bool PS2_RenderPoints(SDL_Renderer *renderer, void *vertices, SDL_RenderC { PS2_RenderData *data = (PS2_RenderData *)renderer->internal; const size_t count = cmd->data.draw.count; - const GSPRIMPOINT *verts = (GSPRIMPOINT *)(vertices + cmd->data.draw.first); + const GSPRIMPOINT *verts = (GSPRIMPOINT *)((Uint8*)vertices + cmd->data.draw.first); PS2_SetBlendMode(data, cmd->data.draw.blend); gsKit_prim_list_points(data->gsGlobal, count, verts); diff --git a/third-party/SDL/src/render/software/SDL_render_sw.c b/third-party/SDL/src/render/software/SDL_render_sw.c index 160e2a10be..ce3a3f5165 100644 --- a/third-party/SDL/src/render/software/SDL_render_sw.c +++ b/third-party/SDL/src/render/software/SDL_render_sw.c @@ -34,6 +34,7 @@ #include "SDL_triangle.h" #include "../../video/SDL_pixels_c.h" #include "../../video/SDL_rotate.h" +#include "../../video/SDL_sysvideo.h" // SDL surface based renderer implementation @@ -54,12 +55,13 @@ typedef struct static SDL_Surface *SW_ActivateRenderer(SDL_Renderer *renderer) { SW_RenderData *data = (SW_RenderData *)renderer->internal; + SDL_Window *window = renderer->window; if (!data->surface) { data->surface = data->window; } - if (!data->surface) { - SDL_Surface *surface = SDL_GetWindowSurface(renderer->window); + if (window && (!data->surface || !window->surface_valid)) { + SDL_Surface *surface = SDL_GetWindowSurface(window); if (surface) { data->surface = data->window = surface; } diff --git a/third-party/SDL/src/render/vitagxm/SDL_render_vita_gxm.c b/third-party/SDL/src/render/vitagxm/SDL_render_vita_gxm.c index 970ce1095e..7cfbaf18af 100644 --- a/third-party/SDL/src/render/vitagxm/SDL_render_vita_gxm.c +++ b/third-party/SDL/src/render/vitagxm/SDL_render_vita_gxm.c @@ -349,25 +349,25 @@ static bool VITA_GXM_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, length = rect->w * SDL_BYTESPERPIXEL(texture->format); if (length == pitch && length == dpitch) { SDL_memcpy(dst, pixels, length * rect->h); - pixels += pitch * rect->h; + pixels = (const Uint8 *)pixels + pitch * rect->h; } else { for (row = 0; row < rect->h; ++row) { SDL_memcpy(dst, pixels, length); - pixels += pitch; + pixels = (const Uint8 *)pixels + pitch; dst += dpitch; } } #ifdef SDL_HAVE_YUV if (vita_texture->yuv) { - void *Udst; - void *Vdst; + Uint8 *Udst; + Uint8 *Vdst; int uv_pitch = (dpitch + 1) / 2; int uv_src_pitch = (pitch + 1) / 2; SDL_Rect UVrect = { rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2 }; // skip Y plane - Uint8 *Dpixels = gxm_texture_get_datap(vita_texture->tex) + (vita_texture->pitch * vita_texture->h); + Uint8 *Dpixels = (Uint8*)gxm_texture_get_datap(vita_texture->tex) + (vita_texture->pitch * vita_texture->h); Udst = Dpixels + (UVrect.y * uv_pitch) + UVrect.x; Vdst = Dpixels + (uv_pitch * ((vita_texture->h + 1) / 2)) + (UVrect.y * uv_pitch) + UVrect.x; @@ -377,11 +377,11 @@ static bool VITA_GXM_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, // U plane if (length == uv_src_pitch && length == uv_pitch) { SDL_memcpy(Udst, pixels, length * UVrect.h); - pixels += uv_src_pitch * UVrect.h; + pixels = (const Uint8 *)pixels + uv_src_pitch * UVrect.h; } else { for (row = 0; row < UVrect.h; ++row) { SDL_memcpy(Udst, pixels, length); - pixels += uv_src_pitch; + pixels = (const Uint8 *)pixels + uv_src_pitch; Udst += uv_pitch; } } @@ -392,19 +392,19 @@ static bool VITA_GXM_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, } else { for (row = 0; row < UVrect.h; ++row) { SDL_memcpy(Vdst, pixels, length); - pixels += uv_src_pitch; + pixels = (const Uint8 *)pixels + uv_src_pitch; Vdst += uv_pitch; } } } else if (vita_texture->nv12) { - void *UVdst; + Uint8 *UVdst; int uv_pitch = 2 * ((dpitch + 1) / 2); int uv_src_pitch = 2 * ((pitch + 1) / 2); SDL_Rect UVrect = { rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2 }; // skip Y plane - void *Dpixels = (void *)((Uint8 *)gxm_texture_get_datap(vita_texture->tex) + (vita_texture->pitch * vita_texture->h)); + Uint8 *Dpixels = (Uint8 *)gxm_texture_get_datap(vita_texture->tex) + (vita_texture->pitch * vita_texture->h); UVdst = Dpixels + (UVrect.y * uv_pitch) + UVrect.x; length = UVrect.w * 2; @@ -415,7 +415,7 @@ static bool VITA_GXM_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, } else { for (row = 0; row < UVrect.h; ++row) { SDL_memcpy(UVdst, pixels, length); - pixels += uv_src_pitch; + pixels = (const Uint8 *)pixels + uv_src_pitch; UVdst += uv_pitch; } } @@ -456,13 +456,13 @@ static bool VITA_GXM_UpdateTextureYUV(SDL_Renderer *renderer, SDL_Texture *textu // U/V planes { - void *Udst; - void *Vdst; + Uint8 *Udst; + Uint8 *Vdst; VITA_GXM_TextureData *vita_texture = (VITA_GXM_TextureData *)texture->internal; int uv_pitch = (dpitch + 1) / 2; // skip Y plane - void *pixels = (void *)((Uint8 *)gxm_texture_get_datap(vita_texture->tex) + (vita_texture->pitch * vita_texture->h)); + Uint8 *pixels = (Uint8 *)gxm_texture_get_datap(vita_texture->tex) + (vita_texture->pitch * vita_texture->h); if (texture->format == SDL_PIXELFORMAT_YV12) { // YVU Vdst = pixels + (UVrect.y * uv_pitch) + UVrect.x; @@ -529,12 +529,12 @@ static bool VITA_GXM_UpdateTextureNV(SDL_Renderer *renderer, SDL_Texture *textur // UV plane { - void *UVdst; + Uint8 *UVdst; VITA_GXM_TextureData *vita_texture = (VITA_GXM_TextureData *)texture->internal; int uv_pitch = 2 * ((dpitch + 1) / 2); // skip Y plane - void *pixels = (void *)((Uint8 *)gxm_texture_get_datap(vita_texture->tex) + (vita_texture->pitch * vita_texture->h)); + Uint8 *pixels = (Uint8 *)gxm_texture_get_datap(vita_texture->tex) + (vita_texture->pitch * vita_texture->h); UVdst = pixels + (UVrect.y * uv_pitch) + UVrect.x; diff --git a/third-party/SDL/src/stdlib/SDL_string.c b/third-party/SDL/src/stdlib/SDL_string.c index 4a870f6e0d..e0e7d57337 100644 --- a/third-party/SDL/src/stdlib/SDL_string.c +++ b/third-party/SDL/src/stdlib/SDL_string.c @@ -1153,6 +1153,11 @@ static const char ntoa_table[] = { }; #endif // ntoa() conversion table +char *SDL_uitoa(unsigned int value, char *string, int radix) +{ + return SDL_ultoa((unsigned long)value, string, radix); +} + char *SDL_itoa(int value, char *string, int radix) { #ifdef HAVE_ITOA @@ -1162,15 +1167,6 @@ char *SDL_itoa(int value, char *string, int radix) #endif // HAVE_ITOA } -char *SDL_uitoa(unsigned int value, char *string, int radix) -{ -#ifdef HAVE__UITOA - return _uitoa(value, string, radix); -#else - return SDL_ultoa((unsigned long)value, string, radix); -#endif // HAVE__UITOA -} - char *SDL_ltoa(long value, char *string, int radix) { #ifdef HAVE__LTOA diff --git a/third-party/SDL/src/thread/SDL_thread.c b/third-party/SDL/src/thread/SDL_thread.c index 9ffba9592c..639090c498 100644 --- a/third-party/SDL/src/thread/SDL_thread.c +++ b/third-party/SDL/src/thread/SDL_thread.c @@ -262,9 +262,12 @@ void SDL_Generic_QuitTLSData(void) static SDL_error *SDL_GetStaticErrBuf(void) { static SDL_error SDL_global_error; - static char SDL_global_error_str[128]; - SDL_global_error.str = SDL_global_error_str; - SDL_global_error.len = sizeof(SDL_global_error_str); + static char SDL_global_error_str1[128]; + static char SDL_global_error_str2[128]; + SDL_global_error.info[0].str = SDL_global_error_str1; + SDL_global_error.info[0].len = sizeof(SDL_global_error_str1); + SDL_global_error.info[1].str = SDL_global_error_str2; + SDL_global_error.info[1].len = sizeof(SDL_global_error_str2); return &SDL_global_error; } @@ -272,9 +275,11 @@ static SDL_error *SDL_GetStaticErrBuf(void) static void SDLCALL SDL_FreeErrBuf(void *data) { SDL_error *errbuf = (SDL_error *)data; - - if (errbuf->str) { - errbuf->free_func(errbuf->str); + if (errbuf->info[0].str) { + errbuf->free_func(errbuf->info[0].str); + } + if (errbuf->info[1].str) { + errbuf->free_func(errbuf->info[1].str); } errbuf->free_func(errbuf); } @@ -330,11 +335,6 @@ void SDL_RunThread(SDL_Thread *thread) // Perform any system-dependent setup - this function may not fail SDL_SYS_SetupThread(thread->name); - // Get the thread id - thread->threadid = SDL_GetCurrentThreadID(); - - SDL_SignalSemaphore(thread->ready_sem); // the thread is officially ready to run! - // Run the function *statusloc = userfunc(userdata); @@ -391,13 +391,6 @@ SDL_Thread *SDL_CreateThreadWithPropertiesRuntime(SDL_PropertiesID props, } } - thread->ready_sem = SDL_CreateSemaphore(0); - if (!thread->ready_sem) { - SDL_free(thread->name); - SDL_free(thread); - return NULL; - } - thread->userfunc = fn; thread->userdata = userdata; thread->stacksize = stacksize; @@ -408,16 +401,11 @@ SDL_Thread *SDL_CreateThreadWithPropertiesRuntime(SDL_PropertiesID props, if (!SDL_SYS_CreateThread(thread, pfnBeginThread, pfnEndThread)) { // Oops, failed. Gotta free everything SDL_SetObjectValid(thread, SDL_OBJECT_TYPE_THREAD, false); - SDL_DestroySemaphore(thread->ready_sem); SDL_free(thread->name); SDL_free(thread); - thread = NULL; + return NULL; } - SDL_WaitSemaphore(thread->ready_sem); - SDL_DestroySemaphore(thread->ready_sem); - thread->ready_sem = NULL; - // Everything is running now return thread; } diff --git a/third-party/SDL/src/thread/SDL_thread_c.h b/third-party/SDL/src/thread/SDL_thread_c.h index 25b7096482..3659b85cc4 100644 --- a/third-party/SDL/src/thread/SDL_thread_c.h +++ b/third-party/SDL/src/thread/SDL_thread_c.h @@ -54,7 +54,6 @@ struct SDL_Thread SDL_error errbuf; char *name; size_t stacksize; // 0 for default, >0 for user-specified stack size. - SDL_Semaphore *ready_sem; // signals when the thread is set up and about to start running. int(SDLCALL *userfunc)(void *); void *userdata; void *data; diff --git a/third-party/SDL/src/thread/n3ds/SDL_sysmutex_c.h b/third-party/SDL/src/thread/n3ds/SDL_sysmutex_c.h index 1db794ccf6..37b5965cd4 100644 --- a/third-party/SDL/src/thread/n3ds/SDL_sysmutex_c.h +++ b/third-party/SDL/src/thread/n3ds/SDL_sysmutex_c.h @@ -30,4 +30,4 @@ struct SDL_Mutex RecursiveLock lock; }; -#endif // SDL_sysmutex_c_h +#endif // SDL_sysmutex_c_h_ diff --git a/third-party/SDL/src/thread/n3ds/SDL_systhread.c b/third-party/SDL/src/thread/n3ds/SDL_systhread.c index a9a18ab8f7..5c1f0a1fa0 100644 --- a/third-party/SDL/src/thread/n3ds/SDL_systhread.c +++ b/third-party/SDL/src/thread/n3ds/SDL_systhread.c @@ -72,6 +72,10 @@ bool SDL_SYS_CreateThread(SDL_Thread *thread, return SDL_SetError("Couldn't create thread"); } + u32 thread_ID = 0; + svcGetThreadId(&thread_ID, threadGetHandle(thread->handle)); + thread->threadid = (SDL_ThreadID) thread_ID; + return true; } diff --git a/third-party/SDL/src/thread/ps2/SDL_systhread.c b/third-party/SDL/src/thread/ps2/SDL_systhread.c index 96c9dcc8e5..6d038915c6 100644 --- a/third-party/SDL/src/thread/ps2/SDL_systhread.c +++ b/third-party/SDL/src/thread/ps2/SDL_systhread.c @@ -95,6 +95,9 @@ bool SDL_SYS_CreateThread(SDL_Thread *thread, if (StartThread(thread->handle, thread) < 0) { return SDL_SetError("StartThread() failed"); } + + thread->threadid = (SDL_ThreadID) thread->handle; + return true; } diff --git a/third-party/SDL/src/thread/psp/SDL_systhread.c b/third-party/SDL/src/thread/psp/SDL_systhread.c index 545165846d..f61dcea790 100644 --- a/third-party/SDL/src/thread/psp/SDL_systhread.c +++ b/third-party/SDL/src/thread/psp/SDL_systhread.c @@ -66,6 +66,8 @@ bool SDL_SYS_CreateThread(SDL_Thread *thread, return SDL_SetError("sceKernelCreateThread() failed"); } + thread->threadid = (SDL_ThreadID) thread->handle; + sceKernelStartThread(thread->handle, 4, &thread); return true; } diff --git a/third-party/SDL/src/thread/pthread/SDL_systhread.c b/third-party/SDL/src/thread/pthread/SDL_systhread.c index 09d3c896b8..f4d4c4d872 100644 --- a/third-party/SDL/src/thread/pthread/SDL_systhread.c +++ b/third-party/SDL/src/thread/pthread/SDL_systhread.c @@ -116,6 +116,8 @@ bool SDL_SYS_CreateThread(SDL_Thread *thread, return SDL_SetError("Not enough resources to create thread"); } + thread->threadid = (SDL_ThreadID) thread->handle; // the SDL thread ID is just the pthread_t. + return true; } diff --git a/third-party/SDL/src/thread/vita/SDL_systhread.c b/third-party/SDL/src/thread/vita/SDL_systhread.c index 79ac8b5ffb..32946522a1 100644 --- a/third-party/SDL/src/thread/vita/SDL_systhread.c +++ b/third-party/SDL/src/thread/vita/SDL_systhread.c @@ -86,6 +86,8 @@ bool SDL_SYS_CreateThread(SDL_Thread *thread, return SDL_SetError("sceKernelCreateThread() failed"); } + thread->threadid = (SDL_ThreadID) thread->handle; + sceKernelStartThread(thread->handle, 4, &thread); return true; } diff --git a/third-party/SDL/src/thread/windows/SDL_systhread.c b/third-party/SDL/src/thread/windows/SDL_systhread.c index 530f44929a..1244c34464 100644 --- a/third-party/SDL/src/thread/windows/SDL_systhread.c +++ b/third-party/SDL/src/thread/windows/SDL_systhread.c @@ -77,15 +77,18 @@ bool SDL_SYS_CreateThread(SDL_Thread *thread, thread->handle = (SYS_ThreadHandle)((size_t)pfnBeginThread(NULL, (unsigned int)thread->stacksize, RunThreadViaBeginThreadEx, thread, flags, &threadid)); + thread->threadid = (SDL_ThreadID) threadid; } else { DWORD threadid = 0; thread->handle = CreateThread(NULL, thread->stacksize, RunThreadViaCreateThread, thread, flags, &threadid); + thread->threadid = (SDL_ThreadID) threadid; } if (!thread->handle) { return SDL_SetError("Not enough resources to create thread"); } + return true; } diff --git a/third-party/SDL/src/tray/windows/SDL_tray.c b/third-party/SDL/src/tray/windows/SDL_tray.c index b7f752f958..e293bd6d99 100644 --- a/third-party/SDL/src/tray/windows/SDL_tray.c +++ b/third-party/SDL/src/tray/windows/SDL_tray.c @@ -196,7 +196,7 @@ static wchar_t *escape_label(const char *in) return out; } -static HICON load_default_icon() +static HICON load_default_icon(void) { HINSTANCE hInstance = GetModuleHandle(NULL); if (!hInstance) { diff --git a/third-party/SDL/src/video/SDL_clipboard.c b/third-party/SDL/src/video/SDL_clipboard.c index d76bbfdcfe..a9887fda15 100644 --- a/third-party/SDL/src/video/SDL_clipboard.c +++ b/third-party/SDL/src/video/SDL_clipboard.c @@ -62,7 +62,7 @@ void SDL_CancelClipboardData(Uint32 sequence) _this->clipboard_userdata = NULL; } -bool SDL_SaveClipboardMimeTypes(const char **mime_types, size_t num_mime_types) +bool SDL_SaveClipboardMimeTypes(const char *const *mime_types, size_t num_mime_types) { SDL_VideoDevice *_this = SDL_GetVideoDevice(); @@ -93,7 +93,7 @@ bool SDL_SaveClipboardMimeTypes(const char **mime_types, size_t num_mime_types) return true; } -bool SDL_SetClipboardData(SDL_ClipboardDataCallback callback, SDL_ClipboardCleanupCallback cleanup, void *userdata, const char **mime_types, size_t num_mime_types) +bool SDL_SetClipboardData(SDL_ClipboardDataCallback callback, SDL_ClipboardCleanupCallback cleanup, void *userdata, const char *const *mime_types, size_t num_mime_types) { SDL_VideoDevice *_this = SDL_GetVideoDevice(); @@ -265,7 +265,7 @@ bool SDL_HasClipboardData(const char *mime_type) } } -char **SDL_CopyClipboardMimeTypes(const char **clipboard_mime_types, size_t num_mime_types, bool temporary) +char **SDL_CopyClipboardMimeTypes(const char *const *clipboard_mime_types, size_t num_mime_types, bool temporary) { size_t allocSize = sizeof(char *); for (size_t i = 0; i < num_mime_types; i++) { @@ -326,12 +326,12 @@ bool SDL_IsTextMimeType(const char *mime_type) return (SDL_strncmp(mime_type, "text", 4) == 0); } -static const char **SDL_GetTextMimeTypes(SDL_VideoDevice *_this, size_t *num_mime_types) +static const char *const *SDL_GetTextMimeTypes(SDL_VideoDevice *_this, size_t *num_mime_types) { if (_this->GetTextMimeTypes) { return _this->GetTextMimeTypes(_this, num_mime_types); } else { - static const char *text_mime_types[] = { + static const char *const text_mime_types[] = { "text/plain;charset=utf-8" }; @@ -355,7 +355,7 @@ bool SDL_SetClipboardText(const char *text) { SDL_VideoDevice *_this = SDL_GetVideoDevice(); size_t num_mime_types; - const char **text_mime_types; + const char *const *text_mime_types; if (!_this) { return SDL_UninitializedVideo(); @@ -373,7 +373,7 @@ char *SDL_GetClipboardText(void) { SDL_VideoDevice *_this = SDL_GetVideoDevice(); size_t i, num_mime_types; - const char **text_mime_types; + const char *const *text_mime_types; size_t length; char *text = NULL; @@ -401,7 +401,7 @@ bool SDL_HasClipboardText(void) { SDL_VideoDevice *_this = SDL_GetVideoDevice(); size_t i, num_mime_types; - const char **text_mime_types; + const char *const *text_mime_types; if (!_this) { return SDL_UninitializedVideo(); diff --git a/third-party/SDL/src/video/SDL_clipboard_c.h b/third-party/SDL/src/video/SDL_clipboard_c.h index b77788d828..d0476d1ffa 100644 --- a/third-party/SDL/src/video/SDL_clipboard_c.h +++ b/third-party/SDL/src/video/SDL_clipboard_c.h @@ -39,8 +39,8 @@ extern bool SDL_HasInternalClipboardData(SDL_VideoDevice *_this, const char *mim // General purpose clipboard text callback const void * SDLCALL SDL_ClipboardTextCallback(void *userdata, const char *mime_type, size_t *size); -bool SDL_SaveClipboardMimeTypes(const char **mime_types, size_t num_mime_types); +bool SDL_SaveClipboardMimeTypes(const char *const *mime_types, size_t num_mime_types); void SDL_FreeClipboardMimeTypes(SDL_VideoDevice *_this); -char **SDL_CopyClipboardMimeTypes(const char **clipboard_mime_types, size_t num_mime_types, bool temporary); +char **SDL_CopyClipboardMimeTypes(const char *const *clipboard_mime_types, size_t num_mime_types, bool temporary); #endif // SDL_clipboard_c_h_ diff --git a/third-party/SDL/src/video/SDL_stretch.c b/third-party/SDL/src/video/SDL_stretch.c index d432c0fbbd..073f62c09c 100644 --- a/third-party/SDL/src/video/SDL_stretch.c +++ b/third-party/SDL/src/video/SDL_stretch.c @@ -375,20 +375,6 @@ static bool scale_mat(const Uint32 *src, int src_w, int src_h, int src_pitch, Ui return true; } -#ifdef SDL_NEON_INTRINSICS -#define CAST_uint8x8_t (uint8x8_t) -#define CAST_uint32x2_t (uint32x2_t) -#endif - -#if defined(_MSC_VER) -#ifdef SDL_NEON_INTRINSICS -#undef CAST_uint8x8_t -#undef CAST_uint32x2_t -#define CAST_uint8x8_t -#define CAST_uint32x2_t -#endif -#endif - #ifdef SDL_SSE2_INTRINSICS #if 0 @@ -587,8 +573,8 @@ static SDL_INLINE void INTERPOL_BILINEAR_NEON(const Uint32 *s0, const Uint32 *s1 uint16x8_t d0; uint8x8_t e0; - x_00_01 = CAST_uint8x8_t vld1_u32(s0); // Load 2 pixels - x_10_11 = CAST_uint8x8_t vld1_u32(s1); + x_00_01 = vreinterpret_u8_u32(vld1_u32(s0)); // Load 2 pixels + x_10_11 = vreinterpret_u8_u32(vld1_u32(s1)); /* Interpolated == x0 + frac * (x1 - x0) == x0 * (1 - frac) + x1 * frac */ k0 = vmull_u8(x_00_01, v_frac_h1); /* k0 := x0 * (1 - frac) */ @@ -608,7 +594,7 @@ static SDL_INLINE void INTERPOL_BILINEAR_NEON(const Uint32 *s0, const Uint32 *s1 e0 = vmovn_u16(d0); // Store 1 pixel - *dst = vget_lane_u32(CAST_uint32x2_t e0, 0); + *dst = vget_lane_u32(vreinterpret_u32_u8(e0), 0); } static bool scale_mat_NEON(const Uint32 *src, int src_w, int src_h, int src_pitch, Uint32 *dst, int dst_w, int dst_h, int dst_pitch) @@ -672,14 +658,14 @@ static bool scale_mat_NEON(const Uint32 *src, int src_w, int src_h, int src_pitc s_16_17 = (const Uint32 *)((const Uint8 *)src_h1 + index_w_3); // Interpolation vertical - x_00_01 = CAST_uint8x8_t vld1_u32(s_00_01); // Load 2 pixels - x_02_03 = CAST_uint8x8_t vld1_u32(s_02_03); - x_04_05 = CAST_uint8x8_t vld1_u32(s_04_05); - x_06_07 = CAST_uint8x8_t vld1_u32(s_06_07); - x_10_11 = CAST_uint8x8_t vld1_u32(s_10_11); - x_12_13 = CAST_uint8x8_t vld1_u32(s_12_13); - x_14_15 = CAST_uint8x8_t vld1_u32(s_14_15); - x_16_17 = CAST_uint8x8_t vld1_u32(s_16_17); + x_00_01 = vreinterpret_u8_u32(vld1_u32(s_00_01)); // Load 2 pixels + x_02_03 = vreinterpret_u8_u32(vld1_u32(s_02_03)); + x_04_05 = vreinterpret_u8_u32(vld1_u32(s_04_05)); + x_06_07 = vreinterpret_u8_u32(vld1_u32(s_06_07)); + x_10_11 = vreinterpret_u8_u32(vld1_u32(s_10_11)); + x_12_13 = vreinterpret_u8_u32(vld1_u32(s_12_13)); + x_14_15 = vreinterpret_u8_u32(vld1_u32(s_14_15)); + x_16_17 = vreinterpret_u8_u32(vld1_u32(s_16_17)); /* Interpolated == x0 + frac * (x1 - x0) == x0 * (1 - frac) + x1 * frac */ k0 = vmull_u8(x_00_01, v_frac_h1); /* k0 := x0 * (1 - frac) */ @@ -729,7 +715,7 @@ static bool scale_mat_NEON(const Uint32 *src, int src_w, int src_h, int src_pitc // Narrow again e1 = vmovn_u16(d1); - f0 = vcombine_u32(CAST_uint32x2_t e0, CAST_uint32x2_t e1); + f0 = vcombine_u32(vreinterpret_u32_u8(e0), vreinterpret_u32_u8(e1)); // Store 4 pixels vst1q_u32(dst, f0); @@ -768,10 +754,10 @@ static bool scale_mat_NEON(const Uint32 *src, int src_w, int src_h, int src_pitc s_12_13 = (const Uint32 *)((const Uint8 *)src_h1 + index_w_1); // Interpolation vertical - x_00_01 = CAST_uint8x8_t vld1_u32(s_00_01); // Load 2 pixels - x_02_03 = CAST_uint8x8_t vld1_u32(s_02_03); - x_10_11 = CAST_uint8x8_t vld1_u32(s_10_11); - x_12_13 = CAST_uint8x8_t vld1_u32(s_12_13); + x_00_01 = vreinterpret_u8_u32(vld1_u32(s_00_01)); // Load 2 pixels + x_02_03 = vreinterpret_u8_u32(vld1_u32(s_02_03)); + x_10_11 = vreinterpret_u8_u32(vld1_u32(s_10_11)); + x_12_13 = vreinterpret_u8_u32(vld1_u32(s_12_13)); /* Interpolated == x0 + frac * (x1 - x0) == x0 * (1 - frac) + x1 * frac */ k0 = vmull_u8(x_00_01, v_frac_h1); /* k0 := x0 * (1 - frac) */ @@ -801,7 +787,7 @@ static bool scale_mat_NEON(const Uint32 *src, int src_w, int src_h, int src_pitc e0 = vmovn_u16(d0); // Store 2 pixels - vst1_u32(dst, CAST_uint32x2_t e0); + vst1_u32(dst, vreinterpret_u32_u8(e0)); dst += 2; } diff --git a/third-party/SDL/src/video/SDL_surface.c b/third-party/SDL/src/video/SDL_surface.c index 8164f76fbc..f89103be1e 100644 --- a/third-party/SDL/src/video/SDL_surface.c +++ b/third-party/SDL/src/video/SDL_surface.c @@ -427,6 +427,10 @@ bool SDL_SetSurfacePalette(SDL_Surface *surface, SDL_Palette *palette) return SDL_InvalidParamError("surface"); } + CHECK_PARAM(palette && !SDL_ISPIXELFORMAT_INDEXED(surface->format)) { + return SDL_SetError("Surface doesn't use a palette"); + } + CHECK_PARAM(palette && palette->ncolors > (1 << SDL_BITSPERPIXEL(surface->format))) { return SDL_SetError("Palette doesn't match surface format"); } diff --git a/third-party/SDL/src/video/SDL_sysvideo.h b/third-party/SDL/src/video/SDL_sysvideo.h index e642714350..1fdc5bf527 100644 --- a/third-party/SDL/src/video/SDL_sysvideo.h +++ b/third-party/SDL/src/video/SDL_sysvideo.h @@ -375,7 +375,7 @@ struct SDL_VideoDevice void (*SetTextInputProperties)(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props); // Clipboard - const char **(*GetTextMimeTypes)(SDL_VideoDevice *_this, size_t *num_mime_types); + const char *const *(*GetTextMimeTypes)(SDL_VideoDevice *_this, size_t *num_mime_types); bool (*SetClipboardData)(SDL_VideoDevice *_this); void *(*GetClipboardData)(SDL_VideoDevice *_this, const char *mime_type, size_t *size); bool (*HasClipboardData)(SDL_VideoDevice *_this, const char *mime_type); diff --git a/third-party/SDL/src/video/SDL_video.c b/third-party/SDL/src/video/SDL_video.c index 9f4b70e226..48ed85a20a 100644 --- a/third-party/SDL/src/video/SDL_video.c +++ b/third-party/SDL/src/video/SDL_video.c @@ -422,7 +422,7 @@ static bool SDL_CreateWindowTexture(SDL_VideoDevice *_this, SDL_Window *window, !SDL_ISPIXELFORMAT_10BIT(texture_format) && !SDL_ISPIXELFORMAT_FLOAT(texture_format) && !SDL_ISPIXELFORMAT_INDEXED(texture_format) && - transparent == SDL_ISPIXELFORMAT_ALPHA(texture_format)) { + (!transparent || SDL_ISPIXELFORMAT_ALPHA(texture_format))) { *format = texture_format; break; } @@ -1420,11 +1420,16 @@ bool SDL_GetClosestFullscreenDisplayMode(SDL_DisplayID displayID, int w, int h, continue; } - if (mode->w == closest->w && mode->h == closest->h && - SDL_fabsf(closest->refresh_rate - refresh_rate) < SDL_fabsf(mode->refresh_rate - refresh_rate)) { - /* We already found a mode and the new mode is further from our - * refresh rate target */ - continue; + if (mode->w == closest->w && mode->h == closest->h) { + if (SDL_fabsf(closest->refresh_rate - refresh_rate) < SDL_fabsf(mode->refresh_rate - refresh_rate)) { + /* We already found a mode and the new mode is further from our + * refresh rate target */ + continue; + } + if (SDL_BYTESPERPIXEL(closest->format) > SDL_BYTESPERPIXEL(mode->format)) { + // Prefer the highest color depth + continue; + } } } @@ -1524,9 +1529,13 @@ bool SDL_SetDisplayModeForDisplay(SDL_VideoDisplay *display, SDL_DisplayMode *mo mode = &display->desktop_mode; } + // On RISC OS, it's necessary to switch from the desktop to single-tasking + // fullscreen so that it can handle switching back to the desktop correctly. +#ifndef SDL_PLATFORM_RISCOS if (mode == display->current_mode) { return true; } +#endif // Actually change the display mode if (_this->SetDisplayMode) { @@ -3038,6 +3047,14 @@ bool SDL_SetWindowPosition(SDL_Window *window, int x, int y) window->pending.x = x; window->pending.y = y; + + /* Windows are placed at the coordinates received while in fullscreen after leaving fullscreen. + * Asynchronous backends need special handling in this case. + */ + if (!_this->SyncWindow && (window->flags & SDL_WINDOW_FULLSCREEN)) { + window->floating.x = window->windowed.x = x; + window->floating.y = window->windowed.y = y; + } window->undefined_x = false; window->undefined_y = false; window->last_position_pending = true; @@ -3226,10 +3243,15 @@ bool SDL_SetWindowAspectRatio(SDL_Window *window, float min_aspect, float max_as window->min_aspect = min_aspect; window->max_aspect = max_aspect; + if (_this->SetWindowAspectRatio) { _this->SetWindowAspectRatio(_this, window); } - return SDL_SetWindowSize(window, window->floating.w, window->floating.h); + + // Ensure that window has the correct aspect ratio + int w = window->last_size_pending ? window->pending.w : window->floating.w; + int h = window->last_size_pending ? window->pending.h : window->floating.h; + return SDL_SetWindowSize(window, w, h); } bool SDL_GetWindowAspectRatio(SDL_Window *window, float *min_aspect, float *max_aspect) @@ -3333,8 +3355,6 @@ bool SDL_SetWindowMinimumSize(SDL_Window *window, int min_w, int min_h) // Ensure that window is not smaller than minimal size int w = window->last_size_pending ? window->pending.w : window->floating.w; int h = window->last_size_pending ? window->pending.h : window->floating.h; - w = window->min_w ? SDL_max(w, window->min_w) : w; - h = window->min_h ? SDL_max(h, window->min_h) : h; return SDL_SetWindowSize(window, w, h); } @@ -3375,8 +3395,6 @@ bool SDL_SetWindowMaximumSize(SDL_Window *window, int max_w, int max_h) // Ensure that window is not larger than maximal size int w = window->last_size_pending ? window->pending.w : window->floating.w; int h = window->last_size_pending ? window->pending.h : window->floating.h; - w = window->max_w ? SDL_min(w, window->max_w) : w; - h = window->max_h ? SDL_min(h, window->max_h) : h; return SDL_SetWindowSize(window, w, h); } @@ -5913,7 +5931,7 @@ bool SDL_ScreenKeyboardShown(SDL_Window *window) void SDL_SendScreenKeyboardShown(void) { - if (_this->screen_keyboard_shown) { + if (!_this || _this->screen_keyboard_shown) { return; } @@ -5929,7 +5947,7 @@ void SDL_SendScreenKeyboardShown(void) void SDL_SendScreenKeyboardHidden(void) { - if (!_this->screen_keyboard_shown) { + if (!_this || !_this->screen_keyboard_shown) { return; } diff --git a/third-party/SDL/src/video/cocoa/SDL_cocoawindow.m b/third-party/SDL/src/video/cocoa/SDL_cocoawindow.m index ca6fed4da6..3b8fa7a042 100644 --- a/third-party/SDL/src/video/cocoa/SDL_cocoawindow.m +++ b/third-party/SDL/src/video/cocoa/SDL_cocoawindow.m @@ -1461,7 +1461,6 @@ 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 @@ -2600,7 +2599,7 @@ bool Cocoa_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window *window) BOOL fullscreen = (window->flags & SDL_WINDOW_FULLSCREEN) ? YES : NO; int x, y; - if ([windata.listener isInFullscreenSpaceTransition]) { + if (fullscreen || [windata.listener isInFullscreenSpaceTransition]) { windata.pending_position = YES; return true; } @@ -3025,8 +3024,13 @@ SDL_FullscreenResult Cocoa_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Windo SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_LEAVE_FULLSCREEN, 0, 0); - rect.origin.x = data.was_zoomed ? window->windowed.x : window->floating.x; - rect.origin.y = data.was_zoomed ? window->windowed.y : window->floating.y; + if (data.pending_position) { + rect.origin.x = window->pending.x; + rect.origin.y = window->pending.y; + } else { + 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; diff --git a/third-party/SDL/src/video/directx/SDL_d3d12_xbox_cmacros.h b/third-party/SDL/src/video/directx/SDL_d3d12_xbox_cmacros.h index fc6df35eba..aa9e4ce2b9 100644 --- a/third-party/SDL/src/video/directx/SDL_d3d12_xbox_cmacros.h +++ b/third-party/SDL/src/video/directx/SDL_d3d12_xbox_cmacros.h @@ -691,8 +691,8 @@ ( (This)->Wait(pFence,Value) ) #define ID3D12CommandQueue_GetTimestampFrequency(This,pFrequency) \ ( (This)->GetTimestampFrequency(pFrequency) ) -#define ID3D12CommandQueue_GetClockCalibration(This,pGPUTimestamp,pCpuTimestamp) \ - ( (This)->GetClockCalibration(pGPUTimestamp,pCpuTimestamp) ) +#define ID3D12CommandQueue_GetClockCalibration(This,pGpuTimestamp,pCpuTimestamp) \ + ( (This)->GetClockCalibration(pGpuTimestamp,pCpuTimestamp) ) #define ID3D12Device_QueryInterface(This,riid,ppvObject) \ ( (This)->QueryInterface(riid,ppvObject) ) #define ID3D12Device_AddRef(This) \ diff --git a/third-party/SDL/src/video/directx/d3d12.h b/third-party/SDL/src/video/directx/d3d12.h index eac7fbbf3f..e670c3cd46 100644 --- a/third-party/SDL/src/video/directx/d3d12.h +++ b/third-party/SDL/src/video/directx/d3d12.h @@ -8670,7 +8670,7 @@ EXTERN_C const IID IID_ID3D12CommandQueue; _Out_ UINT64 *pFrequency) = 0; virtual HRESULT STDMETHODCALLTYPE GetClockCalibration( - _Out_ UINT64 *pGPUTimestamp, + _Out_ UINT64 *pGpuTimestamp, _Out_ UINT64 *pCpuTimestamp) = 0; #if defined(_MSC_VER) || !defined(_WIN32) @@ -8802,7 +8802,7 @@ EXTERN_C const IID IID_ID3D12CommandQueue; DECLSPEC_XFGVIRT(ID3D12CommandQueue, GetClockCalibration) HRESULT ( STDMETHODCALLTYPE *GetClockCalibration )( ID3D12CommandQueue * This, - _Out_ UINT64 *pGPUTimestamp, + _Out_ UINT64 *pGpuTimestamp, _Out_ UINT64 *pCpuTimestamp); DECLSPEC_XFGVIRT(ID3D12CommandQueue, GetDesc) @@ -8885,8 +8885,8 @@ EXTERN_C const IID IID_ID3D12CommandQueue; #define ID3D12CommandQueue_GetTimestampFrequency(This,pFrequency) \ ( (This)->lpVtbl -> GetTimestampFrequency(This,pFrequency) ) -#define ID3D12CommandQueue_GetClockCalibration(This,pGPUTimestamp,pCpuTimestamp) \ - ( (This)->lpVtbl -> GetClockCalibration(This,pGPUTimestamp,pCpuTimestamp) ) +#define ID3D12CommandQueue_GetClockCalibration(This,pGpuTimestamp,pCpuTimestamp) \ + ( (This)->lpVtbl -> GetClockCalibration(This,pGpuTimestamp,pCpuTimestamp) ) #if !defined(_WIN32) #define ID3D12CommandQueue_GetDesc(This) \ @@ -29176,7 +29176,7 @@ DEFINE_ENUM_FLAG_OPERATORS( D3D12_DEVICE_FLAGS ) typedef struct D3D12_DEVICE_CONFIGURATION_DESC { D3D12_DEVICE_FLAGS Flags; - UINT GPUBasedValidationFlags; + UINT GpuBasedValidationFlags; UINT SDKVersion; UINT NumEnabledExperimentalFeatures; } D3D12_DEVICE_CONFIGURATION_DESC; diff --git a/third-party/SDL/src/video/emscripten/SDL_emscriptenevents.c b/third-party/SDL/src/video/emscripten/SDL_emscriptenevents.c index 6326d6967e..b62d69ba04 100644 --- a/third-party/SDL/src/video/emscripten/SDL_emscriptenevents.c +++ b/third-party/SDL/src/video/emscripten/SDL_emscriptenevents.c @@ -1327,11 +1327,33 @@ void Emscripten_RegisterEventHandlers(SDL_WindowData *data) keyElement = Emscripten_GetKeyboardTargetElement(data->keyboard_element); if (keyElement) { + // Emscripten's HTML5 helpers do not deduplicate `addEventListener` calls: + // see `registerOrRemoveHandler` in `emscripten/src/lib/libhtml5.js`. + // + // If a previous SDL window already registered keyboard handlers on the same + // target, the new registration would *stack* a second listener, causing every + // browser keydown to fire `Emscripten_HandleKey` twice. + // + // The duplicate calls then produce two `SDL_EVENT_KEY_DOWN` events per physical + // keypress (the second one with `repeat=true`, due to the keystate-based repeat + // detection in `SDL_SendKeyboardKeyInternal`). + // + // We must clear any prior handler on this target before installing ours: + emscripten_set_keydown_callback(keyElement, NULL, 0, NULL); + emscripten_set_keyup_callback(keyElement, NULL, 0, NULL); + emscripten_set_keypress_callback(keyElement, NULL, 0, NULL); + MAIN_THREAD_EM_ASM_INT({ var data = $0; // our keymod state can get confused in various ways (changed capslock when browser didn't have focus, etc), and you can't query the current // state from the DOM, outside of a keyboard event, so catch keypresses globally and reset mod state if it's unexpectedly wrong. Best we can do. // Note that this thing _only_ adjusts the lock keys if necessary; the real SDL keypress handling happens elsewhere. + + // Remove any prior listener first -- `addEventListener` does not deduplicate either. + if (document.sdlEventHandlerLockKeysCheck) { + document.removeEventListener("keydown", document.sdlEventHandlerLockKeysCheck); + } + document.sdlEventHandlerLockKeysCheck = function(event) { // don't try to adjust the state on the actual lock key presses; the normal key handler will catch that and adjust. if ((event.key != "CapsLock") && (event.key != "NumLock") && (event.key != "ScrollLock")) diff --git a/third-party/SDL/src/video/kmsdrm/SDL_kmsdrmvideo.c b/third-party/SDL/src/video/kmsdrm/SDL_kmsdrmvideo.c index f1366e02f1..276f0d494e 100644 --- a/third-party/SDL/src/video/kmsdrm/SDL_kmsdrmvideo.c +++ b/third-party/SDL/src/video/kmsdrm/SDL_kmsdrmvideo.c @@ -1696,6 +1696,22 @@ static void KMSDRM_DestroySurfaces(SDL_VideoDevice *_this, SDL_Window *window) SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "Could not restore CRTC"); } + /***************************/ + // Destroy the GBM buffers + /***************************/ + + if (windata->bo) { + if (windata->bo != windata->next_bo) { + KMSDRM_gbm_surface_release_buffer(windata->gs, windata->bo); + } + windata->bo = NULL; + } + + if (windata->next_bo) { + KMSDRM_gbm_surface_release_buffer(windata->gs, windata->next_bo); + windata->next_bo = NULL; + } + /***************************/ // Destroy the EGL surface /***************************/ @@ -1707,20 +1723,6 @@ static void KMSDRM_DestroySurfaces(SDL_VideoDevice *_this, SDL_Window *window) windata->egl_surface = EGL_NO_SURFACE; } - /***************************/ - // Destroy the GBM buffers - /***************************/ - - if (windata->bo) { - KMSDRM_gbm_surface_release_buffer(windata->gs, windata->bo); - windata->bo = NULL; - } - - if (windata->next_bo) { - KMSDRM_gbm_surface_release_buffer(windata->gs, windata->next_bo); - windata->next_bo = NULL; - } - /***************************/ // Destroy the GBM surface /***************************/ diff --git a/third-party/SDL/src/video/n3ds/SDL_n3dsswkb.c b/third-party/SDL/src/video/n3ds/SDL_n3dsswkb.c index 16e6c5f27b..dd54a4c9e4 100644 --- a/third-party/SDL/src/video/n3ds/SDL_n3dsswkb.c +++ b/third-party/SDL/src/video/n3ds/SDL_n3dsswkb.c @@ -28,7 +28,7 @@ #include "SDL_n3dsswkb.h" static SwkbdState sw_keyboard; -const static size_t BUFFER_SIZE = 256; +static const size_t BUFFER_SIZE = 256; void N3DS_SwkbInit(void) { diff --git a/third-party/SDL/src/video/openvr/SDL_openvrvideo.c b/third-party/SDL/src/video/openvr/SDL_openvrvideo.c index dae1fc3d1a..1cb62baa76 100644 --- a/third-party/SDL/src/video/openvr/SDL_openvrvideo.c +++ b/third-party/SDL/src/video/openvr/SDL_openvrvideo.c @@ -74,7 +74,7 @@ SDL_ELF_NOTE_DLOPEN( // For access to functions that don't get the video data context. SDL_VideoData * global_openvr_driver; -static void InitializeMouseFunctions(); +static void InitializeMouseFunctions(void); struct SDL_CursorData { @@ -92,9 +92,9 @@ static void (APIENTRY *ov_glRenderbufferStorage)(GLenum target, GLenum internalf static void (APIENTRY *ov_glFramebufferRenderbuffer)(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); static void (APIENTRY *ov_glFramebufferTexture2D)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); static GLenum (APIENTRY *ov_glCheckNamedFramebufferStatus)(GLuint framebuffer, GLenum target); -static GLenum (APIENTRY *ov_glGetError)(); -static void (APIENTRY *ov_glFlush)(); -static void (APIENTRY *ov_glFinish)(); +static GLenum (APIENTRY *ov_glGetError)(void); +static void (APIENTRY *ov_glFlush)(void); +static void (APIENTRY *ov_glFinish)(void); static void (APIENTRY *ov_glGenTextures)(GLsizei n, GLuint *textures); static void (APIENTRY *ov_glDeleteTextures)(GLsizei n, GLuint *textures); static void (APIENTRY *ov_glTexParameterf)(GLenum target, GLenum pname, GLfloat param); @@ -1038,7 +1038,7 @@ static SDL_FunctionPointer OVR_EGL_GetProcAddress(SDL_VideoDevice *_this, const } static void OVR_EGL_UnloadLibrary(SDL_VideoDevice *_this) { - return SDL_EGL_UnloadLibrary(_this); + SDL_EGL_UnloadLibrary(_this); } static SDL_GLContext OVR_EGL_CreateContext(SDL_VideoDevice *_this, SDL_Window * window) { @@ -1431,7 +1431,7 @@ static bool OPENVR_ShowMessageBox(SDL_VideoDevice *_this,const SDL_MessageBoxDat return true; } -static void InitializeMouseFunctions() +static void InitializeMouseFunctions(void) { SDL_Mouse *mouse = SDL_GetMouse(); mouse->CreateCursor = OPENVR_CreateCursor; @@ -1473,7 +1473,7 @@ static void OPENVR_PumpEvents(SDL_VideoDevice *_this) break; case EVREventType_VREvent_OverlayClosed: case EVREventType_VREvent_Quit: - SDL_Quit(); + SDL_SendQuit(); break; } } @@ -1667,5 +1667,5 @@ VideoBootStrap OPENVR_bootstrap = { "openvr", "SDL OpenVR video driver", OPENVR_CreateDevice, NULL, false }; -#endif // SDL_VIDEO_DRIVER_WINDOWS +#endif // SDL_VIDEO_DRIVER_OPENVR diff --git a/third-party/SDL/src/video/riscos/SDL_riscosmodes.c b/third-party/SDL/src/video/riscos/SDL_riscosmodes.c index 9dda0e5ac6..0da0cc8621 100644 --- a/third-party/SDL/src/video/riscos/SDL_riscosmodes.c +++ b/third-party/SDL/src/video/riscos/SDL_riscosmodes.c @@ -96,7 +96,7 @@ static size_t measure_mode_block(const int *block) return blockSize * 4; } -static bool read_mode_variable(int *block, int var) +static int read_mode_variable(int *block, int var) { _kernel_swi_regs regs; regs.r[0] = (int)block; diff --git a/third-party/SDL/src/video/sdlgenblit.pl b/third-party/SDL/src/video/sdlgenblit.pl old mode 100644 new mode 100755 diff --git a/third-party/SDL/src/video/stb_image.h b/third-party/SDL/src/video/stb_image.h index e0f3d891f3..fed28a0a9e 100644 --- a/third-party/SDL/src/video/stb_image.h +++ b/third-party/SDL/src/video/stb_image.h @@ -791,11 +791,13 @@ typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; #endif #if !defined(STBI_NO_SIMD) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) +#ifdef SDL_SSE2_INTRINSICS /* SDL change */ #define STBI_SSE2 #include #ifdef _MSC_VER +#if 0 /* SDL change (unused due to using SDL_HasSSE2) */ #if _MSC_VER >= 1400 // not VC6 #include // __cpuid static int stbi__cpuid3(void) @@ -816,14 +818,14 @@ static int stbi__cpuid3(void) return res; } #endif +#endif /* SDL change */ #define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name #if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) static int stbi__sse2_available(void) { - int info3 = stbi__cpuid3(); - return ((info3 >> 26) & 1) != 0; + return SDL_HasSSE2(); /* SDL change */ } #endif @@ -836,11 +838,12 @@ static int stbi__sse2_available(void) // If we're even attempting to compile this on GCC/Clang, that means // -msse2 is on, which means the compiler is allowed to use SSE2 // instructions at will, and so are we. - return 1; + return SDL_HasSSE2(); /* SDL change */ } #endif #endif +#endif /* SDL change (SDL_SSE2_INTRINSICS) */ #endif // ARM NEON diff --git a/third-party/SDL/src/video/uikit/SDL_uikitappdelegate.m b/third-party/SDL/src/video/uikit/SDL_uikitappdelegate.m index 47de97759c..2bc65e245a 100644 --- a/third-party/SDL/src/video/uikit/SDL_uikitappdelegate.m +++ b/third-party/SDL/src/video/uikit/SDL_uikitappdelegate.m @@ -343,6 +343,7 @@ API_AVAILABLE(ios(13.0)) @implementation SDLUIKitSceneDelegate { UIWindow *launchWindow; + NSMutableArray *launchURLs; } + (NSString *)getSceneDelegateClassName @@ -402,15 +403,16 @@ API_AVAILABLE(ios(13.0)) // Set working directory to resource path [[NSFileManager defaultManager] changeCurrentDirectoryPath:[bundle resourcePath]]; - // Handle any connection options (like opening URLs) + launchURLs = [[NSMutableArray alloc] init]; + for (NSUserActivity *activity in connectionOptions.userActivities) { if (activity.webpageURL) { - [self handleURL:activity.webpageURL]; + [launchURLs addObject:activity.webpageURL]; } } for (UIOpenURLContext *urlContext in connectionOptions.URLContexts) { - [self handleURL:urlContext.URL]; + [launchURLs addObject:urlContext.URL]; } SDL_SetMainReady(); @@ -479,6 +481,7 @@ API_AVAILABLE(ios(13.0)) - (void)postFinishLaunch { [self performSelector:@selector(hideLaunchScreen) withObject:nil afterDelay:0.0]; + [self performSelector:@selector(processLaunchURLs) withObject:nil afterDelay:0.0]; SDL_SetiOSEventPump(true); exit_status = SDL_CallMainFunction(forward_argc, forward_argv, forward_main); @@ -490,6 +493,14 @@ API_AVAILABLE(ios(13.0)) } } +- (void)processLaunchURLs +{ + for (NSURL *url in launchURLs) { + [self handleURL:url]; + } + launchURLs = nil; +} + - (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options API_AVAILABLE(ios(13.0)) { // This doesn't appear to be called, but it needs to be implemented to signal that we support the UIScene life cycle diff --git a/third-party/SDL/src/video/uikit/SDL_uikitpen.m b/third-party/SDL/src/video/uikit/SDL_uikitpen.m index f2975c4e31..ba4c629f1b 100644 --- a/third-party/SDL/src/video/uikit/SDL_uikitpen.m +++ b/third-party/SDL/src/video/uikit/SDL_uikitpen.m @@ -124,6 +124,11 @@ static void UIKit_HandlePenAxes(SDL_Window *window, NSTimeInterval nstimestamp, // rotation is in radians, and only available on a later iOS. const float rotation = rollAngle * radians_to_degrees; // !!! FIXME: this might need adjustment, I don't have a pencil that supports it. + if (force == 0.0f && (SDL_GetPenStatus(penId, NULL, 0) & SDL_PEN_INPUT_DOWN)) { + // The first hover as the pen is being released has a stale position, so ignore it + return; + } + SDL_SendPenMotion(timestamp, penId, window, point->x, point->y); SDL_SendPenAxis(timestamp, penId, window, SDL_PEN_AXIS_PRESSURE, pressure); SDL_SendPenAxis(timestamp, penId, window, SDL_PEN_AXIS_XTILT, xtilt); diff --git a/third-party/SDL/src/video/uikit/SDL_uikitviewcontroller.h b/third-party/SDL/src/video/uikit/SDL_uikitviewcontroller.h index f42f586d89..a758de7ff5 100644 --- a/third-party/SDL/src/video/uikit/SDL_uikitviewcontroller.h +++ b/third-party/SDL/src/video/uikit/SDL_uikitviewcontroller.h @@ -75,7 +75,9 @@ - (void)deinitKeyboard; - (void)keyboardWillShow:(NSNotification *)notification; +- (void)keyboardDidShow:(NSNotification *)notification; - (void)keyboardWillHide:(NSNotification *)notification; +- (void)keyboardDidHide:(NSNotification *)notification; - (void)updateKeyboard; diff --git a/third-party/SDL/src/video/uikit/SDL_uikitviewcontroller.m b/third-party/SDL/src/video/uikit/SDL_uikitviewcontroller.m index ffb822d919..e9b48c2e01 100644 --- a/third-party/SDL/src/video/uikit/SDL_uikitviewcontroller.m +++ b/third-party/SDL/src/video/uikit/SDL_uikitviewcontroller.m @@ -75,7 +75,6 @@ static void SDLCALL SDL_HideHomeIndicatorHintChanged(void *userdata, const char #ifdef SDL_IPHONE_KEYBOARD SDLUITextField *textField; - BOOL hidingKeyboard; BOOL rotatingOrientation; NSString *committedText; NSString *obligateForBackspace; @@ -92,7 +91,6 @@ static void SDLCALL SDL_HideHomeIndicatorHintChanged(void *userdata, const char #ifdef SDL_IPHONE_KEYBOARD [self initKeyboard]; - hidingKeyboard = NO; rotatingOrientation = NO; #endif @@ -289,6 +287,10 @@ 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 @@ -373,6 +375,9 @@ 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]; @@ -543,49 +548,25 @@ static void SDLCALL SDL_HideHomeIndicatorHintChanged(void *userdata, const char [self setKeyboardHeight:(int)kbrect.size.height]; #endif +} - /* 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 +{ + SDL_SendScreenKeyboardShown(); } - (void)keyboardWillHide:(NSNotification *)notification { - hidingKeyboard = YES; [self setKeyboardHeight:0]; - - /* 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); - } } - (void)keyboardDidHide:(NSNotification *)notification { - hidingKeyboard = NO; + SDL_SendScreenKeyboardHidden(); } - (void)textFieldTextDidChange:(NSNotification *)notification { - // When opening a password manager overlay to select a password and have it auto-filled, - // text input becomes stopped as a result of the keyboard being hidden or the text field losing focus. - // As a workaround, ensure text input is activated on any changes to the text field. - bool startTextInputMomentarily = !SDL_TextInputActive(window); - - if (startTextInputMomentarily) - SDL_StartTextInput(window); - if (textField.markedTextRange == nil) { if (isOTPMode && labs((NSInteger)textField.text.length - (NSInteger)committedText.length) != 1) { return; @@ -600,7 +581,7 @@ static void SDLCALL SDL_HideHomeIndicatorHintChanged(void *userdata, const char break; } } - if (matchLength < committedText.length) { + if (matchLength < committedText.length && !SDL_HasKeyboard()) { size_t deleteLength = SDL_utf8strlen([[committedText substringFromIndex:matchLength] UTF8String]); while (deleteLength > 0) { // Send distinct down and up events for each backspace action @@ -624,9 +605,6 @@ static void SDLCALL SDL_HideHomeIndicatorHintChanged(void *userdata, const char } committedText = textField.text; } - - if (startTextInputMomentarily) - SDL_StopTextInput(window); } - (void)updateKeyboard diff --git a/third-party/SDL/src/video/vita/SDL_vitavideo.h b/third-party/SDL/src/video/vita/SDL_vitavideo.h index 18ad31689e..4657f14ea9 100644 --- a/third-party/SDL/src/video/vita/SDL_vitavideo.h +++ b/third-party/SDL/src/video/vita/SDL_vitavideo.h @@ -117,4 +117,4 @@ extern void VITA_HideScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window); extern void VITA_PumpEvents(SDL_VideoDevice *_this); -#endif // SDL_pspvideo_h +#endif // SDL_vitavideo_h diff --git a/third-party/SDL/src/video/wayland/SDL_waylandclipboard.c b/third-party/SDL/src/video/wayland/SDL_waylandclipboard.c index 9e0dc79312..2f3d42319f 100644 --- a/third-party/SDL/src/video/wayland/SDL_waylandclipboard.c +++ b/third-party/SDL/src/video/wayland/SDL_waylandclipboard.c @@ -96,7 +96,7 @@ bool Wayland_HasClipboardData(SDL_VideoDevice *_this, const char *mime_type) return result; } -static const char *text_mime_types[] = { +static const char *const text_mime_types[] = { TEXT_MIME, "text/plain", "TEXT", @@ -104,7 +104,7 @@ static const char *text_mime_types[] = { "STRING" }; -const char **Wayland_GetTextMimeTypes(SDL_VideoDevice *_this, size_t *num_mime_types) +const char *const *Wayland_GetTextMimeTypes(SDL_VideoDevice *_this, size_t *num_mime_types) { *num_mime_types = SDL_arraysize(text_mime_types); return text_mime_types; @@ -185,7 +185,7 @@ bool Wayland_HasPrimarySelectionText(SDL_VideoDevice *_this) result = true; } else { size_t mime_count = 0; - const char **mime_types = Wayland_GetTextMimeTypes(_this, &mime_count); + const char *const *mime_types = Wayland_GetTextMimeTypes(_this, &mime_count); for (size_t i = 0; i < mime_count; i++) { if (Wayland_primary_selection_offer_has_mime(primary_selection_device->selection_offer, mime_types[i])) { result = true; diff --git a/third-party/SDL/src/video/wayland/SDL_waylandclipboard.h b/third-party/SDL/src/video/wayland/SDL_waylandclipboard.h index e850345c20..b57636461e 100644 --- a/third-party/SDL/src/video/wayland/SDL_waylandclipboard.h +++ b/third-party/SDL/src/video/wayland/SDL_waylandclipboard.h @@ -23,7 +23,7 @@ #ifndef SDL_waylandclipboard_h_ #define SDL_waylandclipboard_h_ -extern const char **Wayland_GetTextMimeTypes(SDL_VideoDevice *_this, size_t *num_mime_types); +extern const char *const *Wayland_GetTextMimeTypes(SDL_VideoDevice *_this, size_t *num_mime_types); extern bool Wayland_SetClipboardData(SDL_VideoDevice *_this); extern void *Wayland_GetClipboardData(SDL_VideoDevice *_this, const char *mime_type, size_t *length); extern bool Wayland_HasClipboardData(SDL_VideoDevice *_this, const char *mime_type); diff --git a/third-party/SDL/src/video/wayland/SDL_waylanddatamanager.c b/third-party/SDL/src/video/wayland/SDL_waylanddatamanager.c index e4a040a817..4501efaa1a 100644 --- a/third-party/SDL/src/video/wayland/SDL_waylanddatamanager.c +++ b/third-party/SDL/src/video/wayland/SDL_waylanddatamanager.c @@ -183,10 +183,14 @@ static SDL_MimeDataList *mime_data_list_find(struct wl_list *list, { SDL_MimeDataList *found = NULL; - SDL_MimeDataList *mime_list = NULL; - wl_list_for_each (mime_list, list, link) { - if (SDL_strcmp(mime_list->mime_type, mime_type) == 0) { - found = mime_list; + SDL_MimeDataList *item = NULL; + wl_list_for_each (item, list, link) { + if (!item->mime_type) { + continue; + } + + if (SDL_strcmp(item->mime_type, mime_type) == 0) { + found = item; break; } } @@ -470,6 +474,10 @@ void Wayland_data_offer_notify_from_mimes(SDL_WaylandDataOffer *offer, bool chec // Do a first pass to compute allocation size. SDL_MimeDataList *item = NULL; wl_list_for_each(item, &offer->mimes, link) { + if (!item->mime_type) { + continue; + } + // If origin metadata is found, queue a check and wait for confirmation that this offer isn't recursive. if (check_origin && SDL_strcmp(item->mime_type, SDL_DATA_ORIGIN_MIME) == 0) { Wayland_data_offer_check_source(offer, item->mime_type); @@ -493,6 +501,10 @@ void Wayland_data_offer_notify_from_mimes(SDL_WaylandDataOffer *offer, bool chec item = NULL; int i = 0; wl_list_for_each(item, &offer->mimes, link) { + if (!item->mime_type) { + continue; + } + new_mime_types[i] = strPtr; strPtr = stpcpy(strPtr, item->mime_type) + 1; i++; @@ -705,7 +717,7 @@ bool Wayland_data_device_set_selection(SDL_WaylandDataDevice *data_device, bool Wayland_primary_selection_device_set_selection(SDL_WaylandPrimarySelectionDevice *primary_selection_device, SDL_WaylandPrimarySelectionSource *source, - const char **mime_types, + const char *const *mime_types, size_t mime_count) { bool result = true; diff --git a/third-party/SDL/src/video/wayland/SDL_waylanddatamanager.h b/third-party/SDL/src/video/wayland/SDL_waylanddatamanager.h index d7a863221b..1ac0dbfc2f 100644 --- a/third-party/SDL/src/video/wayland/SDL_waylanddatamanager.h +++ b/third-party/SDL/src/video/wayland/SDL_waylanddatamanager.h @@ -167,7 +167,7 @@ extern bool Wayland_data_device_set_selection(SDL_WaylandDataDevice *device, size_t mime_count); extern bool Wayland_primary_selection_device_set_selection(SDL_WaylandPrimarySelectionDevice *device, SDL_WaylandPrimarySelectionSource *source, - const char **mime_types, + const char *const *mime_types, size_t mime_count); extern void Wayland_data_device_set_serial(SDL_WaylandDataDevice *device, uint32_t serial); diff --git a/third-party/SDL/src/video/wayland/SDL_waylandevents.c b/third-party/SDL/src/video/wayland/SDL_waylandevents.c index eb3083187d..231fd70dbb 100644 --- a/third-party/SDL/src/video/wayland/SDL_waylandevents.c +++ b/third-party/SDL/src/video/wayland/SDL_waylandevents.c @@ -495,9 +495,7 @@ void Wayland_SendWakeupEvent(SDL_VideoDevice *_this, SDL_Window *window) { SDL_VideoData *d = _this->internal; - /* Queue a sync event to unblock the event queue fd if it's empty and being waited on. - * TODO: Maybe use a pipe to avoid the compositor roundtrip? - */ + // Queue a sync event to unblock the main event queue if it's being waited on. struct wl_callback *cb = wl_display_sync(d->display); wl_callback_add_listener(cb, &sync_listener, NULL); WAYLAND_wl_display_flush(d->display); @@ -1171,9 +1169,7 @@ static void pointer_handle_axis_relative_direction(void *data, struct wl_pointer uint32_t axis, uint32_t axis_relative_direction) { SDL_WaylandSeat *seat = data; - if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL) { - return; - } + switch (axis_relative_direction) { case WL_POINTER_AXIS_RELATIVE_DIRECTION_IDENTICAL: seat->pointer.pending_frame.axis.direction = SDL_MOUSEWHEEL_NORMAL; @@ -2754,15 +2750,18 @@ static void data_device_handle_enter(void *data, struct wl_data_device *wl_data_ wl_fixed_t x, wl_fixed_t y, struct wl_data_offer *id) { SDL_WaylandDataDevice *data_device = data; + SDL_WindowData *window = surface ? Wayland_GetWindowDataForOwnedSurface(surface) : NULL; data_device->has_mime_file = false; data_device->has_mime_text = false; - uint32_t dnd_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; data_device->drag_serial = serial; + // Save the drag offer so it can be freed later. if (id) { data_device->drag_offer = wl_data_offer_get_user_data(id); + } + if (data_device->drag_offer && window && window->accepts_drag_and_drop) { // TODO: SDL Support more mime types #ifdef SDL_USE_LIBDBUS if (Wayland_data_offer_has_mime(data_device->drag_offer, FILE_PORTAL_MIME)) { @@ -2778,7 +2777,7 @@ static void data_device_handle_enter(void *data, struct wl_data_device *wl_data_ } size_t mime_count = 0; - const char **text_mime_types = Wayland_GetTextMimeTypes(SDL_GetVideoDevice(), &mime_count); + const char *const *text_mime_types = Wayland_GetTextMimeTypes(SDL_GetVideoDevice(), &mime_count); for (size_t i = 0; i < mime_count; ++i) { if (Wayland_data_offer_has_mime(data_device->drag_offer, text_mime_types[i])) { data_device->has_mime_text = true; @@ -2788,48 +2787,46 @@ static void data_device_handle_enter(void *data, struct wl_data_device *wl_data_ } } - // SDL only supports "copy" style drag and drop if (data_device->has_mime_file || data_device->has_mime_text) { - dnd_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; - } else { - // drag_mime is NULL this will decline the offer - wl_data_offer_accept(id, serial, NULL); - } - if (wl_data_offer_get_version(data_device->drag_offer->offer) >= - WL_DATA_OFFER_SET_ACTIONS_SINCE_VERSION) { - wl_data_offer_set_actions(data_device->drag_offer->offer, - dnd_action, dnd_action); - } - - // find the current window - if (surface) { - SDL_WindowData *window = Wayland_GetWindowDataForOwnedSurface(surface); - if (window) { - data_device->dnd_window = window->sdlwindow; - const float dx = (float)wl_fixed_to_double(x); - const float dy = (float)wl_fixed_to_double(y); - SDL_SendDropPosition(data_device->dnd_window, dx, dy); - SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, - ". In wl_data_device_listener . data_device_handle_enter on data_offer 0x%08x at %d x %d into window %d for serial %d", - WAYLAND_wl_proxy_get_id((struct wl_proxy *)id), - wl_fixed_to_int(x), wl_fixed_to_int(y), SDL_GetWindowID(data_device->dnd_window), serial); - } else { - data_device->dnd_window = NULL; - SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, - ". In wl_data_device_listener . data_device_handle_enter on data_offer 0x%08x at %d x %d for serial %d", - WAYLAND_wl_proxy_get_id((struct wl_proxy *)id), - wl_fixed_to_int(x), wl_fixed_to_int(y), serial); + // SDL only supports "copy" style drag and drop + if (wl_data_offer_get_version(data_device->drag_offer->offer) >= WL_DATA_OFFER_SET_ACTIONS_SINCE_VERSION) { + wl_data_offer_set_actions(data_device->drag_offer->offer, WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY, WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY); } + + // Set the destination window and send the initial position. + data_device->dnd_window = window->sdlwindow; + const float dx = (float)(wl_fixed_to_double(x) * data_device->dnd_window->internal->pointer_scale.x); + const float dy = (float)(wl_fixed_to_double(y) * data_device->dnd_window->internal->pointer_scale.y); + SDL_SendDropPosition(data_device->dnd_window, dx, dy); + SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, + ". In wl_data_device_listener . data_device_handle_enter on data_offer 0x%08x at %d x %d into window %d for serial %d", + WAYLAND_wl_proxy_get_id((struct wl_proxy *)id), + wl_fixed_to_int(x), wl_fixed_to_int(y), SDL_GetWindowID(data_device->dnd_window), serial); } else { + // Decline the offer. + wl_data_offer_accept(id, serial, NULL); + if (wl_data_offer_get_version(data_device->drag_offer->offer) >= WL_DATA_OFFER_SET_ACTIONS_SINCE_VERSION) { + wl_data_offer_set_actions(data_device->drag_offer->offer, WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE, WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE); + } + SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, ". In wl_data_device_listener . data_device_handle_enter on data_offer 0x%08x at %d x %d for serial %d", - WAYLAND_wl_proxy_get_id((struct wl_proxy *)id), - wl_fixed_to_int(x), wl_fixed_to_int(y), serial); + WAYLAND_wl_proxy_get_id((struct wl_proxy *)id), wl_fixed_to_int(x), wl_fixed_to_int(y), serial); } } else { + data_device->dnd_window = NULL; + + // Decline the offer. + if (id) { + wl_data_offer_accept(id, serial, NULL); + if (wl_data_offer_get_version(data_device->drag_offer->offer) >= WL_DATA_OFFER_SET_ACTIONS_SINCE_VERSION) { + wl_data_offer_set_actions(data_device->drag_offer->offer, WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE, WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE); + } + } SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, ". In wl_data_device_listener . data_device_handle_enter on data_offer 0x%08x at %d x %d for serial %d", - -1, wl_fixed_to_int(x), wl_fixed_to_int(y), serial); + id ? WAYLAND_wl_proxy_get_id((struct wl_proxy *)id) : -1, + wl_fixed_to_int(x), wl_fixed_to_int(y), serial); } } @@ -2867,8 +2864,8 @@ static void data_device_handle_motion(void *data, struct wl_data_device *wl_data SDL_WaylandDataDevice *data_device = data; if (data_device->drag_offer && data_device->dnd_window && (data_device->has_mime_file || data_device->has_mime_text)) { - const float dx = (float)wl_fixed_to_double(x); - const float dy = (float)wl_fixed_to_double(y); + const float dx = (float)(wl_fixed_to_double(x) * data_device->dnd_window->internal->pointer_scale.x); + const float dy = (float)(wl_fixed_to_double(y) * data_device->dnd_window->internal->pointer_scale.y); /* XXX: Send the filename here if the event system ever starts passing it though. * Any future implementation should cache the filenames, as otherwise this could diff --git a/third-party/SDL/src/video/wayland/SDL_waylandkeyboard.c b/third-party/SDL/src/video/wayland/SDL_waylandkeyboard.c index 4478b2b9e3..ac0558877d 100644 --- a/third-party/SDL/src/video/wayland/SDL_waylandkeyboard.c +++ b/third-party/SDL/src/video/wayland/SDL_waylandkeyboard.c @@ -183,11 +183,12 @@ bool Wayland_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window, SDL_Prop Wayland_SeatUpdateTextInput(seat); } } - - return true; } - return SDL_SetError("wayland: cannot enable text input; compositor lacks support for the required zwp_text_input_v3 protocol"); + /* Always return true, even if the text input protocol isn't supported, as basic + * text can still be obtained from keysyms and the composition system. + */ + return true; } bool Wayland_StopTextInput(SDL_VideoDevice *_this, SDL_Window *window) diff --git a/third-party/SDL/src/video/wayland/SDL_waylandmouse.c b/third-party/SDL/src/video/wayland/SDL_waylandmouse.c index e367abc384..3550a4fcdd 100644 --- a/third-party/SDL/src/video/wayland/SDL_waylandmouse.c +++ b/third-party/SDL/src/video/wayland/SDL_waylandmouse.c @@ -1104,13 +1104,27 @@ static void Wayland_CursorStateSetCursor(SDL_WaylandCursorState *state, const Wa dst_height = dst_width; } else { /* If viewports aren't available, the scale is always 1.0. - * The dimensions are scaled by the pointer scale, so custom cursors will be scaled relative to the window size. + * + * If the pointer scale values are 1.0, the preferred backing buffer scale is the window scale. + * + * If the pointer is scaled, the dimensions are scaled by the pointer scale, so custom cursors will be scaled + * relative to the viewport size. */ - state->scale = viddata->viewporter && focus ? SDL_min(focus->pointer_scale.x, focus->pointer_scale.y) : 1.0; - dst_width = SDL_max((int)SDL_lround((double)cursor_data->cursor_data.custom.width / state->scale), 1); - dst_height = SDL_max((int)SDL_lround((double)cursor_data->cursor_data.custom.height / state->scale), 1); - hot_x = (int)SDL_lround((double)cursor_data->cursor_data.custom.hot_x / state->scale); - hot_y = (int)SDL_lround((double)cursor_data->cursor_data.custom.hot_y / state->scale); + if (!focus || (focus->pointer_scale.x == 1.0 && focus->pointer_scale.y == 1.0)) { + state->scale = viddata->viewporter && focus ? focus->scale_factor : 1.0; + dst_width = cursor_data->cursor_data.custom.width; + dst_height = cursor_data->cursor_data.custom.height; + hot_x = cursor_data->cursor_data.custom.hot_x; + hot_y = cursor_data->cursor_data.custom.hot_y; + } else { + // The preferred buffer scale is the inverse of the pointer scale. + state->scale = 1.0 / SDL_min(focus->pointer_scale.x, focus->pointer_scale.y); + dst_width = SDL_max((int)SDL_lround((double)cursor_data->cursor_data.custom.width / focus->pointer_scale.x), 1); + dst_height = SDL_max((int)SDL_lround((double)cursor_data->cursor_data.custom.height / focus->pointer_scale.y), 1); + hot_x = (int)SDL_lround((double)cursor_data->cursor_data.custom.hot_x / focus->pointer_scale.x); + hot_y = (int)SDL_lround((double)cursor_data->cursor_data.custom.hot_y / focus->pointer_scale.y); + } + } state->current_cursor = cursor_data; diff --git a/third-party/SDL/src/video/wayland/SDL_waylandutil.c b/third-party/SDL/src/video/wayland/SDL_waylandutil.c new file mode 100644 index 0000000000..d6461cb071 --- /dev/null +++ b/third-party/SDL/src/video/wayland/SDL_waylandutil.c @@ -0,0 +1,134 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2026 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_WAYLAND + +#include "SDL_waylandevents_c.h" +#include "SDL_waylandutil.h" +#include "xdg-activation-v1-client-protocol.h" + +#define WAYLAND_HANDLE_PREFIX "wayland:" + +typedef struct Wayland_ActivationParams +{ + char **token; + bool done; +} Wayland_ActivationParams; + +static void handle_xdg_activation_done(void *data, struct xdg_activation_token_v1 *xdg_activation_token_v1, const char *token) +{ + Wayland_ActivationParams *activation_params = (Wayland_ActivationParams *)data; + *activation_params->token = SDL_strdup(token); + activation_params->done = true; + + xdg_activation_token_v1_destroy(xdg_activation_token_v1); +} + +static const struct xdg_activation_token_v1_listener xdg_activation_listener = { + handle_xdg_activation_done +}; + +bool Wayland_GetActivationTokenForExport(SDL_VideoDevice *_this, char **token, char **window_id) +{ + if (!_this || !token) { + return false; + } + + SDL_VideoData *viddata = _this->internal; + + SDL_WaylandSeat *seat = viddata->last_implicit_grab_seat; + SDL_WindowData *focus = NULL; + + if (seat) { + focus = seat->keyboard.focus; + if (!focus) { + focus = seat->pointer.focus; + } + } + + const char *xdg_activation_token = SDL_getenv("XDG_ACTIVATION_TOKEN"); + if (xdg_activation_token) { + *token = SDL_strdup(xdg_activation_token); + if (!*token) { + return false; + } + + // Unset the envvar after claiming the token. + SDL_unsetenv_unsafe("XDG_ACTIVATION_TOKEN"); + } else if (viddata->activation_manager) { + struct wl_surface *requesting_surface = focus ? focus->surface : NULL; + Wayland_ActivationParams params = { + .token = token, + .done = false + }; + + struct wl_event_queue *activation_token_queue = Wayland_DisplayCreateQueue(viddata->display, "SDL Activation Token Generation Queue"); + + struct wl_proxy *activation_manager_wrapper = WAYLAND_wl_proxy_create_wrapper(viddata->activation_manager); + WAYLAND_wl_proxy_set_queue(activation_manager_wrapper, activation_token_queue); + struct xdg_activation_token_v1 *activation_token = xdg_activation_v1_get_activation_token((struct xdg_activation_v1 *)activation_manager_wrapper); + xdg_activation_token_v1_add_listener(activation_token, &xdg_activation_listener, ¶ms); + + if (requesting_surface) { + // This specifies the surface from which the activation request is originating, not the activation target surface. + xdg_activation_token_v1_set_surface(activation_token, requesting_surface); + } + if (seat && seat->wl_seat) { + xdg_activation_token_v1_set_serial(activation_token, seat->last_implicit_grab_serial, seat->wl_seat); + } + if (focus && focus->app_id) { + // Set the app ID for external use. + xdg_activation_token_v1_set_app_id(activation_token, focus->app_id); + } + xdg_activation_token_v1_commit(activation_token); + + while (!params.done) { + WAYLAND_wl_display_dispatch_queue(viddata->display, activation_token_queue); + } + WAYLAND_wl_proxy_wrapper_destroy(activation_manager_wrapper); + WAYLAND_wl_event_queue_destroy(activation_token_queue); + + if (!*token) { + return false; + } + } + + if (focus && window_id) { + const char *id = SDL_GetStringProperty(focus->sdlwindow->props, SDL_PROP_WINDOW_WAYLAND_XDG_TOPLEVEL_EXPORT_HANDLE_STRING, NULL); + if (id) { + const size_t len = SDL_strlen(id) + sizeof(WAYLAND_HANDLE_PREFIX) + 1; + *window_id = SDL_malloc(len); + if (!*window_id) { + SDL_free(*token); + *token = NULL; + return false; + } + + SDL_strlcpy(*window_id, WAYLAND_HANDLE_PREFIX, len); + SDL_strlcat(*window_id, id, len); + } + } + + return true; +} + +#endif // SDL_VIDEO_DRIVER_WAYLAND diff --git a/third-party/SDL/src/video/wayland/SDL_waylandutil.h b/third-party/SDL/src/video/wayland/SDL_waylandutil.h new file mode 100644 index 0000000000..daad6effb3 --- /dev/null +++ b/third-party/SDL/src/video/wayland/SDL_waylandutil.h @@ -0,0 +1,34 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2026 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_waylandutil_h_ +#define SDL_waylandutil_h_ + +#include "../SDL_sysvideo.h" + +/** + * Generates an activation token that can be passed to external clients. + * The token and window_id parameters must be freed with SDL_free() when done. + */ +extern bool Wayland_GetActivationTokenForExport(SDL_VideoDevice *_this, char **token, char **window_id); + +#endif // SDL_waylandutil_h_ diff --git a/third-party/SDL/src/video/wayland/SDL_waylandvideo.c b/third-party/SDL/src/video/wayland/SDL_waylandvideo.c index a984f8db09..d0f02b4894 100644 --- a/third-party/SDL/src/video/wayland/SDL_waylandvideo.c +++ b/third-party/SDL/src/video/wayland/SDL_waylandvideo.c @@ -677,6 +677,7 @@ static SDL_VideoDevice *Wayland_CreateDevice(bool require_preferred_protocols) device->SyncWindow = Wayland_SyncWindow; device->SetWindowFocusable = Wayland_SetWindowFocusable; device->ReconfigureWindow = Wayland_ReconfigureWindow; + device->AcceptDragAndDrop = Wayland_AcceptDragAndDrop; #ifdef SDL_USE_LIBDBUS if (SDL_SystemTheme_Init()) diff --git a/third-party/SDL/src/video/wayland/SDL_waylandwindow.c b/third-party/SDL/src/video/wayland/SDL_waylandwindow.c index 230125c10e..f939dd1bf9 100644 --- a/third-party/SDL/src/video/wayland/SDL_waylandwindow.c +++ b/third-party/SDL/src/video/wayland/SDL_waylandwindow.c @@ -758,10 +758,10 @@ static void handle_xdg_surface_configure(void *data, struct xdg_surface *xdg, ui wind->pending_config_ack = false; ConfigureWindowGeometry(window); xdg_surface_ack_configure(xdg, serial); - } else { + } else if (!wind->pending_config_ack) { wind->pending_config_ack = true; - // Send an exposure event so that clients doing deferred updates will trigger a frame callback and make guaranteed forward progress when resizing. + // Always send an exposure event during a new frame to ensure forward progress if the frame callback already occurred. SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_EXPOSED, 0, 0); } @@ -946,9 +946,9 @@ static void handle_xdg_toplevel_configure(void *data, const float aspect = (float)wind->requested.logical_width / (float)wind->requested.logical_height; if (window->min_aspect != 0.f && aspect < window->min_aspect) { - wind->requested.logical_height = SDL_lroundf((float)wind->requested.logical_width / window->min_aspect); + wind->requested.logical_height = SDL_max(SDL_lroundf((float)wind->requested.logical_width / window->min_aspect), 1); } else if (window->max_aspect != 0.f && aspect > window->max_aspect) { - wind->requested.logical_width = SDL_lroundf((float)wind->requested.logical_height * window->max_aspect); + wind->requested.logical_width = SDL_max(SDL_lroundf((float)wind->requested.logical_height * window->max_aspect), 1); } } else { if (window->max_w > 0) { @@ -965,9 +965,9 @@ static void handle_xdg_toplevel_configure(void *data, const float aspect = (float)wind->requested.pixel_width / (float)wind->requested.pixel_height; if (window->min_aspect != 0.f && aspect < window->min_aspect) { - wind->requested.pixel_height = SDL_lroundf((float)wind->requested.pixel_width / window->min_aspect); + wind->requested.pixel_height = SDL_max(SDL_lroundf((float)wind->requested.pixel_width / window->min_aspect), 1); } else if (window->max_aspect != 0.f && aspect > window->max_aspect) { - wind->requested.pixel_width = SDL_lroundf((float)wind->requested.pixel_height * window->max_aspect); + wind->requested.pixel_width = SDL_max(SDL_lroundf((float)wind->requested.pixel_height * window->max_aspect), 1); } wind->requested.logical_width = PixelToPoint(window, wind->requested.pixel_width); @@ -1392,9 +1392,9 @@ static void decoration_frame_configure(struct libdecor_frame *frame, const float aspect = (float)wind->requested.logical_width / (float)wind->requested.logical_height; if (window->min_aspect != 0.f && aspect < window->min_aspect) { - wind->requested.logical_height = SDL_lroundf((float)wind->requested.logical_width / window->min_aspect); + wind->requested.logical_height = SDL_max(SDL_lroundf((float)wind->requested.logical_width / window->min_aspect), 1); } else if (window->max_aspect != 0.f && aspect > window->max_aspect) { - wind->requested.logical_width = SDL_lroundf((float)wind->requested.logical_height * window->max_aspect); + wind->requested.logical_width = SDL_max(SDL_lroundf((float)wind->requested.logical_height * window->max_aspect), 1); } } else { if (window->max_w > 0) { @@ -1411,9 +1411,9 @@ static void decoration_frame_configure(struct libdecor_frame *frame, const float aspect = (float)wind->requested.pixel_width / (float)wind->requested.pixel_height; if (window->min_aspect != 0.f && aspect < window->min_aspect) { - wind->requested.pixel_height = SDL_lroundf((float)wind->requested.pixel_width / window->min_aspect); + wind->requested.pixel_height = SDL_max(SDL_lroundf((float)wind->requested.pixel_width / window->min_aspect), 1); } else if (window->max_aspect != 0.f && aspect > window->max_aspect) { - wind->requested.pixel_width = SDL_lroundf((float)wind->requested.pixel_height * window->max_aspect); + wind->requested.pixel_width = SDL_max(SDL_lroundf((float)wind->requested.pixel_height * window->max_aspect), 1); } wind->requested.logical_width = PixelToPoint(window, wind->requested.pixel_width); @@ -1462,6 +1462,11 @@ static void decoration_frame_configure(struct libdecor_frame *frame, struct libdecor_state *state = libdecor_state_new(wind->current.logical_width, wind->current.logical_height); libdecor_frame_commit(frame, state, configuration); libdecor_state_free(state); + + // Always send an exposure event during a new frame to ensure forward progress if the frame callback already occurred. + if (started_resize) { + SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_EXPOSED, 0, 0); + } } if (wind->shell_surface_status == WAYLAND_SHELL_SURFACE_STATUS_WAITING_FOR_CONFIGURE) { @@ -2103,7 +2108,7 @@ void Wayland_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window) wl_surface_commit(data->surface); } - // Make sure the window can't be resized to 0 or it can be spuriously closed by the window manager. + // Make sure the window can't be resized to 0, or it can be spuriously closed by the window manager. data->system_limits.min_width = SDL_max(data->system_limits.min_width, 1); data->system_limits.min_height = SDL_max(data->system_limits.min_height, 1); @@ -2146,6 +2151,13 @@ void Wayland_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window) } } + // No frame callback on an external surface, as it may already have one attached. + if (!(window->flags & SDL_WINDOW_EXTERNAL)) { + // Fire a callback when the compositor wants a new frame. + data->surface_frame_callback = wl_surface_frame(data->surface); + wl_callback_add_listener(data->surface_frame_callback, &surface_frame_listener, data); + } + data->show_hide_sync_required = true; struct wl_callback *cb = wl_display_sync(_this->internal->display); wl_callback_add_listener(cb, &show_hide_sync_listener, (void *)((uintptr_t)window->id)); @@ -2155,9 +2167,7 @@ void Wayland_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window) data->showing_window = false; // Send an exposure event to signal that the client should draw. - if (data->shell_surface_status == WAYLAND_SHELL_SURFACE_STATUS_WAITING_FOR_FRAME) { - SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_EXPOSED, 0, 0); - } + SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_EXPOSED, 0, 0); } static void Wayland_ReleasePopup(SDL_VideoDevice *_this, SDL_Window *popup) @@ -2214,6 +2224,11 @@ void Wayland_HideWindow(SDL_VideoDevice *_this, SDL_Window *window) wind->shell_surface_status = WAYLAND_SHELL_SURFACE_STATUS_HIDDEN; + if (wind->surface_frame_callback) { + wl_callback_destroy(wind->surface_frame_callback); + wind->surface_frame_callback = NULL; + } + if (wind->server_decoration) { zxdg_toplevel_decoration_v1_destroy(wind->server_decoration); wind->server_decoration = NULL; @@ -2803,13 +2818,6 @@ bool Wayland_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_Proper wl_callback_add_listener(data->gles_swap_frame_callback, &gles_swap_frame_listener, data); } - // No frame callback on external surfaces as it may already have one attached. - if (!external_surface) { - // Fire a callback when the compositor wants a new frame to set the surface damage region. - data->surface_frame_callback = wl_surface_frame(data->surface); - wl_callback_add_listener(data->surface_frame_callback, &surface_frame_listener, data); - } - if (window->flags & SDL_WINDOW_TRANSPARENT) { if (_this->gl_config.alpha_size == 0) { _this->gl_config.alpha_size = 8; @@ -3189,6 +3197,11 @@ bool Wayland_SyncWindow(SDL_VideoDevice *_this, SDL_Window *window) return true; } +void Wayland_AcceptDragAndDrop(SDL_Window *window, bool accept) +{ + window->internal->accepts_drag_and_drop = accept; +} + bool Wayland_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, bool focusable) { if (window->flags & SDL_WINDOW_POPUP_MENU) { @@ -3343,10 +3356,6 @@ void Wayland_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window) WAYLAND_wl_event_queue_destroy(wind->gles_swap_frame_event_queue); } - if (wind->surface_frame_callback) { - wl_callback_destroy(wind->surface_frame_callback); - } - if (!(window->flags & SDL_WINDOW_EXTERNAL)) { wl_surface_destroy(wind->surface); } else { diff --git a/third-party/SDL/src/video/wayland/SDL_waylandwindow.h b/third-party/SDL/src/video/wayland/SDL_waylandwindow.h index a82000c2bf..903d666f76 100644 --- a/third-party/SDL/src/video/wayland/SDL_waylandwindow.h +++ b/third-party/SDL/src/video/wayland/SDL_waylandwindow.h @@ -215,6 +215,7 @@ struct SDL_WindowData bool scale_to_display; bool reparenting_required; bool double_buffer; + bool accepts_drag_and_drop; SDL_HitTestResult hit_test_result; @@ -251,6 +252,7 @@ extern bool Wayland_SetWindowIcon(SDL_VideoDevice *_this, SDL_Window *window, SD extern bool Wayland_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, bool focusable); extern float Wayland_GetWindowContentScale(SDL_VideoDevice *_this, SDL_Window *window); extern void *Wayland_GetWindowICCProfile(SDL_VideoDevice *_this, SDL_Window *window, size_t *size); +extern void Wayland_AcceptDragAndDrop(SDL_Window *window, bool accept); extern bool Wayland_SetWindowHitTest(SDL_Window *window, bool enabled); extern bool Wayland_FlashWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_FlashOperation operation); diff --git a/third-party/SDL/src/video/windows/SDL_windowsclipboard.c b/third-party/SDL/src/video/windows/SDL_windowsclipboard.c index 219a5d7b06..b3fa551db0 100644 --- a/third-party/SDL/src/video/windows/SDL_windowsclipboard.c +++ b/third-party/SDL/src/video/windows/SDL_windowsclipboard.c @@ -33,7 +33,7 @@ // 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 UINT GetClipboardFormatPNG() +static UINT GetClipboardFormatPNG(void) { static UINT format; diff --git a/third-party/SDL/src/video/windows/SDL_windowskeyboard.c b/third-party/SDL/src/video/windows/SDL_windowskeyboard.c index 67cae22e55..b114772d85 100644 --- a/third-party/SDL/src/video/windows/SDL_windowskeyboard.c +++ b/third-party/SDL/src/video/windows/SDL_windowskeyboard.c @@ -101,7 +101,7 @@ static void WIN_CacheKeymap(HKL layout, SDL_Keymap *keymap) ++keymap_cache_size; } -static SDL_Keymap *WIN_BuildKeymap() +static SDL_Keymap *WIN_BuildKeymap(void) { SDL_Scancode scancode; BYTE keyboardState[256] = { 0 }; @@ -802,12 +802,14 @@ static void IME_ClearComposition(SDL_VideoData *videodata) IME_SendClearComposition(videodata); } -static void IME_GetCompositionString(SDL_VideoData *videodata, HIMC himc, DWORD string) +static void IME_GetCompositionString(SDL_VideoData *videodata, HIMC himc, LPARAM *lParam, DWORD string) { LONG length; DWORD dwLang = ((DWORD_PTR)videodata->ime_hkl & 0xffff); - videodata->ime_cursor = LOWORD(ImmGetCompositionStringW(himc, GCS_CURSORPOS, 0, 0)); + if (*lParam & GCS_CURSORPOS) { + videodata->ime_cursor = LOWORD(ImmGetCompositionStringW(himc, GCS_CURSORPOS, 0, 0)); + } videodata->ime_selected_start = 0; videodata->ime_selected_length = 0; SDL_DebugIMELog("Cursor = %d", videodata->ime_cursor); @@ -826,6 +828,11 @@ static void IME_GetCompositionString(SDL_VideoData *videodata, HIMC himc, DWORD } length /= sizeof(WCHAR); + if (!(*lParam & GCS_CURSORPOS)) { + // If the IME doesn't support GCS_CURSORPOS, default the cursor to the end of the composition. + videodata->ime_cursor = length; + } + if ((dwLang == LANG_CHT || dwLang == LANG_CHS) && videodata->ime_cursor > 0 && videodata->ime_cursor < (int)(videodata->ime_composition_length / sizeof(WCHAR)) && @@ -1057,16 +1064,12 @@ bool WIN_HandleIMEMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM *lParam, SD SDL_DebugIMELog("WM_IME_SETCONTEXT"); LPARAM element_mask; - if (videodata->ime_internal_composition && videodata->ime_internal_candidates) { - element_mask = 0; - } else { - element_mask = ISC_SHOWUIALL; - if (videodata->ime_internal_composition) { - element_mask &= ~ISC_SHOWUICOMPOSITIONWINDOW; - } - if (videodata->ime_internal_candidates) { - element_mask &= ~ISC_SHOWUIALLCANDIDATEWINDOW; - } + element_mask = ISC_SHOWUIALL; + if (videodata->ime_internal_composition) { + element_mask &= ~ISC_SHOWUICOMPOSITIONWINDOW; + } + if (videodata->ime_internal_candidates) { + element_mask &= ~ISC_SHOWUIALLCANDIDATEWINDOW; } *lParam &= element_mask; @@ -1074,6 +1077,7 @@ bool WIN_HandleIMEMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM *lParam, SD } else if (msg == WM_IME_STARTCOMPOSITION) { SDL_DebugIMELog("WM_IME_STARTCOMPOSITION"); if (videodata->ime_internal_composition) { + videodata->ime_cursor = 0; // Windows may still display a composition dialog even with // ISC_SHOWUICOMPOSITIONWINDOW cleared, so trap the message // here to prevent that (even when the IME is disabled). @@ -1113,14 +1117,14 @@ bool WIN_HandleIMEMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM *lParam, SD himc = ImmGetContext(hwnd); if (*lParam & GCS_RESULTSTR) { SDL_DebugIMELog("GCS_RESULTSTR"); - IME_GetCompositionString(videodata, himc, GCS_RESULTSTR); + IME_GetCompositionString(videodata, himc, lParam, GCS_RESULTSTR); IME_SendClearComposition(videodata); IME_SendInputEvent(videodata); } if (*lParam & GCS_COMPSTR) { SDL_DebugIMELog("GCS_COMPSTR"); videodata->ime_readingstring[0] = 0; - IME_GetCompositionString(videodata, himc, GCS_COMPSTR); + IME_GetCompositionString(videodata, himc, lParam, GCS_COMPSTR); IME_SendEditingEvent(videodata); } ImmReleaseContext(hwnd, himc); diff --git a/third-party/SDL/src/video/windows/SDL_windowsmouse.c b/third-party/SDL/src/video/windows/SDL_windowsmouse.c index 2cd4e726f3..3e7772b451 100644 --- a/third-party/SDL/src/video/windows/SDL_windowsmouse.c +++ b/third-party/SDL/src/video/windows/SDL_windowsmouse.c @@ -148,9 +148,23 @@ static SDL_Cursor *WIN_CreateAnimatedCursorAndData(SDL_CursorFrameInfo *frames, data->hot_y = hot_y; data->num_frames = frame_count; for (int i = 0; i < frame_count; ++i) { - data->frames[i].surface = frames[i].surface; + SDL_Surface *surface = frames[i].surface; + if (surface->flags & SDL_SURFACE_PREALLOCATED) { + surface = SDL_DuplicateSurface(surface); + if (!surface) { + while (i > 0) { + --i; + SDL_DestroySurface(data->frames[i].surface); + } + SDL_free(data); + SDL_free(cursor); + return NULL; + } + } else { + ++surface->refcount; + } + data->frames[i].surface = surface; data->frames[i].duration = frames[i].duration; - ++frames[i].surface->refcount; } cursor->internal = data; return cursor; diff --git a/third-party/SDL/src/video/windows/SDL_windowsrawinput.c b/third-party/SDL/src/video/windows/SDL_windowsrawinput.c index cada2b6d63..06a60a7dcc 100644 --- a/third-party/SDL/src/video/windows/SDL_windowsrawinput.c +++ b/third-party/SDL/src/video/windows/SDL_windowsrawinput.c @@ -323,6 +323,11 @@ bool WIN_SetRawKeyboardFlag_NoHotkeys(SDL_VideoDevice *_this, bool enabled) return SDL_Unsupported(); } +bool WIN_SetRawKeyboardFlag_Inputsink(SDL_VideoDevice *_this, bool enabled) +{ + return SDL_Unsupported(); +} + #endif // !SDL_PLATFORM_XBOXONE && !SDL_PLATFORM_XBOXSERIES #endif // SDL_VIDEO_DRIVER_WINDOWS diff --git a/third-party/SDL/src/video/x11/SDL_x11clipboard.c b/third-party/SDL/src/video/x11/SDL_x11clipboard.c index 601c42f279..8a79d13b77 100644 --- a/third-party/SDL/src/video/x11/SDL_x11clipboard.c +++ b/third-party/SDL/src/video/x11/SDL_x11clipboard.c @@ -29,7 +29,7 @@ #include "../SDL_clipboard_c.h" #include "../../events/SDL_events_c.h" -static const char *text_mime_types[] = { +static const char *const text_mime_types[] = { "UTF8_STRING", "text/plain;charset=utf-8", "text/plain", @@ -62,7 +62,7 @@ Window GetWindow(SDL_VideoDevice *_this) } static bool SetSelectionData(SDL_VideoDevice *_this, Atom selection, SDL_ClipboardDataCallback callback, - void *userdata, const char **mime_types, size_t mime_count, Uint32 sequence) + void *userdata, const char *const *mime_types, size_t mime_count, Uint32 sequence) { SDL_VideoData *videodata = _this->internal; Display *display = videodata->display; @@ -258,7 +258,7 @@ static void *GetSelectionData(SDL_VideoDevice *_this, Atom selection_type, return data; } -const char **X11_GetTextMimeTypes(SDL_VideoDevice *_this, size_t *num_mime_types) +const char *const *X11_GetTextMimeTypes(SDL_VideoDevice *_this, size_t *num_mime_types) { *num_mime_types = SDL_arraysize(text_mime_types); return text_mime_types; diff --git a/third-party/SDL/src/video/x11/SDL_x11clipboard.h b/third-party/SDL/src/video/x11/SDL_x11clipboard.h index 0044515d06..70dc4f6994 100644 --- a/third-party/SDL/src/video/x11/SDL_x11clipboard.h +++ b/third-party/SDL/src/video/x11/SDL_x11clipboard.h @@ -28,12 +28,12 @@ typedef struct X11_ClipboardData { SDL_ClipboardDataCallback callback; void *userdata; - const char **mime_types; + const char *const *mime_types; size_t mime_count; Uint32 sequence; } SDLX11_ClipboardData; -extern const char **X11_GetTextMimeTypes(SDL_VideoDevice *_this, size_t *num_mime_types); +extern const char *const *X11_GetTextMimeTypes(SDL_VideoDevice *_this, size_t *num_mime_types); extern bool X11_SetClipboardData(SDL_VideoDevice *_this); extern void *X11_GetClipboardData(SDL_VideoDevice *_this, const char *mime_type, size_t *length); extern bool X11_HasClipboardData(SDL_VideoDevice *_this, const char *mime_type); diff --git a/third-party/SDL/src/video/x11/SDL_x11events.c b/third-party/SDL/src/video/x11/SDL_x11events.c index b82590071e..dd44207f62 100644 --- a/third-party/SDL/src/video/x11/SDL_x11events.c +++ b/third-party/SDL/src/video/x11/SDL_x11events.c @@ -1130,7 +1130,7 @@ void X11_HandleKeyEvent(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_ } } -void X11_HandleButtonPress(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_MouseID mouseID, int button, float x, float y, unsigned long time) +void X11_HandleButtonPress(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_MouseID mouseID, int button, float x, float y, unsigned long time, unsigned long serial) { SDL_Window *window = windowdata->window; int xticks = 0, yticks = 0; @@ -1149,7 +1149,6 @@ void X11_HandleButtonPress(SDL_VideoDevice *_this, SDL_WindowData *windowdata, S if (X11_IsWheelEvent(button, &xticks, &yticks)) { 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 */ @@ -1164,11 +1163,14 @@ void X11_HandleButtonPress(SDL_VideoDevice *_this, SDL_WindowData *windowdata, S if (windowdata->last_focus_event_time) { const int X11_FOCUS_CLICK_TIMEOUT = 10; if (SDL_GetTicks() < (windowdata->last_focus_event_time + X11_FOCUS_CLICK_TIMEOUT)) { - ignore_click = !SDL_GetHintBoolean(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, false); + if (!SDL_GetHintBoolean(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, false)) { + // Ignore all press events with this serial. + windowdata->ignore_button_press_serial = serial; + } } windowdata->last_focus_event_time = 0; } - if (!ignore_click) { + if (serial != windowdata->ignore_button_press_serial) { SDL_SendMouseButton(timestamp, window, mouseID, button, true); } } @@ -1862,19 +1864,19 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent) case ButtonPress: { - if (data->xinput2_mouse_enabled) { - // This input is being handled by XInput2 + if (data->xinput2_mouse_enabled && xevent->xbutton.serial == videodata->xinput_last_button_serial) { + // This input event was handled by XInput2. break; } X11_HandleButtonPress(_this, data, SDL_GLOBAL_MOUSE_ID, xevent->xbutton.button, - xevent->xbutton.x, xevent->xbutton.y, xevent->xbutton.time); + xevent->xbutton.x, xevent->xbutton.y, xevent->xbutton.time, xevent->xbutton.serial); } break; case ButtonRelease: { - if (data->xinput2_mouse_enabled) { - // This input is being handled by XInput2 + if (data->xinput2_mouse_enabled && xevent->xbutton.serial == videodata->xinput_last_button_serial) { + // This input event was handled by XInput2. break; } diff --git a/third-party/SDL/src/video/x11/SDL_x11events.h b/third-party/SDL/src/video/x11/SDL_x11events.h index 8174f509cb..fbd0bace0a 100644 --- a/third-party/SDL/src/video/x11/SDL_x11events.h +++ b/third-party/SDL/src/video/x11/SDL_x11events.h @@ -31,7 +31,7 @@ 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, float x, float y, unsigned long time); +extern void X11_HandleButtonPress(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_MouseID mouseID, int button, float x, float y, unsigned long time, unsigned long serial); extern void X11_HandleButtonRelease(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_MouseID mouseID, int button, unsigned long time); extern SDL_WindowData *X11_FindWindow(SDL_VideoData *videodata, Window window); extern bool X11_ProcessHitTest(SDL_VideoDevice *_this, SDL_WindowData *data, const float x, const float y, bool force_new_result); diff --git a/third-party/SDL/src/video/x11/SDL_x11messagebox.c b/third-party/SDL/src/video/x11/SDL_x11messagebox.c index 489689692e..64d4938c69 100644 --- a/third-party/SDL/src/video/x11/SDL_x11messagebox.c +++ b/third-party/SDL/src/video/x11/SDL_x11messagebox.c @@ -250,11 +250,11 @@ static bool X11_ShowMessageBoxImpl(const SDL_MessageBoxData *messageboxdata, int #else controls.window = X11Toolkit_CreateWindowStruct(parent_window, NULL, SDL_TOOLKIT_WINDOW_MODE_X11_DIALOG, colorhints, false); #endif - controls.window->cb_data = &controls; - controls.window->cb_on_scale_change = X11_OnMessageBoxScaleChange; if (!controls.window) { return false; } + controls.window->cb_data = &controls; + controls.window->cb_on_scale_change = X11_OnMessageBoxScaleChange; /* Create controls */ controls.buttonID = buttonID; diff --git a/third-party/SDL/src/video/x11/SDL_x11video.h b/third-party/SDL/src/video/x11/SDL_x11video.h index 2dc4d3ab60..c6069b2624 100644 --- a/third-party/SDL/src/video/x11/SDL_x11video.h +++ b/third-party/SDL/src/video/x11/SDL_x11video.h @@ -138,6 +138,7 @@ struct SDL_VideoData Uint32 global_mouse_buttons; SDL_XInput2DeviceInfo *mouse_device_info; + unsigned long xinput_last_button_serial; int xinput_master_pointer_device; bool xinput_hierarchy_changed; diff --git a/third-party/SDL/src/video/x11/SDL_x11window.c b/third-party/SDL/src/video/x11/SDL_x11window.c index 4a68287cc2..e2c8f59c7b 100644 --- a/third-party/SDL/src/video/x11/SDL_x11window.c +++ b/third-party/SDL/src/video/x11/SDL_x11window.c @@ -374,9 +374,6 @@ static bool SetupWindowData(SDL_VideoDevice *_this, SDL_Window *window, Window w SDL_VideoData *videodata = _this->internal; SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(window); SDL_WindowData *data; - int numwindows = videodata->numwindows; - int windowlistlength = videodata->windowlistlength; - SDL_WindowData **windowlist = videodata->windowlist; // Allocate the window data data = (SDL_WindowData *)SDL_calloc(1, sizeof(*data)); @@ -392,19 +389,13 @@ static bool SetupWindowData(SDL_VideoDevice *_this, SDL_Window *window, Window w // Associate the data with the window - if (numwindows < windowlistlength) { - windowlist[numwindows] = data; - videodata->numwindows++; - } else { - SDL_WindowData ** new_windowlist = (SDL_WindowData **)SDL_realloc(windowlist, (numwindows + 1) * sizeof(*windowlist)); + if (videodata->numwindows >= videodata->windowlistlength) { + SDL_WindowData ** new_windowlist = (SDL_WindowData **)SDL_realloc(videodata->windowlist, (videodata->numwindows + 1) * sizeof(*videodata->windowlist)); if (!new_windowlist) { goto error_cleanup; } - windowlist = new_windowlist; - windowlist[numwindows] = data; - videodata->numwindows++; videodata->windowlistlength++; - videodata->windowlist = windowlist; + videodata->windowlist = new_windowlist; } // Fill in the SDL window with the window data @@ -487,6 +478,7 @@ static bool SetupWindowData(SDL_VideoDevice *_this, SDL_Window *window, Window w // All done! window->internal = data; + videodata->windowlist[videodata->numwindows++] = data; return true; error_cleanup: @@ -585,7 +577,8 @@ 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_OPENGL); // Doesn't work well with Vulkan + const bool use_resize_sync = SDL_GetHintBoolean(SDL_HINT_VIDEO_X11_ENABLE_XSYNC_EXT, false) && + (window->flags & SDL_WINDOW_OPENGL) != 0; // Doesn't work well with Vulkan SDL_WindowData *windowdata; Display *display = data->display; int screen = displaydata->screen; @@ -1199,6 +1192,7 @@ bool X11_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window *window) } X11_UpdateWindowPosition(window, false); } else { + window->internal->fs_repositioned = true; SDL_UpdateFullscreenMode(window, SDL_FULLSCREEN_OP_UPDATE, true); } return true; @@ -1897,7 +1891,8 @@ static SDL_FullscreenResult X11_SetWindowFullscreenViaWM(SDL_VideoDevice *_this, X11_XSendEvent(display, RootWindow(display, displaydata->screen), 0, SubstructureNotifyMask | SubstructureRedirectMask, &e); - if (!!(window->flags & SDL_WINDOW_FULLSCREEN) != fullscreen) { + // Only set the pending flag if the fullscreen state actually changed. + if (((window->flags & SDL_WINDOW_FULLSCREEN) != 0) != (fullscreen != 0)) { data->pending_operation |= X11_PENDING_OP_FULLSCREEN; } @@ -1920,6 +1915,8 @@ static SDL_FullscreenResult X11_SetWindowFullscreenViaWM(SDL_VideoDevice *_this, } } else { SDL_zero(data->requested_fullscreen_mode); + data->pending_position = data->fs_repositioned; + data->fs_repositioned = false; /* Fullscreen windows sometimes end up being marked maximized by * window managers. Force it back to how we expect it to be. diff --git a/third-party/SDL/src/video/x11/SDL_x11window.h b/third-party/SDL/src/video/x11/SDL_x11window.h index 5335ae8001..a9cc77c3b5 100644 --- a/third-party/SDL/src/video/x11/SDL_x11window.h +++ b/third-party/SDL/src/video/x11/SDL_x11window.h @@ -63,6 +63,7 @@ struct SDL_WindowData bool xinput2_keyboard_enabled; bool mouse_grabbed; Uint64 last_focus_event_time; + unsigned long ignore_button_press_serial; PendingFocusEnum pending_focus; Uint64 pending_focus_time; bool pending_move; @@ -111,6 +112,7 @@ struct SDL_WindowData bool pending_size; bool pending_position; + bool fs_repositioned; bool window_was_maximized; bool previous_borders_nonzero; bool toggle_borders; diff --git a/third-party/SDL/src/video/x11/SDL_x11xinput2.c b/third-party/SDL/src/video/x11/SDL_x11xinput2.c index 51a18a66e5..a0269e747e 100644 --- a/third-party/SDL/src/video/x11/SDL_x11xinput2.c +++ b/third-party/SDL/src/video/x11/SDL_x11xinput2.c @@ -139,7 +139,7 @@ static SDL_Window *xinput2_get_sdlwindow(SDL_VideoData *videodata, Window window #endif // SDL_VIDEO_DRIVER_X11_XINPUT2 #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_SCROLLINFO -static void xinput2_reset_scrollable_valuators() +static void xinput2_reset_scrollable_valuators(void) { for (int i = 0; i < scrollable_device_count; ++i) { for (int j = 0; j < scrollable_devices[i].scroll_info_count; ++j) { @@ -180,7 +180,7 @@ static void xinput2_parse_scrollable_valuators(const XIDeviceEvent *xev) const double y = info->scroll_type == XIScrollTypeVertical ? delta : 0; SDL_Mouse *mouse = SDL_GetMouse(); - SDL_SendMouseWheel(xev->time, mouse->focus, (SDL_MouseID)xev->sourceid, (float)x, (float)y, SDL_MOUSEWHEEL_NORMAL); + SDL_SendMouseWheel(xev->time, mouse->focus, (SDL_MouseID)xev->sourceid, (float)-x, (float)y, SDL_MOUSEWHEEL_NORMAL); } info->prev_value = current_val; info->prev_value_valid = true; @@ -590,6 +590,9 @@ void X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie) bool pointer_emulated = false; #endif + // Store the button serial to filter out redundant core button events. + videodata->xinput_last_button_serial = xev->serial; + if (pen) { if (xev->deviceid != xev->sourceid) { // Discard events from "Master" devices to avoid duplicates. @@ -607,8 +610,15 @@ void X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie) SDL_WindowData *windowdata = X11_FindWindow(videodata, xev->event); int x_ticks = 0, y_ticks = 0; - // Slave pointer devices don't have button remapping applied automatically, so do it manually. if (xev->deviceid != videodata->xinput_master_pointer_device) { + /* Ignore slave button events on non-focused windows, as they can arrive before FocusIn events, + * or result in focus being incorrectly set while a grab is active. + */ + if (SDL_GetMouseFocus() != windowdata->window || SDL_GetKeyboardFocus() != windowdata->window) { + break; + } + + // Slave pointer devices don't have button remapping applied automatically, so do it manually. if (button <= xinput2_pointer_button_map_size) { button = xinput2_pointer_button_map[button - 1]; } @@ -623,7 +633,7 @@ void X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie) if (down) { X11_HandleButtonPress(_this, windowdata, (SDL_MouseID)xev->sourceid, button, - (float)xev->event_x, (float)xev->event_y, xev->time); + (float)xev->event_x, (float)xev->event_y, xev->time, xev->serial); } else { X11_HandleButtonRelease(_this, windowdata, (SDL_MouseID)xev->sourceid, button, xev->time); } diff --git a/third-party/SDL/test/build-shaders.sh b/third-party/SDL/test/build-shaders.sh old mode 100644 new mode 100755 diff --git a/third-party/SDL/test/checkkeys.c b/third-party/SDL/test/checkkeys.c index f2223b6879..9c2e28ec93 100644 --- a/third-party/SDL/test/checkkeys.c +++ b/third-party/SDL/test/checkkeys.c @@ -434,7 +434,7 @@ static void loop(void) } /* Slow down framerate */ - SDL_Delay(100); + SDL_Delay(10); #ifdef SDL_PLATFORM_EMSCRIPTEN if (done) { @@ -467,6 +467,9 @@ int main(int argc, char *argv[]) /* Disable mouse emulation */ SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0"); + /* Enable text editing events and tell SDL that we'll handle rendering compositions. */ + SDL_SetHint(SDL_HINT_IME_IMPLEMENTED_UI, "composition"); + /* Initialize SDL */ if (!SDLTest_CommonInit(state)) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s", SDL_GetError()); diff --git a/third-party/SDL/test/emscripten/driver.py b/third-party/SDL/test/emscripten/driver.py old mode 100644 new mode 100755 diff --git a/third-party/SDL/test/emscripten/server.py b/third-party/SDL/test/emscripten/server.py old mode 100644 new mode 100755 diff --git a/third-party/SDL/test/testautomation_render.c b/third-party/SDL/test/testautomation_render.c index c88a3df7ea..071ae5db16 100644 --- a/third-party/SDL/test/testautomation_render.c +++ b/third-party/SDL/test/testautomation_render.c @@ -1361,6 +1361,20 @@ static int SDLCALL render_testClipRect(void *arg) /* Check to see if final image matches. */ compare(referenceSurface, ALLOWABLE_ERROR_OPAQUE); + /* + * Verify that empty cliprect clips all drawing + */ + + /* Set the cliprect and do a fill operation */ + cliprect.h = 0; + CHECK_FUNC(SDL_SetRenderClipRect, (renderer, &cliprect)) + CHECK_FUNC(SDL_SetRenderDrawColor, (renderer, 255, 0, 0, SDL_ALPHA_OPAQUE)) + CHECK_FUNC(SDL_RenderFillRect, (renderer, NULL)) + CHECK_FUNC(SDL_SetRenderClipRect, (renderer, NULL)) + + /* Check to see if final image matches. */ + compare(referenceSurface, ALLOWABLE_ERROR_OPAQUE); + /* * Verify that clear ignores the cliprect */ diff --git a/third-party/SDL/test/testautomation_video.c b/third-party/SDL/test/testautomation_video.c index c5e3022217..fe5436bc85 100644 --- a/third-party/SDL/test/testautomation_video.c +++ b/third-party/SDL/test/testautomation_video.c @@ -845,6 +845,7 @@ static int SDLCALL video_getSetWindowPosition(void *arg) { const char *title = "video_getSetWindowPosition Test Window"; SDL_Window *window; + SDL_WindowFlags flags; int result; int maxxVariation, maxyVariation; int xVariation, yVariation; @@ -976,6 +977,95 @@ static int SDLCALL video_getSetWindowPosition(void *arg) } } + /* Fullscreen test */ + desiredX = 100; + desiredY = 100; + SDL_SetWindowPosition(window, desiredX, desiredY); + SDLTest_AssertPass("Call to SDL_SetWindowPosition(...,%d,%d)", desiredX, desiredY); + + result = SDL_SyncWindow(window); + SDLTest_AssertPass("SDL_SyncWindow()"); + SDLTest_AssertCheck(result == true, "Verify return value; expected: true, got: %d", result); + + /* Get position */ + currentX = desiredX + 1; + currentY = desiredY + 1; + SDL_GetWindowPosition(window, ¤tX, ¤tY); + SDLTest_AssertPass("Call to SDL_GetWindowPosition()"); + + if (desiredX == currentX && desiredY == currentY) { + SDLTest_AssertCheck(desiredX == currentX, "Verify returned X position; expected: %d, got: %d", desiredX, currentX); + SDLTest_AssertCheck(desiredY == currentY, "Verify returned Y position; expected: %d, got: %d", desiredY, currentY); + } else { + bool hasEvent; + /* SDL_SetWindowPosition() and SDL_SetWindowSize() will make requests of the window manager and set the internal position and size, + * and then we get events signaling what actually happened, and they get passed on to the application if they're not what we expect. */ + currentX = desiredX + 1; + currentY = desiredY + 1; + hasEvent = getPositionFromEvent(¤tX, ¤tY); + SDLTest_AssertCheck(hasEvent == true, "Changing position was not honored by WM, checking present of SDL_EVENT_WINDOW_MOVED"); + if (hasEvent) { + SDLTest_AssertCheck(desiredX == currentX, "Verify returned X position is the position from SDL event; expected: %d, got: %d", desiredX, currentX); + SDLTest_AssertCheck(desiredY == currentY, "Verify returned Y position is the position from SDL event; expected: %d, got: %d", desiredY, currentY); + } + } + + /* Test setting position while fullscreen */ + result = SDL_SetWindowFullscreen(window, true); + SDLTest_AssertPass("SDL_SetWindowFullscreen()"); + 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); + + /* Verify that window is in fullscreen */ + flags = SDL_GetWindowFlags(window); + SDLTest_AssertPass("SDL_GetWindowFlags()"); + SDLTest_AssertCheck(flags & SDL_WINDOW_FULLSCREEN, "Verify the `SDL_WINDOW_FULLSCREEN` flag is set: %s", (flags & SDL_WINDOW_FULLSCREEN) ? "true" : "false"); + + /* Set the fullscreen window position */ + desiredX = desiredX + 10; + desiredY = desiredY + 10; + SDL_SetWindowPosition(window, desiredX, desiredY); + SDLTest_AssertPass("Call to SDL_SetWindowPosition(...,%d,%d)", desiredX, desiredY); + + result = SDL_SetWindowFullscreen(window, false); + SDLTest_AssertPass("SDL_SetWindowFullscreen()"); + 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); + + /* Verify that window left fullscreen */ + flags = SDL_GetWindowFlags(window); + SDLTest_AssertPass("SDL_GetWindowFlags()"); + SDLTest_AssertCheck(!(flags & SDL_WINDOW_FULLSCREEN), "Verify the `SDL_WINDOW_FULLSCREEN` flag is not set: %s", !(flags & SDL_WINDOW_FULLSCREEN) ? "true" : "false"); + + /* Get position */ + currentX = desiredX + 1; + currentY = desiredY + 1; + SDL_GetWindowPosition(window, ¤tX, ¤tY); + SDLTest_AssertPass("Call to SDL_GetWindowPosition()"); + + if (desiredX == currentX && desiredY == currentY) { + SDLTest_AssertCheck(desiredX == currentX, "Verify returned X position; expected: %d, got: %d", desiredX, currentX); + SDLTest_AssertCheck(desiredY == currentY, "Verify returned Y position; expected: %d, got: %d", desiredY, currentY); + } else { + bool hasEvent; + /* SDL_SetWindowPosition() and SDL_SetWindowSize() will make requests of the window manager and set the internal position and size, + * and then we get events signaling what actually happened, and they get passed on to the application if they're not what we expect. */ + currentX = desiredX + 1; + currentY = desiredY + 1; + hasEvent = getPositionFromEvent(¤tX, ¤tY); + SDLTest_AssertCheck(hasEvent == true, "Changing position was not honored by WM, checking present of SDL_EVENT_WINDOW_MOVED"); + if (hasEvent) { + SDLTest_AssertCheck(desiredX == currentX, "Verify returned X position is the position from SDL event; expected: %d, got: %d", desiredX, currentX); + SDLTest_AssertCheck(desiredY == currentY, "Verify returned Y position is the position from SDL event; expected: %d, got: %d", desiredY, currentY); + } + } + null_tests: /* Dummy call with both pointers NULL */ diff --git a/third-party/SDL/test/testfilesystem.c b/third-party/SDL/test/testfilesystem.c index 87ba39501f..3e522c3e60 100644 --- a/third-party/SDL/test/testfilesystem.c +++ b/third-party/SDL/test/testfilesystem.c @@ -94,6 +94,9 @@ int main(int argc, char *argv[]) char *curdir; const char *base_path; + /* this will be SDL's best guess at the human-readable exe name (or bundle id, or whatever) by default. */ + SDL_Log("Default app name: '%s'", SDL_GetAppMetadataProperty(SDL_PROP_APP_METADATA_NAME_STRING)); + /* Initialize test framework */ state = SDLTest_CommonCreateState(argv, 0); if (!state) { diff --git a/third-party/SDL/test/testgpu/build-shaders.sh b/third-party/SDL/test/testgpu/build-shaders.sh old mode 100644 new mode 100755 diff --git a/third-party/SDL/test/testime.c b/third-party/SDL/test/testime.c index e2945a2cf3..cd09d6ddd1 100644 --- a/third-party/SDL/test/testime.c +++ b/third-party/SDL/test/testime.c @@ -474,7 +474,7 @@ static void InitInput(WindowState *ctx) { /* Prepare a rect for text input */ ctx->textRect.x = 100.0f; - ctx->textRect.y = 250.0f; + ctx->textRect.y = 250.0f; ctx->textRect.w = DEFAULT_WINDOW_WIDTH - 2 * ctx->textRect.x; ctx->textRect.h = 50.0f; ctx->markedRect = ctx->textRect; @@ -1062,7 +1062,7 @@ int main(int argc, char *argv[]) SDL_StopTextInput(ctx->window); } else { SDL_Log("Enabling text input\n"); - SDL_StartTextInput(ctx->window); + SDL_StartTextInputWithProperties(ctx->window, ctx->text_settings); } } break; @@ -1109,12 +1109,14 @@ int main(int argc, char *argv[]) int index = (event.key.key - SDLK_KP_1); if (index < state->num_windows) { SDL_Window *window = state->windows[index]; + ctx = &windowstate[index]; if (SDL_TextInputActive(window)) { SDL_Log("Disabling text input for window %d\n", 1 + index); SDL_StopTextInput(window); } else { SDL_Log("Enabling text input for window %d\n", 1 + index); SDL_StartTextInput(window); + SDL_StartTextInputWithProperties(window, ctx->text_settings); } } } diff --git a/third-party/SDL/test/testpalette.c b/third-party/SDL/test/testpalette.c index 6355d59230..acaead57c2 100644 --- a/third-party/SDL/test/testpalette.c +++ b/third-party/SDL/test/testpalette.c @@ -304,7 +304,7 @@ static SDL_Texture *CreateTexture(const void *pixels, int pitch) return tex; } -static bool CreateTextures() +static bool CreateTextures(void) { Uint8 data[256]; int i; diff --git a/third-party/SDL/test/testsymbols.c b/third-party/SDL/test/testsymbols.c index 7c68fd15c3..ab1e9dec8e 100644 --- a/third-party/SDL/test/testsymbols.c +++ b/third-party/SDL/test/testsymbols.c @@ -66,7 +66,7 @@ extern SDL_DECLSPEC void SDLCALL SDL_SetWindowsMessageHook(void); extern SDL_DECLSPEC void SDLCALL JNI_OnLoad(void); -const static struct { +static const struct { const char *name; SDL_FunctionPointer address; } sdl_symbols[] = { diff --git a/third-party/SDL/test/testurl.c b/third-party/SDL/test/testurl.c index 56b1904e6a..a6fd9c5ec4 100644 --- a/third-party/SDL/test/testurl.c +++ b/third-party/SDL/test/testurl.c @@ -25,29 +25,27 @@ static void tryOpenURL(const char *url) int main(int argc, char **argv) { - int i; - SDLTest_CommonState *state; - - state = SDLTest_CommonCreateState(argv, 0); - - if (!SDL_Init(SDL_INIT_VIDEO)) { - SDL_Log("SDL_Init failed: %s", SDL_GetError()); - return 1; - } + const char *url = NULL; + SDLTest_CommonState *state = SDLTest_CommonCreateState(argv, 0); + bool use_gui = false; /* Parse commandline */ - for (i = 1; i < argc;) { + for (int i = 1; i < argc;) { int consumed; consumed = SDLTest_CommonArg(state, i); if (consumed == 0) { if (argv[i][0] != '-') { - tryOpenURL(argv[i]); + url = argv[i]; + consumed = 1; + } else if (SDL_strcasecmp(argv[i], "--gui") == 0) { + use_gui = true; consumed = 1; } } if (consumed <= 0) { static const char *options[] = { + "[--gui]" "[URL [...]]", NULL, }; @@ -57,6 +55,38 @@ int main(int argc, char **argv) i += consumed; } + state->flags = SDL_INIT_VIDEO; + if (!SDLTest_CommonInit(state)) { + return SDL_APP_FAILURE; + } + + if (!use_gui) { + tryOpenURL(url); + } else { + SDL_Event event; + bool quit = false; + + while (!quit) { + while (SDL_PollEvent(&event)) { + if (event.type == SDL_EVENT_KEY_DOWN) { + if (event.key.key == SDLK_SPACE) { + tryOpenURL(url); + } else if (event.key.key == SDLK_ESCAPE) { + quit = true; + } + } else if (event.type == SDL_EVENT_QUIT) { + quit = true; + } + } + + SDL_SetRenderDrawColor(state->renderers[0], 0, 0, 0, 255); + SDL_RenderClear(state->renderers[0]); + SDL_SetRenderDrawColor(state->renderers[0], 255, 255, 255, 255); + SDL_RenderDebugTextFormat(state->renderers[0], 8.f, 16.f, "Press space to open %s", url); + SDL_RenderPresent(state->renderers[0]); + } + } + SDL_Quit(); SDLTest_CommonDestroyState(state); return 0; diff --git a/vendor.yaml b/vendor.yaml index 34921dbc79..e4ff979589 100644 --- a/vendor.yaml +++ b/vendor.yaml @@ -28,7 +28,7 @@ third-party/sqlite3: third-party/fmt: git: https://github.com/fmtlib/fmt/tree/11.1.4 third-party/SDL: - git: https://github.com/libsdl-org/SDL/tree/release-3.4.4 + git: https://github.com/libsdl-org/SDL/tree/release-3.4.10 third-party/imgui: git: https://github.com/ocornut/imgui/releases/tag/v1.92.6 third-party/tree-sitter: