From 85265575ead644b5973f07dbfe5a5cb848955a8c Mon Sep 17 00:00:00 2001 From: Rosalie Wanders Date: Tue, 13 Jan 2026 14:08:11 +0100 Subject: [PATCH] 3rdParty: update SDL_net --- .../SDL_net/.github/workflows/main.yml | 2 +- .../SDL_net/.github/workflows/release.yml | 483 +++++------ Source/3rdParty/SDL_net/.gitignore | 1 + Source/3rdParty/SDL_net/.gitrepo | 4 +- Source/3rdParty/SDL_net/LICENSE.txt | 2 +- .../SDL_net/build-scripts/wikiheaders.pl | 56 +- .../cmake/GetGitRevisionDescription.cmake | 16 + .../cmake/GetGitRevisionDescription.cmake.in | 6 +- .../SDL_net/cmake/PrivateSdlFunctions.cmake | 26 +- .../SDL_net/include/SDL3_net/SDL_net.h | 2 +- Source/3rdParty/SDL_net/src/SDL_net.c | 785 ++++++++++++------ Source/3rdParty/SDL_net/src/genexports.py | 2 +- Source/3rdParty/SDL_net/src/version.rc | 2 +- 13 files changed, 856 insertions(+), 531 deletions(-) diff --git a/Source/3rdParty/SDL_net/.github/workflows/main.yml b/Source/3rdParty/SDL_net/.github/workflows/main.yml index 6db5446b..1046fd02 100644 --- a/Source/3rdParty/SDL_net/.github/workflows/main.yml +++ b/Source/3rdParty/SDL_net/.github/workflows/main.yml @@ -42,7 +42,7 @@ jobs: ${{ matrix.platform.msys-env }}-ninja ${{ matrix.platform.msys-env }}-perl - name: Get SDL3_net sources - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Install Ninja if: ${{ !contains(matrix.platform.shell, 'msys2') }} uses: aseprite/get-ninja@main diff --git a/Source/3rdParty/SDL_net/.github/workflows/release.yml b/Source/3rdParty/SDL_net/.github/workflows/release.yml index c416aab7..3cb3b450 100644 --- a/Source/3rdParty/SDL_net/.github/workflows/release.yml +++ b/Source/3rdParty/SDL_net/.github/workflows/release.yml @@ -24,12 +24,12 @@ jobs: with: python-version: '3.11' - name: 'Fetch build-release.py' - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: ref: ${{ inputs.commit }} sparse-checkout: 'build-scripts/build-release.py' - name: 'Set up SDL sources' - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: ref: ${{ inputs.commit }} path: 'SDL' @@ -47,7 +47,7 @@ jobs: --github \ --debug - name: 'Store source archives' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: sources path: '${{ github.workspace}}/dist' @@ -69,7 +69,7 @@ jobs: with: python-version: '3.11' - name: 'Download source archives' - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v6 with: name: sources path: '/tmp' @@ -97,7 +97,7 @@ jobs: run: | ${{ steps.tar.outputs.path }}/build-scripts/test-versioning.sh - name: 'Fetch build-release.py' - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: ref: ${{ inputs.commit }} sparse-checkout: 'build-scripts/build-release.py' @@ -139,224 +139,225 @@ jobs: cmake --build /tmp/build --verbose # ctest --test-dir /tmp/build --no-tests=error --output-on-failure -# dmg: -# needs: [src] -# runs-on: macos-latest -# outputs: -# dmg: ${{ steps.releaser.outputs.dmg }} -# steps: -# - name: 'Set up Python' -# uses: actions/setup-python@v5 -# with: -# python-version: '3.11' -# - name: 'Fetch build-release.py' -# uses: actions/checkout@v4 -# with: -# ref: ${{ inputs.commit }} -# sparse-checkout: 'build-scripts/build-release.py' -# - name: 'Download source archives' -# uses: actions/download-artifact@v4 -# with: -# name: sources -# path: '${{ github.workspace }}' -# - name: 'Untar ${{ needs.src.outputs.src-tar-gz }}' -# id: tar + dmg: + needs: [src] + runs-on: macos-latest + outputs: + dmg: ${{ steps.releaser.outputs.dmg }} + steps: + - name: 'Set up Python' + uses: actions/setup-python@v5 + with: + python-version: '3.11' + - name: 'Fetch build-release.py' + uses: actions/checkout@v6 + with: + ref: ${{ inputs.commit }} + sparse-checkout: 'build-scripts/build-release.py' + - name: 'Download source archives' + uses: actions/download-artifact@v6 + with: + name: sources + path: '${{ github.workspace }}' + - name: 'Untar ${{ needs.src.outputs.src-tar-gz }}' + id: tar + run: | + mkdir -p "${{ github.workspace }}/tardir" + tar -C "${{ github.workspace }}/tardir" -v -x -f "${{ github.workspace }}/${{ needs.src.outputs.src-tar-gz }}" + echo "path=${{ github.workspace }}/tardir/${{ needs.src.outputs.project }}-${{ needs.src.outputs.version }}" >>$GITHUB_OUTPUT +# - name: 'Download external dependencies' # run: | -# mkdir -p "${{ github.workspace }}/tardir" -# tar -C "${{ github.workspace }}/tardir" -v -x -f "${{ github.workspace }}/${{ needs.src.outputs.src-tar-gz }}" -# echo "path=${{ github.workspace }}/tardir/${{ needs.src.outputs.project }}-${{ needs.src.outputs.version }}" >>$GITHUB_OUTPUT -## - name: 'Download external dependencies' -## run: | -## sh "${{ steps.tar.outputs.path }}/external/download.sh" --depth 1 -# - name: 'Build SDL3_net.dmg' -# id: releaser -# shell: bash -# env: -# GH_TOKEN: ${{ github.token }} -# run: | -# python build-scripts/build-release.py \ -# --actions dmg \ -# --commit ${{ inputs.commit }} \ -# --root "${{ steps.tar.outputs.path }}" \ -# --github \ -# --debug -# - name: 'Store DMG image file' -# uses: actions/upload-artifact@v4 -# with: -# name: dmg -# path: '${{ github.workspace }}/dist' -# -# dmg-verify: -# needs: [dmg, src] -# runs-on: macos-latest -# steps: -# - name: 'Set up Python' -# uses: actions/setup-python@v5 -# with: -# python-version: '3.11' -# - name: 'Fetch build-release.py' -# uses: actions/checkout@v4 -# with: -# ref: ${{ inputs.commit }} -# sparse-checkout: 'build-scripts/build-release.py' -# - name: 'Download source archives' -# uses: actions/download-artifact@v4 -# with: -# name: sources -# path: '${{ github.workspace }}' -# - name: 'Untar ${{ needs.src.outputs.src-tar-gz }}' -# id: src -# run: | -# mkdir -p /tmp/tardir -# tar -C /tmp/tardir -v -x -f "${{ github.workspace }}/${{ needs.src.outputs.src-tar-gz }}" -# echo "path=/tmp/tardir/${{ needs.src.outputs.project }}-${{ needs.src.outputs.version }}" >>$GITHUB_OUTPUT -# - name: 'Download dependencies' -# id: deps -# env: -# GH_TOKEN: ${{ github.token }} -# run: | -# python build-scripts/build-release.py \ -# --actions download \ -# --commit ${{ inputs.commit }} \ -# --root "${{ steps.src.outputs.path }}" \ -# --github \ -# --debug -# - name: 'Mount dependencies' -# id: deps-mount -# run: | -# hdiutil attach "${{ steps.deps.outputs.dep-path }}/SDL3-${{ steps.deps.outputs.dep-sdl-version }}.dmg" -# sdl_mount_pount="/Volumes/SDL3" -# if [ ! -d "$sdl_mount_pount/SDL3.xcframework" ]; then -# echo "Cannot find SDL3.xcframework!" -# exit 1 -# fi -# echo "path=${sdl_mount_pount}" >>$GITHUB_OUTPUT -# - name: 'Download ${{ needs.dmg.outputs.dmg }}' -# uses: actions/download-artifact@v4 -# with: -# name: dmg -# path: '${{ github.workspace }}' -# - name: 'Mount ${{ needs.dmg.outputs.dmg }}' -# id: mount -# run: | -# hdiutil attach '${{ github.workspace }}/${{ needs.dmg.outputs.dmg }}' -# mount_point="/Volumes/${{ needs.src.outputs.project }}" -# if [ ! -d "$mount_point/${{ needs.src.outputs.project }}.xcframework" ]; then -# echo "Cannot find ${{ needs.src.outputs.project }}.xcframework!" -# exit 1 -# fi -# echo "mount-point=${mount_point}">>$GITHUB_OUTPUT -# - name: 'CMake (configure + build) Darwin' -# run: | -# set -e -# cmake -S "${{ steps.src.outputs.path }}/cmake/test" \ -# -DTEST_SHARED=TRUE \ -# -DTEST_SHARED=TRUE \ -# -DTEST_STATIC=FALSE \ -# -DCMAKE_PREFIX_PATH="${{ steps.mount.outputs.mount-point }};${{ steps.deps-mount.outputs.path }}" \ -# -DCMAKE_SYSTEM_NAME=Darwin \ -# -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" \ -# -Werror=dev \ -# -B build_darwin -# cmake --build build_darwin --config Release --verbose -# -# cmake -S "${{ steps.src.outputs.path }}/cmake/test" \ -# -DTEST_SHARED=TRUE \ -# -DTEST_STATIC=FALSE \ -# -DCMAKE_PREFIX_PATH="${{ steps.mount.outputs.mount-point }}/SDL3_net.xcframework/macos-arm64_x86_64;${{ steps.deps-mount.outputs.path }}/SDL3.xcframework/macos-arm64_x86_64" \ -# -DCMAKE_SYSTEM_NAME=Darwin \ -# -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" \ -# -B build_darwin2 -# cmake --build build_darwin2 --config Release --verbose -# -# - name: 'CMake (configure + build) iOS' -# run: | -# cmake -S "${{ steps.src.outputs.path }}/cmake/test" \ -# -DTEST_SHARED=TRUE \ -# -DTEST_STATIC=FALSE \ -# -DCMAKE_PREFIX_PATH="${{ steps.mount.outputs.mount-point }};${{ steps.deps-mount.outputs.path }}" \ -# -DCMAKE_SYSTEM_NAME=iOS \ -# -DCMAKE_OSX_ARCHITECTURES="arm64" \ -# -Werror=dev \ -# -B build_ios -# cmake --build build_ios --config Release --verbose -# -# cmake -S "${{ steps.src.outputs.path }}/cmake/test" \ -# -DTEST_SHARED=TRUE \ -# -DTEST_STATIC=FALSE \ -# -DCMAKE_PREFIX_PATH="${{ steps.mount.outputs.mount-point }}/SDL3_net.xcframework/ios-arm64;${{ steps.deps-mount.outputs.path }}/SDL3.xcframework/ios-arm64" \ -# -DCMAKE_SYSTEM_NAME=iOS \ -# -DCMAKE_OSX_ARCHITECTURES="arm64" \ -# -B build_ios2 -# cmake --build build_ios2 --config Release --verbose -# - name: 'CMake (configure + build) tvOS' -# run: | -# cmake -S "${{ steps.src.outputs.path }}/cmake/test" \ -# -DTEST_SHARED=TRUE \ -# -DTEST_STATIC=FALSE \ -# -DCMAKE_PREFIX_PATH="${{ steps.mount.outputs.mount-point }};${{ steps.deps-mount.outputs.path }}" \ -# -DCMAKE_SYSTEM_NAME=tvOS \ -# -DCMAKE_OSX_ARCHITECTURES="arm64" \ -# -Werror=dev \ -# -B build_tvos -# cmake --build build_tvos --config Release --verbose -# -# cmake -S "${{ steps.src.outputs.path }}/cmake/test" \ -# -DTEST_SHARED=TRUE \ -# -DTEST_STATIC=FALSE \ -# -DCMAKE_PREFIX_PATH="${{ steps.mount.outputs.mount-point }}/SDL3_net.xcframework/tvos-arm64;${{ steps.deps-mount.outputs.path }}/SDL3.xcframework/tvos-arm64" \ -# -DCMAKE_SYSTEM_NAME=tvOS \ -# -DCMAKE_OSX_ARCHITECTURES="arm64" \ -# -B build_tvos2 -# cmake --build build_tvos2 --config Release --verbose -# - name: 'CMake (configure + build) iOS simulator' -# run: | -# sysroot=$(xcodebuild -version -sdk iphonesimulator Path) -# echo "sysroot=$sysroot" -# cmake -S "${{ steps.src.outputs.path }}/cmake/test" \ -# -DTEST_SHARED=TRUE \ -# -DTEST_STATIC=FALSE \ -# -DCMAKE_PREFIX_PATH="${{ steps.mount.outputs.mount-point }};${{ steps.deps-mount.outputs.path }}" \ -# -DCMAKE_SYSTEM_NAME=iOS \ -# -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" \ -# -DCMAKE_OSX_SYSROOT="${sysroot}" \ -# -Werror=dev \ -# -B build_ios_simulator -# cmake --build build_ios_simulator --config Release --verbose -# -# cmake -S "${{ steps.src.outputs.path }}/cmake/test" \ -# -DTEST_SHARED=TRUE \ -# -DTEST_STATIC=FALSE \ -# -DCMAKE_PREFIX_PATH="${{ steps.mount.outputs.mount-point }}/SDL3_net.xcframework/ios-arm64_x86_64-simulator;${{ steps.deps-mount.outputs.path }}/SDL3.xcframework/ios-arm64_x86_64-simulator" \ -# -DCMAKE_SYSTEM_NAME=iOS \ -# -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" \ -# -DCMAKE_OSX_SYSROOT="${sysroot}" \ -# -B build_ios_simulator2 -# cmake --build build_ios_simulator2 --config Release --verbose -# - name: 'CMake (configure + build) tvOS simulator' -# run: | -# sysroot=$(xcodebuild -version -sdk appletvsimulator Path) -# echo "sysroot=$sysroot" -# cmake -S "${{ steps.src.outputs.path }}/cmake/test" \ -# -DTEST_SHARED=TRUE \ -# -DTEST_STATIC=FALSE \ -# -DCMAKE_PREFIX_PATH="${{ steps.mount.outputs.mount-point }};${{ steps.deps-mount.outputs.path }}" \ -# -DCMAKE_SYSTEM_NAME=tvOS \ -# -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" \ -# -DCMAKE_OSX_SYSROOT="${sysroot}" \ -# -Werror=dev \ -# -B build_tvos_simulator -# cmake --build build_tvos_simulator --config Release --verbose -# -# cmake -S "${{ steps.src.outputs.path }}/cmake/test" \ -# -DTEST_SHARED=TRUE \ -# -DTEST_STATIC=FALSE \ -# -DCMAKE_PREFIX_PATH="${{ steps.mount.outputs.mount-point }}/SDL3_net.xcframework/tvos-arm64_x86_64-simulator;${{ steps.deps-mount.outputs.path }}/SDL3.xcframework/tvos-arm64_x86_64-simulator" \ -# -DCMAKE_SYSTEM_NAME=tvOS \ -# -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" \ -# -DCMAKE_OSX_SYSROOT="${sysroot}" \ -# -B build_tvos_simulator2 -# cmake --build build_tvos_simulator2 --config Release --verbose +# sh "${{ steps.tar.outputs.path }}/external/download.sh" --depth 1 + - name: 'Build SDL3_net.dmg' + id: releaser + shell: bash + env: + GH_TOKEN: ${{ github.token }} + run: | + python build-scripts/build-release.py \ + --actions dmg \ + --commit ${{ inputs.commit }} \ + --root "${{ steps.tar.outputs.path }}" \ + --github \ + --debug + - name: 'Store DMG image file' + uses: actions/upload-artifact@v6 + with: + name: dmg + path: '${{ github.workspace }}/dist' + + dmg-verify: + needs: [dmg, src] + runs-on: macos-latest + steps: + - name: 'Set up Python' + uses: actions/setup-python@v5 + with: + python-version: '3.11' + - name: 'Fetch build-release.py' + uses: actions/checkout@v6 + with: + ref: ${{ inputs.commit }} + sparse-checkout: 'build-scripts/build-release.py' + - name: 'Download source archives' + uses: actions/download-artifact@v6 + with: + name: sources + path: '${{ github.workspace }}' + - name: 'Untar ${{ needs.src.outputs.src-tar-gz }}' + id: src + run: | + mkdir -p /tmp/tardir + tar -C /tmp/tardir -v -x -f "${{ github.workspace }}/${{ needs.src.outputs.src-tar-gz }}" + echo "path=/tmp/tardir/${{ needs.src.outputs.project }}-${{ needs.src.outputs.version }}" >>$GITHUB_OUTPUT + - name: 'Download dependencies' + id: deps + env: + GH_TOKEN: ${{ github.token }} + run: | + python build-scripts/build-release.py \ + --actions download \ + --commit ${{ inputs.commit }} \ + --root "${{ steps.src.outputs.path }}" \ + --github \ + --debug + - name: 'Mount dependencies' + id: deps-mount + run: | + hdiutil attach "${{ steps.deps.outputs.dep-path }}/SDL3-${{ steps.deps.outputs.dep-sdl-version }}.dmg" + sdl_mount_pount="/Volumes/SDL3" + if [ ! -d "$sdl_mount_pount/SDL3.xcframework" ]; then + echo "Cannot find SDL3.xcframework!" + exit 1 + fi + echo "path=${sdl_mount_pount}" >>$GITHUB_OUTPUT + - name: 'Download ${{ needs.dmg.outputs.dmg }}' + uses: actions/download-artifact@v6 + with: + name: dmg + path: '${{ github.workspace }}' + - name: 'Mount ${{ needs.dmg.outputs.dmg }}' + id: mount + run: | + hdiutil attach '${{ github.workspace }}/${{ needs.dmg.outputs.dmg }}' + mount_point="/Volumes/${{ needs.src.outputs.project }}" + if [ ! -d "$mount_point/${{ needs.src.outputs.project }}.xcframework" ]; then + echo "Cannot find ${{ needs.src.outputs.project }}.xcframework!" + exit 1 + fi + echo "mount-point=${mount_point}">>$GITHUB_OUTPUT + - name: 'CMake (configure + build) Darwin' + run: | + set -e + cmake -S "${{ steps.src.outputs.path }}/cmake/test" \ + -DTEST_SHARED=TRUE \ + -DTEST_SHARED=TRUE \ + -DTEST_STATIC=FALSE \ + -DCMAKE_PREFIX_PATH="${{ steps.mount.outputs.mount-point }};${{ steps.deps-mount.outputs.path }}" \ + -DCMAKE_SYSTEM_NAME=Darwin \ + -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" \ + -Werror=dev \ + -B build_darwin + cmake --build build_darwin --config Release --verbose + + cmake -S "${{ steps.src.outputs.path }}/cmake/test" \ + -DTEST_SHARED=TRUE \ + -DTEST_STATIC=FALSE \ + -DCMAKE_PREFIX_PATH="${{ steps.mount.outputs.mount-point }}/SDL3_net.xcframework/macos-arm64_x86_64;${{ steps.deps-mount.outputs.path }}/SDL3.xcframework/macos-arm64_x86_64" \ + -DCMAKE_SYSTEM_NAME=Darwin \ + -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" \ + -B build_darwin2 + cmake --build build_darwin2 --config Release --verbose + + - name: 'CMake (configure + build) iOS' + run: | + cmake -S "${{ steps.src.outputs.path }}/cmake/test" \ + -DTEST_SHARED=TRUE \ + -DTEST_STATIC=FALSE \ + -DCMAKE_PREFIX_PATH="${{ steps.mount.outputs.mount-point }};${{ steps.deps-mount.outputs.path }}" \ + -DCMAKE_SYSTEM_NAME=iOS \ + -DCMAKE_OSX_ARCHITECTURES="arm64" \ + -Werror=dev \ + -B build_ios + cmake --build build_ios --config Release --verbose + + cmake -S "${{ steps.src.outputs.path }}/cmake/test" \ + -DTEST_SHARED=TRUE \ + -DTEST_STATIC=FALSE \ + -DCMAKE_PREFIX_PATH="${{ steps.mount.outputs.mount-point }}/SDL3_net.xcframework/ios-arm64;${{ steps.deps-mount.outputs.path }}/SDL3.xcframework/ios-arm64" \ + -DCMAKE_SYSTEM_NAME=iOS \ + -DCMAKE_OSX_ARCHITECTURES="arm64" \ + -B build_ios2 + cmake --build build_ios2 --config Release --verbose + - name: 'CMake (configure + build) tvOS' + run: | + cmake -S "${{ steps.src.outputs.path }}/cmake/test" \ + -DTEST_SHARED=TRUE \ + -DTEST_STATIC=FALSE \ + -DCMAKE_PREFIX_PATH="${{ steps.mount.outputs.mount-point }};${{ steps.deps-mount.outputs.path }}" \ + -DCMAKE_SYSTEM_NAME=tvOS \ + -DCMAKE_OSX_ARCHITECTURES="arm64" \ + -Werror=dev \ + -B build_tvos + cmake --build build_tvos --config Release --verbose + + cmake -S "${{ steps.src.outputs.path }}/cmake/test" \ + -DTEST_SHARED=TRUE \ + -DTEST_STATIC=FALSE \ + -DCMAKE_PREFIX_PATH="${{ steps.mount.outputs.mount-point }}/SDL3_net.xcframework/tvos-arm64;${{ steps.deps-mount.outputs.path }}/SDL3.xcframework/tvos-arm64" \ + -DCMAKE_SYSTEM_NAME=tvOS \ + -DCMAKE_OSX_ARCHITECTURES="arm64" \ + -B build_tvos2 + cmake --build build_tvos2 --config Release --verbose + - name: 'CMake (configure + build) iOS simulator' + run: | + sysroot=$(xcodebuild -version -sdk iphonesimulator Path) + echo "sysroot=$sysroot" + cmake -S "${{ steps.src.outputs.path }}/cmake/test" \ + -DTEST_SHARED=TRUE \ + -DTEST_STATIC=FALSE \ + -DCMAKE_PREFIX_PATH="${{ steps.mount.outputs.mount-point }};${{ steps.deps-mount.outputs.path }}" \ + -DCMAKE_SYSTEM_NAME=iOS \ + -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" \ + -DCMAKE_OSX_SYSROOT="${sysroot}" \ + -Werror=dev \ + -B build_ios_simulator + cmake --build build_ios_simulator --config Release --verbose + + cmake -S "${{ steps.src.outputs.path }}/cmake/test" \ + -DTEST_SHARED=TRUE \ + -DTEST_STATIC=FALSE \ + -DCMAKE_PREFIX_PATH="${{ steps.mount.outputs.mount-point }}/SDL3_net.xcframework/ios-arm64_x86_64-simulator;${{ steps.deps-mount.outputs.path }}/SDL3.xcframework/ios-arm64_x86_64-simulator" \ + -DCMAKE_SYSTEM_NAME=iOS \ + -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" \ + -DCMAKE_OSX_SYSROOT="${sysroot}" \ + -B build_ios_simulator2 + cmake --build build_ios_simulator2 --config Release --verbose + - name: 'CMake (configure + build) tvOS simulator' + run: | + sysroot=$(xcodebuild -version -sdk appletvsimulator Path) + echo "sysroot=$sysroot" + cmake -S "${{ steps.src.outputs.path }}/cmake/test" \ + -DTEST_SHARED=TRUE \ + -DTEST_STATIC=FALSE \ + -DCMAKE_PREFIX_PATH="${{ steps.mount.outputs.mount-point }};${{ steps.deps-mount.outputs.path }}" \ + -DCMAKE_SYSTEM_NAME=tvOS \ + -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" \ + -DCMAKE_OSX_SYSROOT="${sysroot}" \ + -Werror=dev \ + -B build_tvos_simulator + cmake --build build_tvos_simulator --config Release --verbose + + cmake -S "${{ steps.src.outputs.path }}/cmake/test" \ + -DTEST_SHARED=TRUE \ + -DTEST_STATIC=FALSE \ + -DCMAKE_PREFIX_PATH="${{ steps.mount.outputs.mount-point }}/SDL3_net.xcframework/tvos-arm64_x86_64-simulator;${{ steps.deps-mount.outputs.path }}/SDL3.xcframework/tvos-arm64_x86_64-simulator" \ + -DCMAKE_SYSTEM_NAME=tvOS \ + -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" \ + -DCMAKE_OSX_SYSROOT="${sysroot}" \ + -B build_tvos_simulator2 + cmake --build build_tvos_simulator2 --config Release --verbose + msvc: needs: [src] runs-on: windows-2025 @@ -370,12 +371,12 @@ jobs: with: python-version: '3.11' - name: 'Fetch build-release.py' - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: ref: ${{ inputs.commit }} sparse-checkout: 'build-scripts/build-release.py' - name: 'Download source archives' - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v6 with: name: sources path: '${{ github.workspace }}' @@ -401,7 +402,7 @@ jobs: --github ` --debug - name: 'Store MSVC archives' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: msvc path: '${{ github.workspace }}/dist' @@ -411,7 +412,7 @@ jobs: runs-on: windows-latest steps: - name: 'Fetch .github/actions/setup-ninja/action.yml' - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: ref: ${{ inputs.commit }} sparse-checkout: | @@ -424,7 +425,7 @@ jobs: - name: Set up ninja uses: ./.github/actions/setup-ninja - name: 'Download source archives' - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v6 with: name: sources path: '${{ github.workspace }}' @@ -454,7 +455,7 @@ jobs: unzip "${{ steps.deps.outputs.dep-path }}/SDL3-devel-${{ steps.deps.outputs.dep-sdl-version }}-VC.zip" echo "path=${{ github.workspace }}/deps-vc" >>$env:GITHUB_OUTPUT - name: 'Download MSVC binaries' - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v6 with: name: msvc path: '${{ github.workspace }}' @@ -529,7 +530,7 @@ jobs: with: python-version: '3.11' - name: 'Fetch build-release.py' - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: ref: ${{ inputs.commit }} sparse-checkout: 'build-scripts/build-release.py' @@ -538,7 +539,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@v6 with: name: sources path: '${{ github.workspace }}' @@ -560,7 +561,7 @@ jobs: --github \ --debug - name: 'Store MinGW archives' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: mingw path: '${{ github.workspace }}/dist' @@ -574,7 +575,7 @@ jobs: with: python-version: '3.11' - name: 'Fetch build-release.py' - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: ref: ${{ inputs.commit }} sparse-checkout: 'build-scripts/build-release.py' @@ -583,7 +584,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@v6 with: name: sources path: '${{ github.workspace }}' @@ -619,7 +620,7 @@ jobs: mkdir -p /tmp/deps-mingw/cmake cp -rv /tmp/deps-mingw-extract/sdl3/SDL3-${{ steps.deps.outputs.dep-sdl-version }}/cmake/* /tmp/deps-mingw/cmake - name: 'Download MinGW binaries' - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v6 with: name: mingw path: '${{ github.workspace }}' @@ -665,7 +666,7 @@ jobs: 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' @@ -675,7 +676,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' @@ -684,7 +685,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@v6 with: name: sources path: '${{ github.workspace }}' @@ -708,7 +709,7 @@ jobs: --github \ --debug - name: 'Store Android archive(s)' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: android path: '${{ github.workspace }}/dist' @@ -721,17 +722,17 @@ jobs: uses: actions/setup-python@v5 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@v6 with: name: sources path: '${{ github.workspace }}' - name: 'Download Android .aar archive' - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v6 with: name: android path: '${{ github.workspace }}' diff --git a/Source/3rdParty/SDL_net/.gitignore b/Source/3rdParty/SDL_net/.gitignore index a46d9ec7..7d70950d 100644 --- a/Source/3rdParty/SDL_net/.gitignore +++ b/Source/3rdParty/SDL_net/.gitignore @@ -1,5 +1,6 @@ build/ buildbot/ +!build-scripts/ /VERSION.txt *.so diff --git a/Source/3rdParty/SDL_net/.gitrepo b/Source/3rdParty/SDL_net/.gitrepo index a9eafe4b..bed454e9 100644 --- a/Source/3rdParty/SDL_net/.gitrepo +++ b/Source/3rdParty/SDL_net/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = git@github.com:libsdl-org/SDL_net.git branch = main - commit = cb287ed74f612f137e29fab9dfc08b3dc87a67e4 - parent = d23f381dfa7d85acfa198a20d5cf5b202676b297 + commit = 4dd85af45d8597f93796815fba0defa5d499be21 + parent = 02eef161e7f4ac7a4deebd0115574b7a27c5f58f method = merge cmdver = 0.4.9 diff --git a/Source/3rdParty/SDL_net/LICENSE.txt b/Source/3rdParty/SDL_net/LICENSE.txt index 23abb73f..e9adee44 100644 --- a/Source/3rdParty/SDL_net/LICENSE.txt +++ b/Source/3rdParty/SDL_net/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (C) 1997-2025 Sam Lantinga +Copyright (C) 1997-2026 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/Source/3rdParty/SDL_net/build-scripts/wikiheaders.pl b/Source/3rdParty/SDL_net/build-scripts/wikiheaders.pl index 6e465476..2092912b 100755 --- a/Source/3rdParty/SDL_net/build-scripts/wikiheaders.pl +++ b/Source/3rdParty/SDL_net/build-scripts/wikiheaders.pl @@ -1,7 +1,7 @@ #!/usr/bin/perl -w # Simple DirectMedia Layer -# Copyright (C) 1997-2025 Sam Lantinga +# Copyright (C) 1997-2026 Sam Lantinga # # This software is provided 'as-is', without any express or implied # warranty. In no event will the authors be held liable for any damages @@ -37,6 +37,11 @@ my $versionfname = 'include/SDL_version.h'; my $versionmajorregex = '\A\#define\s+SDL_MAJOR_VERSION\s+(\d+)\Z'; my $versionminorregex = '\A\#define\s+SDL_MINOR_VERSION\s+(\d+)\Z'; my $versionmicroregex = '\A\#define\s+SDL_MICRO_VERSION\s+(\d+)\Z'; +my $wikidocsectionsym = 'SDL_WIKI_DOCUMENTATION_SECTION'; +my $forceinlinesym = 'SDL_FORCE_INLINE'; +my $deprecatedsym = 'SDL_DEPRECATED'; +my $declspecsym = '(?:SDLMAIN_|SDL_)?DECLSPEC'; +my $callconvsym = 'SDLCALL'; my $mainincludefname = 'SDL.h'; my $selectheaderregex = '\ASDL.*?\.h\Z'; my $projecturl = 'https://libsdl.org/'; @@ -147,6 +152,12 @@ if (defined $optionsfname) { $envvardesc = $val, next if $key eq 'envvardesc'; $envvarsymregex = $val, next if $key eq 'envvarsymregex'; $envvarsymreplace = $val, next if $key eq 'envvarsymreplace'; + $wikidocsectionsym = $val, next if $key eq 'wikidocsectionsym'; + $forceinlinesym = $val, next if $key eq 'forceinlinesym'; + $deprecatedsym = $val, next if $key eq 'deprecatedsym'; + $declspecsym = $val, next if $key eq 'declspecsym'; + $callconvsym = $val, next if $key eq 'callconvsym'; + } } close(OPTIONS); @@ -750,6 +761,7 @@ sub print_undocumented_section { } } +# !!! FIXME: generalize this for other libraries to use. sub strip_fn_declaration_metadata { my $decl = shift; $decl =~ s/SDL_(PRINTF|SCANF)_FORMAT_STRING\s*//; # don't want this metadata as part of the documentation. @@ -1153,7 +1165,7 @@ while (my $d = readdir(DH)) { } elsif ($ignoring_lines) { push @contents, $_; next; - } elsif (/\A\s*\#\s*ifndef\s+SDL_WIKI_DOCUMENTATION_SECTION\s*\Z/) { + } elsif (/\A\s*\#\s*ifndef\s+$wikidocsectionsym\s*\Z/) { $ignoring_lines = 1; push @contents, $_; next; @@ -1162,13 +1174,13 @@ while (my $d = readdir(DH)) { #print("CATEGORY FOR '$dent' CHANGED TO " . (defined($current_wiki_category) ? "'$current_wiki_category'" : '(undef)') . "\n"); push @contents, $_; next; - } elsif (/\A\s*extern\s+(SDL_DEPRECATED\s+|)(SDLMAIN_|SDL_)?DECLSPEC/) { # a function declaration without a doxygen comment? + } elsif (/\A\s*extern\s+(?:$deprecatedsym\s+|)$declspecsym/) { # a function declaration without a doxygen comment? $symtype = 1; # function declaration @templines = (); $decl = $_; $str = ''; $has_doxygen = 0; - } elsif (/\A\s*SDL_FORCE_INLINE/) { # a (forced-inline) function declaration without a doxygen comment? + } elsif (/\A\s*$forceinlinesym/) { # a (forced-inline) function declaration without a doxygen comment? $symtype = 1; # function declaration @templines = (); $decl = $_; @@ -1235,9 +1247,9 @@ while (my $d = readdir(DH)) { $lineno++ if defined $decl; $decl = '' if not defined $decl; chomp($decl); - if ($decl =~ /\A\s*extern\s+(SDL_DEPRECATED\s+|)(SDLMAIN_|SDL_)?DECLSPEC/) { + if ($decl =~ /\A\s*extern\s+(?:$deprecatedsym\s+|)$declspecsym/) { $symtype = 1; # function declaration - } elsif ($decl =~ /\A\s*SDL_FORCE_INLINE/) { + } elsif ($decl =~ /\A\s*$forceinlinesym/) { $symtype = 1; # (forced-inline) function declaration } elsif ($decl =~ /\A\s*\#\s*define\s+/) { $symtype = 2; # macro @@ -1274,7 +1286,7 @@ while (my $d = readdir(DH)) { } $headercategorydocs{$current_wiki_category} = $sym; } elsif ($symtype == 1) { # a function - my $is_forced_inline = ($decl =~ /\A\s*SDL_FORCE_INLINE/); + my $is_forced_inline = ($decl =~ /\A\s*$forceinlinesym/); if ($is_forced_inline) { if (not $decl =~ /\)\s*(\{.*|)\s*\Z/) { @@ -1311,14 +1323,14 @@ while (my $d = readdir(DH)) { my $paramsstr = undef; - if (!$is_forced_inline && $decl =~ /\A\s*extern\s+(SDL_DEPRECATED\s+|)(SDLMAIN_|SDL_)?DECLSPEC\w*\s+(const\s+|)(unsigned\s+|)(.*?)([\*\s]+)(\*?)\s*SDLCALL\s+(.*?)\s*\((.*?)\);/) { - $sym = $8; - $rettype = "$3$4$5$6"; - $paramsstr = $9; - } elsif ($is_forced_inline && $decl =~ /\A\s*SDL_FORCE_INLINE\s+(SDL_DEPRECATED\s+|)(const\s+|)(unsigned\s+|)(.*?)([\*\s]+)(.*?)\s*\((.*?)\);/) { + if (!$is_forced_inline && $decl =~ /\A\s*extern\s+(?:$deprecatedsym\s+|)$declspecsym\w*\s+(const\s+|)(unsigned\s+|)(.*?)([\*\s]+)(\*?)\s*$callconvsym\s+(.*?)\s*\((.*?)\);/) { $sym = $6; - $rettype = "$2$3$4$5"; + $rettype = "$1$2$3$4$5"; $paramsstr = $7; + } elsif ($is_forced_inline && $decl =~ /\A\s*$forceinlinesym\s+(?:$deprecatedsym\s+|)(const\s+|)(unsigned\s+|)(.*?)([\*\s]+)(.*?)\s*\((.*?)\);/) { + $sym = $5; + $rettype = "$1$2$3$4"; + $paramsstr = $6; } else { #print "Found doxygen but no function sig:\n$str\n\n"; foreach (@templines) { @@ -1384,7 +1396,7 @@ while (my $d = readdir(DH)) { $decl = $_; $temp = $decl; - $temp =~ s/\Aextern\s+(SDL_DEPRECATED\s+|)(SDLMAIN_|SDL_)?DECLSPEC\w*\s+(.*?)\s+(\*?)SDLCALL\s+/$3$4 /; + $temp =~ s/\Aextern\s+(?:$deprecatedsym\s+|)$declspecsym\w*\s+(.*?)\s+(\*?)$callconvsym\s+/$1$2 /; $shrink_length = length($decl) - length($temp); $decl = $temp; } else { @@ -2259,10 +2271,10 @@ if ($copy_direction == 1) { # --copy-to-headers $desc =~ s/[\s\n]+\Z//ms; - if (0) { # !!! FIXME: disabled because it's not currently suitable for general use, but for manually inspecting the output, it can be useful. - if (($desc =~ /\A[A-Z]/) && (not $desc =~ /\ASDL_/)) { - print STDERR "WARNING: $sym\'s '\\param $arg' text starts with a capital letter: '$desc'. Fixing.\n"; - $desc = lcfirst($desc); + if (0) { + if (($desc =~ /\A[a-z]/) && (not $desc =~ /$apiprefixregex/)) { + print STDERR "WARNING: $sym\'s '\\param $arg' text starts with a lowercase letter: '$desc'. Fixing.\n"; + $desc = ucfirst($desc); } } @@ -2306,8 +2318,8 @@ if ($copy_direction == 1) { # --copy-to-headers } $desc =~ s/[\s\n]+\Z//ms; - if (0) { # !!! FIXME: disabled because it's not currently suitable for general use, but for manually inspecting the output, it can be useful. - if (($desc =~ /\A[A-Z]/) && (not $desc =~ /\ASDL_/)) { + if (0) { + if (($desc =~ /\A[A-Z]/) && (not $desc =~ /$apiprefixregex/)) { print STDERR "WARNING: $sym\'s '\\returns' text starts with a capital letter: '$desc'. Fixing.\n"; $desc = lcfirst($desc); } @@ -2451,7 +2463,7 @@ if ($copy_direction == 1) { # --copy-to-headers } else { die("Unexpected symbol type $symtype!"); } - my $str = "This $symtypename is available since SDL 3.0.0."; + my $str = "This $symtypename is available since $projectshortname 3.0.0."; $sections{'Version'} = wordwrap(wikify($wikitype, $str)) . "\n"; } } @@ -2916,7 +2928,7 @@ __EOF__ $str .= ".\\\" Please report issues in this manpage's content at:\n"; $str .= ".\\\" $bugreporturl\n"; $str .= ".\\\" Please report issues in the generation of this manpage from the wiki at:\n"; - $str .= ".\\\" https://github.com/libsdl-org/SDL/issues/new?title=Misgenerated%20manpage%20for%20$sym\n"; + $str .= ".\\\" https://github.com/libsdl-org/SDL/issues/new?title=Misgenerated%20manpage%20for%20$sym\n"; # !!! FIXME: if this becomes a problem for other projects, we'll generalize this. $str .= ".\\\" $projectshortname can be found at $projecturl\n"; # Define a .URL macro. The "www.tmac" thing decides if we're using GNU roff (which has a .URL macro already), and if so, overrides the macro we just created. diff --git a/Source/3rdParty/SDL_net/cmake/GetGitRevisionDescription.cmake b/Source/3rdParty/SDL_net/cmake/GetGitRevisionDescription.cmake index a08895c6..70c554e3 100644 --- a/Source/3rdParty/SDL_net/cmake/GetGitRevisionDescription.cmake +++ b/Source/3rdParty/SDL_net/cmake/GetGitRevisionDescription.cmake @@ -164,6 +164,22 @@ function(get_git_head_revision _refspecvar _hashvar) "${GIT_DATA}/grabRef.cmake" @ONLY) include("${GIT_DATA}/grabRef.cmake") + # Fallback for reftable or other storage formats + if(NOT HEAD_HASH OR HEAD_HASH STREQUAL "") + find_package(Git QUIET) + if(GIT_FOUND) + execute_process( + COMMAND "${GIT_EXECUTABLE}" rev-parse HEAD + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + RESULT_VARIABLE res + OUTPUT_VARIABLE HEAD_HASH + ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) + if(NOT res EQUAL 0) + set(HEAD_HASH "") + endif() + endif() + endif() + set(${_refspecvar} "${HEAD_REF}" PARENT_SCOPE) diff --git a/Source/3rdParty/SDL_net/cmake/GetGitRevisionDescription.cmake.in b/Source/3rdParty/SDL_net/cmake/GetGitRevisionDescription.cmake.in index 116efc4e..81b4213e 100644 --- a/Source/3rdParty/SDL_net/cmake/GetGitRevisionDescription.cmake.in +++ b/Source/3rdParty/SDL_net/cmake/GetGitRevisionDescription.cmake.in @@ -25,19 +25,21 @@ if(HEAD_CONTENTS MATCHES "ref") string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}") if(EXISTS "@GIT_DIR@/${HEAD_REF}") configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY) - else() + elseif(EXISTS "@GIT_DIR@/packed-refs") configure_file("@GIT_DIR@/packed-refs" "@GIT_DATA@/packed-refs" COPYONLY) file(READ "@GIT_DATA@/packed-refs" PACKED_REFS) if(${PACKED_REFS} MATCHES "([0-9a-z]*) ${HEAD_REF}") set(HEAD_HASH "${CMAKE_MATCH_1}") endif() + elseif(EXISTS "@GIT_DIR@/reftable/tables.list") + configure_file("@GIT_DIR@/reftable/tables.list" "@GIT_DATA@/reftable-tables.list" COPYONLY) endif() else() # detached HEAD configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY) endif() -if(NOT HEAD_HASH) +if(NOT HEAD_HASH AND EXISTS "@GIT_DATA@/head-ref") file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024) string(STRIP "${HEAD_HASH}" HEAD_HASH) endif() diff --git a/Source/3rdParty/SDL_net/cmake/PrivateSdlFunctions.cmake b/Source/3rdParty/SDL_net/cmake/PrivateSdlFunctions.cmake index 8d71ede0..0f3eb406 100644 --- a/Source/3rdParty/SDL_net/cmake/PrivateSdlFunctions.cmake +++ b/Source/3rdParty/SDL_net/cmake/PrivateSdlFunctions.cmake @@ -3,6 +3,9 @@ include(CheckCCompilerFlag) include(CheckCSourceCompiles) include(CMakePushCheckState) +if(NOT CMAKE_VERSION VERSION_LESS "3.18") + include(CheckLinkerFlag) +endif() macro(sdl_calculate_derived_version_variables MAJOR MINOR MICRO) set(SO_VERSION_MAJOR "0") @@ -241,11 +244,14 @@ function(sdl_check_project_in_subfolder relative_subfolder name vendored_option) endfunction() macro(sdl_check_linker_flag flag var) - # FIXME: Use CheckLinkerFlag module once cmake minimum version >= 3.18 - cmake_push_check_state(RESET) - set(CMAKE_REQUIRED_LINK_OPTIONS "${flag}") - check_c_source_compiles("int main() { return 0; }" ${var} FAIL_REGEX "(unsupported|syntax error|unrecognized option)") - cmake_pop_check_state() + if(NOT CMAKE_VERSION VERSION_LESS "3.18") + check_linker_flag(C "${flag}" "${var}") + else() + cmake_push_check_state(RESET) + set(CMAKE_REQUIRED_LINK_OPTIONS "${flag}") + check_c_source_compiles("int main() { return 0; }" ${var} FAIL_REGEX "(unsupported|syntax error|unrecognized option)") + cmake_pop_check_state() + endif() endmacro() function(SDL_detect_linker) @@ -283,7 +289,6 @@ endfunction() function(sdl_target_link_options_no_undefined TARGET) if(NOT MSVC AND NOT CMAKE_SYSTEM_NAME MATCHES ".*OpenBSD.*") if(CMAKE_C_COMPILER_ID MATCHES "AppleClang") - target_link_options(${TARGET} PRIVATE "-Wl,-undefined,error") else() sdl_check_linker_flag("-Wl,--no-undefined" HAVE_WL_NO_UNDEFINED) if(HAVE_WL_NO_UNDEFINED AND NOT ((CMAKE_C_COMPILER_ID MATCHES "Clang") AND WIN32)) @@ -318,6 +323,15 @@ function(sdl_add_warning_options TARGET) else() target_compile_options(${TARGET} PRIVATE -Werror) endif() + get_property(target_type TARGET ${TARGET} PROPERTY TYPE) + if(NOT (APPLE OR MSVC)) + if(target_type MATCHES "SHARED_LIBRARY|MODULE_LIBRARY") + sdl_check_linker_flag("-Wl,-fatal-warnings" HAVE_WL_FATAL_WARNINGS) + if(HAVE_WL_FATAL_WARNINGS) + target_link_options(${TARGET} PRIVATE "-Wl,-fatal-warnings") + endif() + endif() + endif() endif() endfunction() diff --git a/Source/3rdParty/SDL_net/include/SDL3_net/SDL_net.h b/Source/3rdParty/SDL_net/include/SDL3_net/SDL_net.h index f2446900..a7a2ca87 100644 --- a/Source/3rdParty/SDL_net/include/SDL3_net/SDL_net.h +++ b/Source/3rdParty/SDL_net/include/SDL3_net/SDL_net.h @@ -1,6 +1,6 @@ /* SDL_net: A simple networking library for use with SDL - Copyright (C) 1997-2025 Sam Lantinga + Copyright (C) 1997-2026 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/Source/3rdParty/SDL_net/src/SDL_net.c b/Source/3rdParty/SDL_net/src/SDL_net.c index 1d80611d..46aae63b 100644 --- a/Source/3rdParty/SDL_net/src/SDL_net.c +++ b/Source/3rdParty/SDL_net/src/SDL_net.c @@ -1,6 +1,6 @@ /* SDL_net: A simple networking library for use with SDL - Copyright (C) 1997-2025 Sam Lantinga + Copyright (C) 1997-2026 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -171,6 +171,7 @@ static int WindowsPoll(struct pollfd *fds, unsigned int nfds, int timeout) #include #include #include +#include #include #include #include @@ -179,7 +180,11 @@ static int WindowsPoll(struct pollfd *fds, unsigned int nfds, int timeout) #include #include -#ifdef SDL_PLATFORM_ANDROID +#if defined(SDL_PLATFORM_LINUX) || defined(SDL_PLATFORM_ANDROID) +#define USE_NETLINK 1 +#endif + +#ifdef USE_NETLINK #include #include #else @@ -206,6 +211,14 @@ int NET_Version(void) return SDL_NET_VERSION; } +typedef enum NET_AddressType +{ + NET_ADDRTYPE_UNKNOWN=-1, + NET_ADDRTYPE_UNICAST, + NET_ADDRTYPE_MULTICAST, + NET_ADDRTYPE_BROADCAST +} NET_AddressType; + struct NET_Address { char *hostname; @@ -213,13 +226,24 @@ struct NET_Address char *errstr; SDL_AtomicInt refcount; SDL_AtomicInt status; // This is actually a NET_Status. + NET_AddressType type; struct addrinfo *ainfo; NET_Address *resolver_next; // a linked list for the resolution job queue. }; +typedef struct NetworkInterfaces +{ + char *name; + int index; + NET_Address *address; + NET_Address *broadcast; +} NetworkInterfaces; + + #define MIN_RESOLVER_THREADS 2 #define MAX_RESOLVER_THREADS 10 +// Address resolver state... static NET_Address *resolver_queue = NULL; static SDL_Thread *resolver_threads[MAX_RESOLVER_THREADS]; static SDL_Mutex *resolver_lock = NULL; @@ -229,6 +253,13 @@ static SDL_AtomicInt resolver_num_threads; static SDL_AtomicInt resolver_num_requests; static SDL_AtomicInt resolver_percent_loss; +// Network interface state... +static SDL_InitState interface_init; +static SDL_RWLock *interface_rwlock = NULL; +static int num_interfaces = 0; +static NetworkInterfaces *interfaces = NULL; +static SDL_AtomicInt interfaces_have_changed; + // between lo and hi (inclusive; it can return lo or hi itself, too!). static int RandomNumberBetween(const int lo, const int hi) { @@ -326,6 +357,470 @@ static bool SetGetAddrInfoErrorBool(const char *msg, int err) return false; } +static int MakeSocketNonblocking(Socket handle) +{ + #ifdef SDL_PLATFORM_WINDOWS + DWORD one = 1; + return ioctlsocket(handle, FIONBIO, &one); + #else + return fcntl(handle, F_SETFL, fcntl(handle, F_GETFL, 0) | O_NONBLOCK); + #endif +} + +static bool WouldBlock(const int err) +{ + #ifdef SDL_PLATFORM_WINDOWS + return (err == WSAEWOULDBLOCK); + #else + return ((err == EWOULDBLOCK) || (err == EAGAIN) || (err == EINPROGRESS)); + #endif +} + +static NET_Address *CreateSDLNetAddrFromSockAddr(const struct sockaddr *saddr, SockLen saddrlen); + + + +/* Network interface enumeration... */ +static void FreeNetworkInterfaces(NetworkInterfaces *ifaces, int num_ifaces) +{ + for (int i = 0; i < num_ifaces; i++) { + NET_UnrefAddress(ifaces[i].address); + NET_UnrefAddress(ifaces[i].broadcast); + SDL_free(ifaces[i].name); + } + SDL_free(ifaces); +} + + +#ifdef SDL_PLATFORM_WINDOWS +static HANDLE interface_change_notifications_handle; + +static VOID WINAPI NetworkInterfaceChangedCallback(PVOID ctx, PMIB_IPINTERFACE_ROW row, MIB_NOTIFICATION_TYPE type) +{ + SDL_SetAtomicInt(&interfaces_have_changed, 1); +} + +static bool InitInterfaceChangeNotifications(void) // WINDOWS VERSION +{ + // this would need to use NotifyAddrChange for Windows XP, but it doesn't work with IPv6 before Vista. + //return (NotifyAddrChange(&addr_change_notification_handle, &overlapped) == ERROR_IO_PENDING); + + // !!! FIXME: this function is available on Vista and later. + return (NotifyIpInterfaceChange(AF_UNSPEC, NetworkInterfaceChangedCallback, NULL, FALSE, &interface_change_notifications_handle) == NO_ERROR); +} + +static void QuitInterfaceChangeNotifications(void) // WINDOWS VERSION +{ + CancelMibChangeNotify2(interface_change_notifications_handle); + interface_change_notifications_handle = 0; +} + +static void RefreshInterfaces(void) // WINDOWS VERSION +{ + // MSDN docs say start with a 15K buffer, which usually works on the first + // try, instead of trying to query for size, allocate, and then retry, + // since this tends to be more expensive. + ULONG buflen = 15 * 1024; + IP_ADAPTER_ADDRESSES *addrs = NULL; + ULONG rc; + + do { + SDL_free(addrs); + addrs = (IP_ADAPTER_ADDRESSES *) SDL_malloc(buflen); + if (!addrs) { + return; + } + + const ULONG flags = GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME; + rc = GetAdaptersAddresses(AF_UNSPEC, flags, NULL, addrs, &buflen); + } while (rc == ERROR_BUFFER_OVERFLOW); + + if (rc != NO_ERROR) { + SDL_free(addrs); + return; + } + + int new_num_interfaces = 0; + for (IP_ADAPTER_ADDRESSES *i = addrs; i != NULL; i = i->Next) { + for (IP_ADAPTER_UNICAST_ADDRESS *j = i->FirstUnicastAddress; j != NULL; j = j->Next) { + new_num_interfaces++; + } + } + + NetworkInterfaces *new_interfaces = (NetworkInterfaces *) SDL_calloc(new_num_interfaces + 1, sizeof (*new_interfaces)); + if (!new_interfaces) { + SDL_free(addrs); + return; + } + + int count = 0; + for (IP_ADAPTER_ADDRESSES *i = addrs; i != NULL; i = i->Next) { + for (IP_ADAPTER_UNICAST_ADDRESS *j = i->FirstUnicastAddress; j != NULL; j = j->Next) { + NET_Address *addr = CreateSDLNetAddrFromSockAddr(j->Address.lpSockaddr, j->Address.iSockaddrLength); + if (addr) { + new_interfaces[count].address = addr; + if (j->Address.lpSockaddr->sa_family == AF_INET6) { + new_interfaces[count].index = i->Ipv6IfIndex; + } else if (j->Address.lpSockaddr->sa_family == AF_INET) { // if this is IPv4, calculate the broadcast address. + SOCKADDR_IN bcast; + SDL_assert(j->Address.iSockaddrLength == sizeof (bcast)); + SDL_memcpy(&bcast, (const SOCKADDR_IN *) j->Address.lpSockaddr, sizeof (SOCKADDR_IN)); + bcast.sin_addr.S_un.S_addr |= htonl(~((1 << (32 - j->OnLinkPrefixLength)) - 1)); + new_interfaces[count].broadcast = CreateSDLNetAddrFromSockAddr((const struct sockaddr *) &bcast, sizeof (bcast)); + new_interfaces[count].broadcast->type = NET_ADDRTYPE_BROADCAST; + new_interfaces[count].index = i->IfIndex; + } + count++; + } + } + } + + new_num_interfaces = count; // in case we dropped one somewhere. + + SDL_LockRWLockForWriting(interface_rwlock); + NetworkInterfaces *old_interfaces = interfaces; + const int old_num_interfaces = num_interfaces; + interfaces = new_interfaces; + num_interfaces = new_num_interfaces; + SDL_UnlockRWLock(interface_rwlock); + FreeNetworkInterfaces(old_interfaces, old_num_interfaces); + + SDL_free(addrs); +} + +#elif defined(SDL_PLATFORM_LINUX) || defined(SDL_PLATFORM_ANDROID) || defined(SDL_PLATFORM_APPLE) || defined(SDL_PLATFORM_FREEBSD) || defined(SDL_PLATFORM_OPENBSD) || defined(SDL_PLATFORM_NETBSD) + +// AF_NETLINK covers Linux (and by extension Android). PF_ROUTE covers the BSDs (and by extension Apple platforms). +// This doesn't cover all Unix platforms that ever existed, but this hits just about everything that is still being maintained seriously. + +static SDL_Thread *interface_change_notifications_thread = NULL; +static SDL_AtomicInt interface_change_notifications_flag; // !!! FIXME + +static int SDLCALL LinuxInterfaceChangeNotificationThread(void *data) +{ + #ifdef USE_NETLINK + const int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + #else + const int fd = socket(PF_ROUTE, SOCK_RAW, 0); + #endif + if (fd == -1) { + return 0; // oh well. + } + + // !!! FIXME: don't make this non-blocking, find a more efficient way to terminate the thread. + fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK); + + #ifdef USE_NETLINK + struct sockaddr_nl addr; + SDL_zero(addr); + addr.nl_family = AF_NETLINK; + addr.nl_pid = getpid(); + addr.nl_groups = RTMGRP_LINK|RTMGRP_IPV4_IFADDR|RTMGRP_IPV6_IFADDR; + if (bind(fd, (struct sockaddr *) &addr, sizeof (addr)) == -1) { + close(fd); + return 0; // oh well. + } + #elif defined(RO_MSGFILTER) + const unsigned char filter[] = { RTM_IFANNOUNCE, RTM_IFINFO, RTM_NEWADDR, RTM_DELADDR, RTM_CHGADDR }; + setsockopt(fd, PF_ROUTE, RO_MSGFILTER, &filter, sizeof (filter)); + #endif + + while (SDL_GetAtomicInt(&interface_change_notifications_flag) == 0) { + Uint8 buf[8192]; + struct iovec iov = { buf, sizeof (buf) }; + struct msghdr msg; + SDL_zero(msg); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + #ifdef USE_NETLINK + struct sockaddr_nl sa; + SDL_zero(sa); + msg.msg_name = &sa; + msg.msg_namelen = sizeof (sa); + #endif + + const ssize_t br = recvmsg(fd, &msg, 0); + if (br < 0) { + if (WouldBlock(LastSocketError())) { + SDL_Delay(300); + continue; + } + // quit if there's a failure. Presumably this is because another thread close()'d the socket, to unblock the thread and request termination. + break; + } + + // we don't currently bother parsing for specifics, we just use this as a signal to reenumerate interfaces. + //for (struct nlmsghdr *i = buf; NLMSG_OK(i, br); i = NLMSG_NEXT(i, br)) {} + SDL_Log("Network interfaces changed!"); + SDL_SetAtomicInt(&interfaces_have_changed, 1); + } + + close(fd); + + return 0; +} + +static bool InitInterfaceChangeNotifications(void) // LINUX/BSD VERSION +{ + interface_change_notifications_thread = SDL_CreateThread(LinuxInterfaceChangeNotificationThread, "SDLNetIfaceEnum", NULL); + return (interface_change_notifications_thread != NULL); +} + +static void QuitInterfaceChangeNotifications(void) // LINUX/BSD VERSION +{ + if (interface_change_notifications_thread) { + SDL_SetAtomicInt(&interface_change_notifications_flag, 1); + SDL_WaitThread(interface_change_notifications_thread, NULL); + SDL_SetAtomicInt(&interface_change_notifications_flag, 0); + } + interface_change_notifications_thread = NULL; +} + +static void RefreshInterfaces(void) // LINUX/BSD VERSION +{ +#ifdef USE_NETLINK + // Android has getifaddrs() in Android 24, but we still target 21, so do it the hard way. Since this works on normal Linux, we'll just use it there, too. + const int sock = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + if (sock < 0) { + return; // oh well. + } + + fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, 0) | O_NONBLOCK); + + // Ask for the address information. + typedef struct reqstruct + { + struct nlmsghdr header; + struct ifaddrmsg msg; + } reqstruct; + + reqstruct req; + SDL_zero(req); + req.header.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; + req.header.nlmsg_type = RTM_GETADDR; + req.header.nlmsg_len = NLMSG_LENGTH(sizeof (req.msg)); + req.msg.ifa_family = AF_UNSPEC; + + if (send(sock, &req, req.header.nlmsg_len, 0) != req.header.nlmsg_len) { + close(sock); + return; // oh well. + } + + int new_num_interfaces = 0; + NetworkInterfaces *new_interfaces = NULL; + + Uint8 buffer[64 * 1024]; + ssize_t br; + while ((br = recv(sock, buffer, sizeof (buffer), 0)) > 0) { + for (const struct nlmsghdr *header = (const struct nlmsghdr *) buffer; NLMSG_OK(header, (size_t) br); header = NLMSG_NEXT(header, br)) { + if (header->nlmsg_type == NLMSG_DONE) { + goto succeeded; // we got it all. + } else if (header->nlmsg_type == NLMSG_ERROR) { + goto failed; // uhoh. + } else if (header->nlmsg_type == RTM_NEWADDR) { + // strictly speaking, one interface can have multiple IP addresses, so you might have multiple addresses with the same interface index. + void *ptr = SDL_realloc(new_interfaces, sizeof (*new_interfaces) * (new_num_interfaces + 1)); + if (!ptr) { + goto failed; + } + new_interfaces = (NetworkInterfaces *) ptr; + NetworkInterfaces *iface = &new_interfaces[new_num_interfaces]; + SDL_zerop(iface); + + const struct ifaddrmsg *msg = (const struct ifaddrmsg *) (NLMSG_DATA(header)); + size_t payload_len = IFA_PAYLOAD(header); + for (const struct rtattr *attr = IFA_RTA(msg); RTA_OK(attr, payload_len); attr = RTA_NEXT(attr, payload_len)) { + const bool isbroadcast = (attr->rta_type == IFA_BROADCAST); + if (isbroadcast || (attr->rta_type == IFA_ADDRESS)) { + // this gives us the raw bytes of an address, but not the actual sockaddr_* layout, so we have to go with known protocols. :/ + AddressStorage addrstorage; + SockLen addrlen = 0; + SDL_zero(addrstorage); + addrstorage.ss_family = msg->ifa_family; + if (addrstorage.ss_family == AF_INET) { + struct sockaddr_in *sa = (struct sockaddr_in *) &addrstorage; + SDL_memcpy(&sa->sin_addr, RTA_DATA(attr), RTA_PAYLOAD(attr)); + addrlen = sizeof (*sa); + } else if (addrstorage.ss_family == AF_INET6) { + struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) &addrstorage; + SDL_memcpy(&sa6->sin6_addr, RTA_DATA(attr), RTA_PAYLOAD(attr)); + addrlen = sizeof (*sa6); + } else { + continue; // unknown protocol family. + } + + NET_Address **paddr = isbroadcast ? &iface->broadcast : &iface->address; + SDL_assert(*paddr == NULL); // shouldn't be two of these attributes on a single RTM_NEWADDR. + *paddr = CreateSDLNetAddrFromSockAddr((const struct sockaddr *) &addrstorage, addrlen); + } + } + + if (iface->address) { + iface->index = msg->ifa_index; + if (iface->broadcast) { + iface->broadcast->type = NET_ADDRTYPE_BROADCAST; + } + + char interface_name[IF_NAMESIZE]; + if (if_indextoname((unsigned int) iface->index, interface_name) == NULL) { // miraculously, this function is in Android 21; nothing else is, though! We could have done this with netlink too, but this is the easy button. + interface_name[0] = '\0'; + } + + iface->name = SDL_strdup(interface_name); + if (!iface->name) { + goto failed; + } + + new_num_interfaces++; + } else { + NET_UnrefAddress(iface->broadcast); // just in case. + } + } + } + } + +succeeded: + close(sock); + SDL_LockRWLockForWriting(interface_rwlock); + NetworkInterfaces *old_interfaces = interfaces; + const int old_num_interfaces = num_interfaces; + interfaces = new_interfaces; + num_interfaces = new_num_interfaces; + SDL_UnlockRWLock(interface_rwlock); + FreeNetworkInterfaces(old_interfaces, old_num_interfaces); + return; + +failed: + close(sock); + FreeNetworkInterfaces(new_interfaces, new_num_interfaces); + // if this was a failure case, oh well, old (maybe incorrect) ones will have to stand. +#else + struct ifaddrs *ifaddr = NULL; + if (getifaddrs(&ifaddr) == -1) { + return; // oh well. + } + + NetworkInterfaces *new_interfaces = NULL; + int new_num_interfaces = 0; + for (struct ifaddrs *i = ifaddr; i != NULL; i = i->ifa_next) { + if (i->ifa_name != NULL) { + new_num_interfaces++; + } + } + + if (!new_num_interfaces) { + goto succeeded; + } + + new_interfaces = SDL_calloc(new_num_interfaces, sizeof (*new_interfaces)); + if (!new_interfaces) { + goto failed; + } + + NetworkInterfaces *iface = &new_interfaces[0]; + for (struct ifaddrs *i = ifaddr; i != NULL; i = i->ifa_next, iface++) { + if (i->ifa_name == NULL) { + continue; // i guess. + } + + // !!! FIXME: getifaddrs doesn't return the sockaddr length, so we have to go with known protocols. :/ + if (i->ifa_addr->sa_family == AF_INET) { + iface->address = CreateSDLNetAddrFromSockAddr(i->ifa_addr, sizeof (struct sockaddr_in)); + if (i->ifa_flags & IFF_BROADCAST) { + iface->broadcast = CreateSDLNetAddrFromSockAddr(i->ifa_broadaddr, sizeof (struct sockaddr_in)); + } + } else if (i->ifa_addr->sa_family == AF_INET6) { + iface->address = CreateSDLNetAddrFromSockAddr(i->ifa_addr, sizeof (struct sockaddr_in6)); + } else { + new_num_interfaces--; + iface--; + continue; + } + + if (iface->address) { + if (iface->broadcast) { + iface->broadcast->type = NET_ADDRTYPE_BROADCAST; + } + + iface->index = (int) if_nametoindex(i->ifa_name); + iface->name = SDL_strdup(i->ifa_name); + if (!iface->name) { + goto failed; + } + + char *ptr = SDL_strchr(iface->address->human_readable, '%'); // chop off interface name. + if (ptr) { + *ptr = '\0'; + } + } else { + NET_UnrefAddress(iface->broadcast); // just in case. + goto failed; + } + } + +succeeded: + if (ifaddr) { + freeifaddrs(ifaddr); + } + + SDL_LockRWLockForWriting(interface_rwlock); + NetworkInterfaces *old_interfaces = interfaces; + const int old_num_interfaces = num_interfaces; + interfaces = new_interfaces; + num_interfaces = new_num_interfaces; + SDL_UnlockRWLock(interface_rwlock); + FreeNetworkInterfaces(old_interfaces, old_num_interfaces); + return; + +failed: + if (ifaddr) { + freeifaddrs(ifaddr); + } + FreeNetworkInterfaces(new_interfaces, new_num_interfaces); + // if this was a failure case, oh well, old (maybe incorrect) ones will have to stand. +#endif +} + +#else +#error implement me for your platform. +#endif + + +static bool InterfacesReady(void) +{ + if (SDL_ShouldInit(&interface_init)) { + if (!interface_rwlock) { + interface_rwlock = SDL_CreateRWLock(); + } + if (!interface_rwlock || !InitInterfaceChangeNotifications()) { + SDL_SetInitialized(&interface_init, false); + return false; + } + SDL_SetAtomicInt(&interfaces_have_changed, 1); // force refresh at start. + SDL_SetInitialized(&interface_init, true); + } + + // if there were changes, mark it as unchanged and then we'll enumerate. So if it changes again while enumerating, we'll pick that up next time. + if (SDL_CompareAndSwapAtomicInt(&interfaces_have_changed, 1, 0)) { + RefreshInterfaces(); + #if 0 + SDL_Log("NETWORK INTERFACE REFRESH"); + for (int i = 0; i < num_interfaces; i++) { + const NetworkInterfaces *iface = &interfaces[i]; + SDL_Log("Interface %d ('%s')", iface->index, iface->name); + SDL_Log(" - address %s", iface->address->human_readable); + if (iface->broadcast) { + SDL_Log(" - broadcast %s", iface->broadcast->human_readable); + } + } + #endif + } + + return true; +} + + // this blocks! static NET_Status ResolveAddress(NET_Address *addr) { @@ -354,6 +849,8 @@ static NET_Status ResolveAddress(NET_Address *addr) addr->human_readable = SDL_strdup(buf); addr->ainfo = ainfo; + addr->type = NET_ADDRTYPE_UNICAST; + return NET_SUCCESS; // success (zero means "still in progress"). } @@ -479,6 +976,8 @@ static NET_Address *CreateSDLNetAddrFromSockAddr(const struct sockaddr *saddr, S return NULL; } + addr->type = NET_ADDRTYPE_UNICAST; // until told otherwise. + addr->human_readable = SDL_strdup(hostbuf); if (!addr->human_readable) { freeaddrinfo(addr->ainfo); @@ -588,6 +1087,26 @@ void NET_Quit(void) resolver_queue = NULL; + if (SDL_ShouldQuit(&interface_init)) { + QuitInterfaceChangeNotifications(); + SDL_SetAtomicInt(&interfaces_have_changed, 0); + if (interfaces) { + FreeNetworkInterfaces(interfaces, num_interfaces); + } + if (interface_rwlock) { + SDL_DestroyRWLock(interface_rwlock); + } + interfaces = NULL; + num_interfaces = 0; + interface_rwlock = NULL; + SDL_SetInitialized(&interface_init, false); + } + + // asserts to catch that these shouldn't have ever been set if we never initialized interface_init... + SDL_assert(!interfaces); + SDL_assert(!interface_rwlock); + SDL_assert(num_interfaces == 0); + #ifdef SDL_PLATFORM_WINDOWS WSACleanup(); #endif @@ -770,41 +1289,9 @@ void NET_SimulateAddressResolutionLoss(int percent_loss) SDL_SetAtomicInt(&resolver_percent_loss, SDL_clamp(percent_loss, 0, 100)); } -#ifdef SDL_PLATFORM_ANDROID -typedef struct EnumerateNetAddrTableData -{ - NET_Address **retval; - int count; - int real_count; -} EnumerateNetAddrTableData; - -static void SDLCALL EnumerateNetAddrTable(void *userdata, SDL_PropertiesID props, const char *name) -{ - NET_Address *netaddr = SDL_GetPointerProperty(props, name, NULL); - if (netaddr) { - NET_RefAddress(netaddr); - EnumerateNetAddrTableData *data = (EnumerateNetAddrTableData *) userdata; - SDL_assert(data->real_count < data->count); - data->retval[data->real_count++] = netaddr; - } -} - -static void SDLCALL CleanupNetAddrTable(void *userdata, void *value) -{ - NET_UnrefAddress((NET_Address *) value); -} - -static int SDLCALL CompareNetAddrQsort(const void *a, const void *b) -{ - return NET_CompareAddresses(*(NET_Address **) a, *(NET_Address **) b); -} -#endif NET_Address **NET_GetLocalAddresses(int *num_addresses) { - int count = 0; - NET_Address **retval = NULL; - int dummy_addresses; if (!num_addresses) { num_addresses = &dummy_addresses; @@ -812,209 +1299,20 @@ NET_Address **NET_GetLocalAddresses(int *num_addresses) *num_addresses = 0; -#ifdef SDL_PLATFORM_WINDOWS - // !!! FIXME: maybe LoadLibrary(iphlpapi) on the first call, since most - // !!! FIXME: things won't ever use this. + NET_Address **retval = NULL; - // MSDN docs say start with a 15K buffer, which usually works on the first - // try, instead of trying to query for size, allocate, and then retry, - // since this tends to be more expensive. - ULONG buflen = 15 * 1024; - IP_ADAPTER_ADDRESSES *addrs = NULL; - ULONG rc; - - do { - SDL_free(addrs); - addrs = (IP_ADAPTER_ADDRESSES *) SDL_malloc(buflen); - if (!addrs) { - return NULL; - } - - const ULONG flags = GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME; - rc = GetAdaptersAddresses(AF_UNSPEC, flags, NULL, addrs, &buflen); - } while (rc == ERROR_BUFFER_OVERFLOW); - - if (rc != NO_ERROR) { - SetSocketError("GetAdaptersAddresses failed", rc); - SDL_free(addrs); - return NULL; - } - - for (IP_ADAPTER_ADDRESSES *i = addrs; i != NULL; i = i->Next) { - for (IP_ADAPTER_UNICAST_ADDRESS *j = i->FirstUnicastAddress; j != NULL; j = j->Next) { - count++; - } - } - - retval = (NET_Address **) SDL_calloc(((size_t)count) + 1, sizeof (NET_Address *)); - if (!retval) { - SDL_free(addrs); - return NULL; - } - - count = 0; - for (IP_ADAPTER_ADDRESSES *i = addrs; i != NULL; i = i->Next) { - for (IP_ADAPTER_UNICAST_ADDRESS *j = i->FirstUnicastAddress; j != NULL; j = j->Next) { - NET_Address *addr = CreateSDLNetAddrFromSockAddr(j->Address.lpSockaddr, j->Address.iSockaddrLength); - if (addr) { - retval[count++] = addr; + if (InterfacesReady()) { + SDL_LockRWLockForReading(interface_rwlock); + const int total = num_interfaces; + retval = (NET_Address **) SDL_malloc((total + 1) * sizeof (NET_Address *)); + if (retval) { + for (int i = 0; i < total; i++) { + retval[i] = NET_RefAddress(interfaces[i].address); } + retval[total] = NULL; + *num_addresses = total; } - } - - SDL_free(addrs); - -#elif defined(SDL_PLATFORM_ANDROID) - // Android has getifaddrs() in Android 24, but we still target 21, so do it the hard way. - const int sock = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); - if (sock < 0) { - SetLastSocketError("Failed to create AF_NETLINK socket"); - return NULL; - } - - fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, 0) | O_NONBLOCK); - - // Ask for the address information. - typedef struct reqstruct - { - struct nlmsghdr header; - struct ifaddrmsg msg; - } reqstruct; - - reqstruct req; - SDL_zero(req); - req.header.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; - req.header.nlmsg_type = RTM_GETADDR; - req.header.nlmsg_len = NLMSG_LENGTH(sizeof (req.msg)); - req.msg.ifa_family = AF_UNSPEC; - - if (send(sock, &req, req.header.nlmsg_len, 0) != req.header.nlmsg_len) { - SetLastSocketError("Failed to send request to AF_NETLINK socket"); - close(sock); - return NULL; - } - - // this can produce duplicate entries for various reasons; use an SDL_PropertiesID to keep a unique table. - const SDL_PropertiesID addr_table = SDL_CreateProperties(); - if (!addr_table) { - close(sock); - return NULL; - } - - Uint8 buffer[64 * 1024]; - ssize_t br; - while ((br = recv(sock, buffer, sizeof (buffer), 0)) > 0) { - for (const struct nlmsghdr *header = (const struct nlmsghdr *) buffer; NLMSG_OK(header, (size_t) br); header = NLMSG_NEXT(header, br)) { - if (header->nlmsg_type == NLMSG_DONE) { - break; // we got it all. - } else if (header->nlmsg_type == NLMSG_ERROR) { - // uhoh. - NET_FreeLocalAddresses(retval); - retval = NULL; - break; - } else if (header->nlmsg_type == RTM_NEWADDR) { - const struct ifaddrmsg *msg = (const struct ifaddrmsg *) (NLMSG_DATA(header)); - size_t payload_len = IFA_PAYLOAD(header); - for (const struct rtattr *attr = IFA_RTA(msg); RTA_OK(attr, payload_len); attr = RTA_NEXT(attr, payload_len)) { - if ((attr->rta_type != IFA_ADDRESS) && (attr->rta_type != IFA_LOCAL)) { - continue; - } - - // this gives us the raw bytes of an address, but not the actual sockaddr_* layout, so we have to go with known protocols. :/ - AddressStorage addrstorage; - SockLen addrlen = 0; - SDL_zero(addrstorage); - addrstorage.ss_family = msg->ifa_family; - if (addrstorage.ss_family == AF_INET) { - struct sockaddr_in *sa = (struct sockaddr_in *) &addrstorage; - SDL_memcpy(&sa->sin_addr, RTA_DATA(attr), RTA_PAYLOAD(attr)); - addrlen = sizeof (*sa); - } else if (addrstorage.ss_family == AF_INET6) { - struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) &addrstorage; - SDL_memcpy(&sa6->sin6_addr, RTA_DATA(attr), RTA_PAYLOAD(attr)); - addrlen = sizeof (*sa6); - } else { - continue; // unknown protocol family. - } - - NET_Address *netaddr = CreateSDLNetAddrFromSockAddr((const struct sockaddr *) &addrstorage, addrlen); - if (!netaddr) { - continue; - } - - const char *key = NET_GetAddressString(netaddr); - if (!key) { - NET_UnrefAddress(netaddr); - } else { - SDL_SetPointerPropertyWithCleanup(addr_table, key, netaddr, CleanupNetAddrTable, NULL); - count++; - } - } - } - } - } - - close(sock); - - retval = (NET_Address **) SDL_calloc(count + 1, sizeof (NET_Address *)); - if (retval) { - EnumerateNetAddrTableData data = { retval, count, 0 }; - SDL_EnumerateProperties(addr_table, EnumerateNetAddrTable, &data); - count = data.real_count; - SDL_qsort(retval, count, sizeof (*retval), CompareNetAddrQsort); - } - - SDL_DestroyProperties(addr_table); // will unref all addresses still in the table, but we took a reference too. - -#else - struct ifaddrs *ifaddr; - if (getifaddrs(&ifaddr) == -1) { - SDL_SetError("getifaddrs failed: %s", strerror(errno)); - return NULL; // oh well. - } - - for (struct ifaddrs *i = ifaddr; i != NULL; i = i->ifa_next) { - if (i->ifa_name != NULL) { - count++; - } - } - - retval = (NET_Address **) SDL_calloc(count + 1, sizeof (NET_Address *)); - if (!retval) { - if (ifaddr) { - freeifaddrs(ifaddr); - } - return NULL; - } - - count = 0; - for (struct ifaddrs *i = ifaddr; i != NULL; i = i->ifa_next) { - if (i->ifa_name != NULL) { - NET_Address *addr = NULL; - // !!! FIXME: getifaddrs doesn't return the sockaddr length, so we have to go with known protocols. :/ - if (i->ifa_addr->sa_family == AF_INET) { - addr = CreateSDLNetAddrFromSockAddr(i->ifa_addr, sizeof (struct sockaddr_in)); - } else if (i->ifa_addr->sa_family == AF_INET6) { - addr = CreateSDLNetAddrFromSockAddr(i->ifa_addr, sizeof (struct sockaddr_in6)); - } - - if (addr) { - retval[count++] = addr; - } - } - } - - if (ifaddr) { - freeifaddrs(ifaddr); - } -#endif - - *num_addresses = count; - - // try to shrink allocation. - void *ptr = SDL_realloc(retval, (((size_t) count) + 1) * sizeof (NET_Address *)); - if (ptr) { - retval = (NET_Address **) ptr; + SDL_UnlockRWLock(interface_rwlock); } return retval; @@ -1074,25 +1372,6 @@ struct NET_StreamSocket Uint64 simulated_failure_until; }; -static int MakeSocketNonblocking(Socket handle) -{ - #ifdef SDL_PLATFORM_WINDOWS - DWORD one = 1; - return ioctlsocket(handle, FIONBIO, &one); - #else - return fcntl(handle, F_SETFL, fcntl(handle, F_GETFL, 0) | O_NONBLOCK); - #endif -} - -static bool WouldBlock(const int err) -{ - #ifdef SDL_PLATFORM_WINDOWS - return (err == WSAEWOULDBLOCK) ? true : false; - #else - return ((err == EWOULDBLOCK) || (err == EAGAIN) || (err == EINPROGRESS)) ? true : false; - #endif -} - NET_StreamSocket *NET_CreateClient(NET_Address *addr, Uint16 port) { if (addr == NULL) { diff --git a/Source/3rdParty/SDL_net/src/genexports.py b/Source/3rdParty/SDL_net/src/genexports.py index 0f48f6d3..d20ab781 100755 --- a/Source/3rdParty/SDL_net/src/genexports.py +++ b/Source/3rdParty/SDL_net/src/genexports.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # Simple DirectMedia Layer -# Copyright (C) 1997-2025 Sam Lantinga +# Copyright (C) 1997-2026 Sam Lantinga # # This software is provided 'as-is', without any express or implied # warranty. In no event will the authors be held liable for any damages diff --git a/Source/3rdParty/SDL_net/src/version.rc b/Source/3rdParty/SDL_net/src/version.rc index d051a499..40c66d31 100644 --- a/Source/3rdParty/SDL_net/src/version.rc +++ b/Source/3rdParty/SDL_net/src/version.rc @@ -25,7 +25,7 @@ BEGIN VALUE "FileDescription", "SDL_net\0" VALUE "FileVersion", "3, 0, 0, 0\0" VALUE "InternalName", "SDL_net\0" - VALUE "LegalCopyright", "Copyright (C) 2025 Sam Lantinga\0" + VALUE "LegalCopyright", "Copyright (C) 2026 Sam Lantinga\0" VALUE "OriginalFilename", "SDL3_net.dll\0" VALUE "ProductName", "Simple DirectMedia Layer\0" VALUE "ProductVersion", "3, 0, 0, 0\0"