mirror of
https://github.com/open-goal/jak-project
synced 2026-07-31 08:16:02 -04:00
+2
@@ -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").
|
||||
|
||||
<!--- Provide a general summary of your changes in the Title above -->
|
||||
|
||||
## Description
|
||||
|
||||
+2
-2
@@ -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 }}
|
||||
|
||||
+2
-2
@@ -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
|
||||
|
||||
+2
-2
@@ -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 }}
|
||||
|
||||
+3
-3
@@ -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
|
||||
|
||||
+2
-2
@@ -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 }}
|
||||
|
||||
+2
-2
@@ -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
|
||||
|
||||
+11
-11
@@ -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
|
||||
|
||||
+33
-33
@@ -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 }}'
|
||||
|
||||
+20
@@ -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.
|
||||
+22
@@ -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.
|
||||
|
||||
|
||||
+28
-28
@@ -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 <stddef.h>
|
||||
#include <iconv.h>
|
||||
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 <stddef.h>
|
||||
#include <iconv.h>
|
||||
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 <stddef.h>
|
||||
#include <iconv.h>
|
||||
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 <stddef.h>
|
||||
#include <iconv.h>
|
||||
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")
|
||||
|
||||
+2
-2
@@ -19,10 +19,10 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>3.4.4</string>
|
||||
<string>3.4.10</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>SDLX</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>3.4.4</string>
|
||||
<string>3.4.10</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
+4
-4
@@ -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;
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
+2
-1
@@ -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();
|
||||
|
||||
Generated
Vendored
+209
-35
@@ -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<GattOperation> 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<Integer, BluetoothGattCharacteristic> mOutputReportChars = new HashMap<Integer, BluetoothGattCharacteristic>();
|
||||
|
||||
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
|
||||
|
||||
Generated
Vendored
+10
-3
@@ -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);
|
||||
|
||||
Generated
Vendored
+45
-4
@@ -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 {
|
||||
|
||||
Generated
Vendored
+20
-10
@@ -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);
|
||||
|
||||
Generated
Vendored
+80
-5
@@ -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<InputDevice.MotionRange> hats;
|
||||
ArrayList<Light> lights;
|
||||
LightsManager.LightsSession lightsSession;
|
||||
SensorManager sensorManager;
|
||||
SDLJoySensorListener sensorListener;
|
||||
Sensor accelerometerSensor;
|
||||
Sensor gyroscopeSensor;
|
||||
}
|
||||
static class RangeComparator implements Comparator<InputDevice.MotionRange> {
|
||||
@Override
|
||||
@@ -225,12 +242,13 @@ class SDLJoystickHandler {
|
||||
joystick.desc = getJoystickDescriptor(joystickDevice);
|
||||
joystick.axes = new ArrayList<InputDevice.MotionRange>();
|
||||
joystick.hats = new ArrayList<InputDevice.MotionRange>();
|
||||
java.util.Set<Integer> axisStrsSet = new java.util.HashSet<Integer>();
|
||||
joystick.lights = new ArrayList<Light>();
|
||||
|
||||
List<InputDevice.MotionRange> 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]);
|
||||
}
|
||||
}
|
||||
|
||||
Generated
Vendored
+32
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+18
-3
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
+41
@@ -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
|
||||
(...)
|
||||
|
||||
Generated
Vendored
Regular → Executable
+5
@@ -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);
|
||||
}
|
||||
|
||||
-1
@@ -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__")
|
||||
|
||||
-1
@@ -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__")
|
||||
|
||||
-1
@@ -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__")
|
||||
|
||||
+1
-1
@@ -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)
|
||||
|
||||
+3
-1
@@ -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")
|
||||
|
||||
+1
-1
@@ -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.
|
||||
|
||||
|
||||
+2
-2
@@ -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
|
||||
|
||||
+1
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -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.
|
||||
}
|
||||
}
|
||||
|
||||
+20
-22
@@ -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);
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
+1
-1
@@ -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! */
|
||||
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
+1
-1
@@ -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)
|
||||
|
||||
+12
-2
@@ -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 <arm64intr.h>
|
||||
#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:
|
||||
|
||||
+14
-3
@@ -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.
|
||||
|
||||
+1
-1
@@ -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.
|
||||
|
||||
+1
-1
@@ -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 */
|
||||
|
||||
+5
-5
@@ -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) */
|
||||
|
||||
+26
-1
@@ -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.
|
||||
|
||||
+20
-20
@@ -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);
|
||||
|
||||
+5
-4
@@ -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)
|
||||
|
||||
+1
-1
@@ -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.
|
||||
|
||||
-1
@@ -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
|
||||
|
||||
+4
-1
@@ -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";
|
||||
}
|
||||
|
||||
+23
-14
@@ -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;
|
||||
}
|
||||
|
||||
+7
-1
@@ -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;
|
||||
|
||||
+5
@@ -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
|
||||
|
||||
+35
-2
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
+3
@@ -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,
|
||||
|
||||
+55
-18
@@ -33,18 +33,16 @@
|
||||
#include <atomic.h>
|
||||
#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)
|
||||
|
||||
+8
-9
@@ -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
|
||||
}
|
||||
|
||||
+29
-10
@@ -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);
|
||||
|
||||
+552
-57
@@ -44,6 +44,7 @@
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <dlfcn.h>
|
||||
#include <time.h>
|
||||
|
||||
#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();
|
||||
|
||||
+3
@@ -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,
|
||||
|
||||
+115
@@ -22,6 +22,9 @@
|
||||
#include "SDL_dbus.h"
|
||||
#include "../../stdlib/SDL_vacopy.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#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);
|
||||
|
||||
+2
@@ -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);
|
||||
|
||||
|
||||
+1
@@ -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();
|
||||
|
||||
+1
-33
@@ -24,39 +24,6 @@
|
||||
#include "SDL_appid.h"
|
||||
#include <unistd.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
-1
@@ -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_
|
||||
|
||||
+2
-2
@@ -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;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user